Programmatically play video with sound on Safari and Mobile Chrome

10,548

Yes, you can bind on event that are not directly ones triggered on the video element:

btn.onclick = e => vid.play();
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>

So you can replace this button with any other splash screen requesting an user click, and you'll be granted access to play the video.

But to keep this ability, you must call at least once the video's play method inside the event handler itself.

Not working:

btn.onclick = e => {
  // won't work, we're not in the event handler anymore
  setTimeout(()=> vid.play().catch(console.error), 5000);
  }
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>

Proper fix:

btn.onclick = e => {
  vid.play().then(()=>vid.pause()); // grants full access to the video
  setTimeout(()=> vid.play().catch(console.error), 5000);
  }
<button id="btn">play</button><br>
<video id="vid" src="https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4"></video>

Ps: here is the list of trusted events as defined by the specs, I'm not sure if Safari limits itself to these, nor if it includes all of these.


Important note regarding Chrome and preparing multiple MediaElements

Chrome has a long-standing bug caused by the maximum simultaneous requests per host which does affect MediaElement playing in the page, limiting their number to 6.

This means that you can not use the method above to prepare more than 6 different MediaElements in your page.

At least two workarounds exist though:

  • It seems that once a MediaElement has been marked as user-approved, it will keep this state, even though you change its src. So you could prepare a maximum of MediaElements and then change their src when needed.
  • The Web Audio API, while also concerned by this user-gesture requirement can play any number of audio sources once allowed. So, thanks to the decodeAudioData() method, one could load all their audio resources as AudioBuffers, and even audio resources from videos medias, which images stream could just be displayed in a muted <video> element in parallel of the AudioBuffer.
Share:
10,548
Geert
Author by

Geert

Updated on June 04, 2022

Comments

  • Geert
    Geert almost 2 years

    With the release of OSX High-Sierra*, one of the new features in Safari is that videos on websites will not auto play anymore and scripts can't start it either, just like on iOS. As a user, I like the feature, but as a developer it puts a problem before me: I have an in-browser HTML5 game that contains video. The videos do not get automatically played anymore unless the user changes their settings. This messes up the game flow.

    My question is, can I somehow use the players' interaction with the game as a trigger for the video to start playing automatically, even if said activity is not directly linked to the video element?

    I cannot use jQuery or other frameworks, because of a restraint that my employer has put on our development. The one exception is pixi.js which - among all other animations - we are also using to play our videos inside a pixi container.

    *The same restriction also applies on Mobile Chrome.

  • Geert
    Geert over 6 years
    Ah, so that is exactly what I'd want then :-) I can present the player with a splash screen like you suggested, as soon as the game is done loading.
  • maracuja-juice
    maracuja-juice over 6 years
    if you play and then pause: will the user hear sound of it or is this too fast?
  • Kaiido
    Kaiido over 6 years
    @Marimba it seems that you can mute the video just before calling play, and unmute it in the returned promise, after pause. Also, it seems to work on document's click too. But note that I tested only on my Safari 11.0.1 on OS X 10.12.6. There might be other behavior in other os / versions, and I actually start to wonder if my browser's behavior didn't already changed with the last update...
  • maracuja-juice
    maracuja-juice over 6 years
    Confirmed that this works for Audio aswell after I disabled autoplay in Safari. (I'm on the same macOs version as you) I can't figure out yet how to play multiple Instances at once though. (I'll see if I'll ask a new question)
  • Kaiido
    Kaiido over 6 years
    @Marimba calling play on multiple media elements from the same event should work too.
  • maracuja-juice
    maracuja-juice over 6 years
    I now get "Uncaught TypeError: Canot read property 'then' of undefined" when trying your snippet in Chrome.
  • Kaiido
    Kaiido over 6 years
    @Marimba you probably tried this on an old version of chrome. MediaElement.play does return a Promise since a few versions already. But if you need to support older browsers, you can also listen for the playing event.
  • maracuja-juice
    maracuja-juice over 6 years
    I'm on the newest version. It was an extension that caused the trouble. Fixed it now.
  • maracuja-juice
    maracuja-juice over 6 years
    It's a shame that we can't play multiple sounds at once.
  • mdomino
    mdomino about 3 years
    Man, you are a lifesaver. I thought I was going crazy here, wondering why my video wouldn't play. Thanks!