Skip to content

Instantly share code, notes, and snippets.

@TimvanScherpenzeel
Last active April 21, 2022 20:30
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save TimvanScherpenzeel/c870b35358fb96fa643d9ed1ea606efd to your computer and use it in GitHub Desktop.
Save TimvanScherpenzeel/c870b35358fb96fa643d9ed1ea606efd to your computer and use it in GitHub Desktop.
Implementing audio and video autoplay unlocking

Implementing audio and video autoplay unlocking

Due to recent changes in the autoplay policy of Chrome 66 developers of interactive experiences on the web are facing new challenges regarding audio and video autoplay. The new policy has unfortunately broken many of the older experiences that rely on autoplaying audio and video.

So how to move forward?

Previous solutions

Previously developers used to face this issue on iOS mobile devices where the audio context was locked. Most developers fixed this by using the initial touch event of a user to unlock the audio.

See:

Due to this only being an issue on the iOS mobile browser many developers checked for the existence of ontouchstart on the window, often in combination with user agent detection for iOS devices. Looking back we can say: sure that was naive of us. Thinking that this policy wouldn't appear on other platforms has gotten us into trouble now.

Restrictions

All new restrictions regarding autoplay can be found been in the following resources:

From these resources the major points are:

  • Assume that the video won't autoplay, develop a strategy based on that.
  • Muted autoplay is always allowed.
  • Audio tracks that render silence are still audio tracks, and their existence affects whether a video will auto-play at all. In these cases, a video with silent audio tracks won’t play. The audio track should be removed or, alternatively, the muted attribute can be set on the media element.
  • Auto-play restrictions are granted on a per-element basis. Change the source of the media element instead of creating multiple media elements if you want to play multiple videos back to back.
  • Autoplay with sound is allowed if:
    • User has interacted with the domain (click, tap, keydown): after the context has been unlocked on one page of the domain the context does not have to be unlocked again on other pages.
    • On desktop, the user's Media Engagement Index threshold has been crossed, meaning the user has previously play video with sound.
    • On mobile, the user has added the site to his or her home screen.
  • Top frames can delegate autoplay permission to their iframes to allow autoplay with sound.

Possible solution

  • If a video is not intended to have an audio track make sure to pass muted to the video context. This will prevent any errors regarding autoplaying as muted autoplay is always allowed.

A solution to the problem must be a cross-device and cross-browser solution that allows for future policy changes of other browsers and devices. One possible solution could be defined as follows:

// Does not appear to work on Android Chrome (tested on Google Pixel 2 and Galaxy S8): 
// context.state returns 'running' even though it also throws:
// 'Failed to execute 'play' on 'HTMLMediaElement': API can only be initiated by a user gesture.'.

// Pass the DOM element used to unlock to the method and remove it on unlock.
function autoplayUnlock(element) {
    var context = new (window.AudioContext || window.webkitAudioContext)();

    return new Promise(function (resolve, reject) {
        if (context.state === 'suspended') {
            var unlock = function unlock() {
                context.resume()
                    .then(function () {
                        window.removeEventListener('keydown', unlock);
                        element.removeEventListener('click', unlock);
                        element.removeEventListener('touchstart', unlock);
                        element.removeEventListener('touchend', unlock);

                        resolve();
                    }).catch(function (error) {
                        reject(error);
                    });
            };

            window.addEventListener('keydown', unlock, false);
            element.addEventListener('click', unlock, false);
            element.addEventListener('touchstart', unlock, false);
            element.addEventListener('touchend', unlock, false);
        } else {
            resolve();
        }
    });
}

var autoplayUnlockElement = document.getElementById('autoplay-unlock-overlay');
var audioElement = document.getElementById('audio-element');
var videoElement = document.getElementById('video-element');

autoplayUnlock(autoplayUnlockElement)
    .then(function() {
        document.body.removeChild(autoplayUnlockElement);
        audioElement.play();
        videoElement.play();        
    })
    .catch(function(error) {
        console.error(error);
    });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment