51

I have a page with an input field for scanning products. When a barcode is scanned or a SKU is typed into the field, an ajax request is made and the application plays either a success or an error sound depending on the response using HTMLMediaElement.play().

sounds.error.play();

This was working fine a while ago but now I get this error:

⚠ Autoplay is only allowed when approved by the user, the site is activated by the user, or media is muted.

Followed by:

NotAllowedError: The play method is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.

Since this page only exists for the purpose of scanning SKUs, when the page loads, that input field is programmatically focused so as to make things easier on the end user. I tried removing this focus so that the user must click into the input field, but that doesn't appear to satisfy whatever the requirements are to allow playing of audio

After a bit more experimenting I found that with some additional amount of user interaction, the sound will play. For instance if I create a checkbox to "enable" the sound and the user clicks it, that appears to be enough. Or if the user clicks outside of the input element and then back into to it again that also works.

What exactly are the requirements that will satisfy most modern browsers so that they will allow playing of audio?

I realize the answer may be different for different browsers and configurations, but I was unable to find anything authoritative for current versions of either Firefox or Chrome. I'm looking for a workaround so that the application does not need to be complicated with extra clicks or other kinds of interactions, and since I am now aware of this new policy, I'd like the revisions to be as unobtrusive as possible.

UPDATE:

Here is a basic example I worked up to demonstrate the issue. I tried three different browsers just now and they all behaved a bit differently. Firefox in particular behaves as described above — does not play the sound until I focus on the input field, blur, then click to focus again:

http://so.dev.zuma-design.com/input-sounds.html

  • 3
    Look at recent [autoplay policy announcement](https://developers.google.com/web/updates/2017/09/autoplay-policy-changes). It depends on site's ME Index. Atleast, there is canplay js library to check if autoplay is possible. – bigless Aug 15 '19 at 03:09
  • 1
    @bigless - I did a google search for "canplay js" and found nothing. Can you give me a link? I've been unable to make that determination without resorting to attempting a play and catching the promise error - which requires that I try to play the sound first. – But those new buttons though.. Aug 15 '19 at 04:59
  • 1
    Sorry. it is [can-autoplay](https://github.com/video-dev/can-autoplay) – bigless Aug 15 '19 at 13:32

4 Answers4

26

There is some permission blocking in all modern browser (especially chrome) when comes down to autoplaying multimedia.

Here is the Autoplay availability's section from MDN which shows when the media will be allowed to execute automatically:

  • The audio is muted or its volume is set to 0;
  • The user has interacted with the site (by clicking, tapping, pressing keys, etc.)
  • If the site has been whitelisted; this may happen either automatically if the browser determines that the user engages with media frequently, or manually through preferences or other user interface features
  • If the autoplay feature policy is used to grant autoplay support to an <iframe> and its document.

This here is a similar solution for what you want with arrayBuffer using AJAX

here is a DEMO

Luis Febro
  • 1,733
  • 1
  • 16
  • 21
  • 8
    "*The user has interacted with the site*" - this part is what I'm struggling with. Because as I said, simply clicking into the field does not appear to be enough. I must at least click out and then into it again and thus the statement on MDN is ambiguous because there appears to be an *interaction threshold* and I'm looking for clarity on exactly what that threshold is. – But those new buttons though.. Aug 17 '19 at 03:35
  • 1
    I partly agree with @billynoah . I used touchstart as an event to video.play(), it works on all browser, except it seems the event has to be triggered twice in order to play the video in FF mobile 81.1. So if user touch the screen twice, the video plays, otherwise I have a black screen. I am trying to find a more efficient approach – Max Dec 09 '20 at 14:40
21

In safari to allowing autoplay in video tags you need add to them playsinline attribute. Like this:

<video autoplay="" muted="" playsinline=""></video>
metodribic
  • 1,561
  • 17
  • 27
Bakha Sharipov
  • 447
  • 4
  • 5
  • 4
    This did it for me! Thank you very much – Greg Sadetsky Feb 25 '21 at 19:32
  • 3
    This should be accepted answer, this works in all browser, tested in chrome, safari. – Shurvir Mori Aug 08 '22 at 03:06
  • 1
    Also works on latest firefox (104.0.2) – A__ Sep 15 '22 at 13:44
  • 2
    I'm glad this answer has been useful to some, but please note that it does address my specific question which was specifically about playing an audio file using an Audio() constructor built from an ajax response. – But those new buttons though.. Nov 26 '22 at 00:13
  • @EatenbyaGrue I understand that my answer may not solve yours, but googling in the output shows this question when searching for a solution to the "How to play video automatically in HTML" problem. So let that answer be here – Bakha Sharipov Nov 30 '22 at 15:26
  • @BakhaSharipov - I completely understand and I'm not trying to detract from the usefulness of your answer. I'm just addressing to the comment that says "This should be accepted answer". Although it's useful. this cannot possibly be the accepted answer since it basically answers a different question. – But those new buttons though.. Nov 30 '22 at 16:17
15

I struggled with that same issue as well and out of the blue I fixed that very easily:

Just change your event listener at your play button

onClick={togglePlay} to onClickCapture={togglePlay}

Example in React using hooks:

const Player = ({ audio }) => {
const [playing, setPlaying] = useState(false);

const togglePlaying = () => setPlaying((prev) => !prev);

useEffect(() => {
    if (audioRef && audioRef.current) {
        if (playing) {
            audioRef.current.play();
        } else {
            audioRef.current.pause();
        }
    }
  }, [playing]);

  return (

 <audio
            
            autoPlay=""
            src={audio}
            ref={audioRef}
            onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}
            onCanPlay={(e) => setDur(e.target.duration)}
            onEnded={songEnded}
        />
            <PlayerStart onClickCapture={togglePlaying}>
                    {playing ? <PauseIcon /> : <PlayIcon />}
            </PlayerStart>
    )
}
sFritsch09
  • 424
  • 4
  • 10
  • 2
    `onClickCapture={togglePlay}` did it for me. Honestly, this weird behaviour looks like a safari bug, because I was not autoplaying and the user interacted already with the site. – Sir hennihau Oct 04 '21 at 11:29
  • 1
    This fixed it for me as well for my Safari issue with the player that I'm using. – milesmeow Mar 24 '22 at 21:12
  • @Sirhennihau why did this work? i had the issue on chrome and safari and i don't understand why capturing the event worked as opposed to the standard bubbling. – user137717 Feb 19 '23 at 09:19
11

[edit] Firefox 70 fixes this issue. The rest of this answer provides an explanation and the proposed workaround.

As you wondered about what specifically "The user has interacted with the site" criteria means:

  • Note that there's currently no requirement for the autoplay to be triggered in response to a user gesture (though Mozilla considers changing this). The user must interact with the page to "activate" it, after which autoplay is allowed.
  • According to a comment in Firefox source code,

    ["activation" is triggered by] events which are likely to be user interaction with the document, rather than the byproduct of interaction with the browser (i.e. a keypress to scroll the view port, keyboard shortcuts, etc).

  • Specifically, in the current development version of Firefox, the following events do NOT make the page "user-gesture-activated" (code):

An idea - start with a fake/blurred textbox, and show/focus the real textbox after the first character is typed (adding the first character to the text input via JS) - the following code appears to work in Firefox:

$(document).one('keypress', function(ev) {
    // copy the first character typed over to the text input
    let char = String.fromCharCode(ev.which);
    $('.play').val(char).focus();
});

$('.play').on('keypress', function() {
    if($(this).val().length === 3) { // assuming you want to validate when a certain number of characters is typed
        sounds[$(this).data('sound')].play();
    }
})

This won't help if the user starts by clicking the text input, but a fallback non-audible feedback could be implemented for this case.

Nickolay
  • 31,095
  • 13
  • 107
  • 185
  • 2
    Thanks for illuminating some key points here. I still feel the criteria is a bit ambiguous to the point which leads to the kind of inconsistent behaviour I'm experiencing. While things like this are generally good for web browsing experience, they are a bane to people like me developing private web applications not meant for the public. We liked the old behaviour and now staff has to do extra clicks for no apparent reason. I would love a workaround that got me there... And a question - can you explain about your idea at the end there? I don't understand how the would/could/might help. – But those new buttons though.. Aug 24 '19 at 01:59
  • 1
    1) An internal web app and an internet web site are different; your question focussed on the web behavior, which can be overridden if you control the clients, which is usually the case with an internal app. 2) Since keypresses outside a textbox are supposed to "activate" the web page and allow it to play audio later, the idea is to make the first keypress of the user to happen outside the textbox and forward it to the input in a JS handler. I've added some code to my answer. – Nickolay Aug 24 '19 at 02:20
  • 1
    I also noticed I messed up my description of the criteria used in Firefox, which probably made my answer confusing. Fixed now. – Nickolay Aug 24 '19 at 02:24
  • 1
    I am still completely stumped about why this works but it does. Check the two new examples I created which attempt to play the sound on a blur event which is triggered by the user hitting "return or enter": http://so.dev.zuma-design.com/input-sounds-b.html **does not work**. In this one I auto focus the input field on page load. You type some stuff and hit enter and a sound should play. but it doesn't – But those new buttons though.. Aug 24 '19 at 21:10
  • 1
    http://so.dev.zuma-design.com/input-sounds-c.html **this one works** and uses you suggestion. The only difference is I do not focus on page load but instead once the user starts typing. Same blur event, same everything else. Thanks for the workaround! I think it's rather silly that this satisfies firefox but it does and that's enough for me since I asked for *"a workaround so that the application does not need to be complicated with extra clicks"* – But those new buttons though.. Aug 24 '19 at 21:12
  • 1
    The really weird thing in all of this is that if you engage the second example (http://so.dev.zuma-design.com/input-sounds-c.html) by ***clicking into the input field***, then typing something, the sound fails — presumably because you didn't actually type outside the input field. But it's odd because in this case you've actually done *more* to interact with the page then just typing something and focusing the field using js. IMO, the logic here is not very well thought out. – But those new buttons though.. Aug 24 '19 at 21:20
  • 2
    I also don't understand the motivation behind these rules. I [asked in the bug where this was implemented](https://bugzilla.mozilla.org/show_bug.cgi?id=1452536#c37). – Nickolay Aug 24 '19 at 22:25
  • 1
    Update: a fix for this issue was landed in bug https://bugzilla.mozilla.org/show_bug.cgi?id=1572939 , and you can test in https://nightly.mozilla.org/ – Nickolay Aug 28 '19 at 07:49