You may have heard that there are changes to autoplay coming on Apple Safari and Google Chrome and are interested to know more. In this post, I go over some of the history of the video element and how autoplay shaped video support in browsers, issues that cropped up and how we fixed those issues, recommendations for developers for the best autoplay behavior, and what to expect in the future for autoplay on the web.
If you don’t want to read the entire post, here is the gist of it: you can continue using the autoplay
attribute and things should work, but to get the best support, you should consider moving away from the autoplay
attribute and use the play()
method to take advantage of the play promise that it returns.
Background
In February 2007, Opera published a manifesto calling for video on the web. Along with it they released a technical specification for what we now know and love as the <video>
element. The WHATWG quickly adopted the spec with other browsers starting to implement support in the following years.
Apple was one of the first to release support for the video element with the release of Safari 3.1 on iOS and desktop in March of 2008. Mozilla Firefox followed in June of 2009. Google Chrome in October of 2009. Internet Explorer was a bit of a straggler; they released IE9 and support for the video element in March of 2011.
By March of 2011, Opera, IE, Firefox, Chrome, and Safari for desktop had support for autoplay, with the exception of Apple’s iOS, which restricted autoplay functionality. This was because of the battery and user experience issues around it, particularly because videos on the iPhone always launched into the native player rather than playing inline.
Between 2008 and 2016, battery life greatly improved and video on the web became prolific. With the tremendous growth in video, in iOS 10, Apple introduced the ability to autoplay media on iPhones and iPads. However, there was a catch: the video must be muted or not have an audio track. In addition, on iPhones, the video must be allowed to play inline with the playsinline
attribute.
After Apple introduced muted autoplay, Google followed suit and introduced it in Chrome for Andriod 53. In the months since, we worked, along with the ad SDK providers, to resolve issues around the new autoplay behavior.
In June 2017, Apple announced that starting with Safari 11 they’d include the same autoplay behavior on desktop, i.e., unmuted autoplay would be rejected. With this announcement, we thought it best to take a step back and think about the issues around autoplay, particularly with ads, to see if the issues all stemmed from the same place. It was time to address autoplay as a fundamental change in how the player functioned.
In September 2017, Google announced that they are implementing similar changes in Google Chrome: Unified Autoplay. Unlike Apple, Google is taking it a step further, besides allowing muted autoplay, they are going to allow autoplay if the site has been interacted with by the user or if a Media Engagement Index (MEI) was crossed. The MEI is assigned to each website and tracks how much the user interacts with the page and if the user views videos or listens to audio. This way, sites with video as their core feature, like YouTube and Netflix, would have a high Media Engagement Index and would not be required to wait for a user gesture to play unmuted. But a site that the user has never visited before will not be able to autoplay video with sound. This will be released in early 2018, as a part of Google Chrome 64.
The issues and what we did
After a thorough test, it turned out that there weren’t a lot of fundamental changes that needed to be made to the player. The main issue arose from how Video.js made sure that autoplay was added to the video element it created, and an inconsistency with how browsers were treating its HTML attributes and properties.
The first issue was that during initialization, Video.js set autoplay
as the first attribute on the video element before other attributes like muted
and playsinline
were included. While Chrome re-ran their autoplay checks when those attributes were set, Safari and iOS did not. So, we made sure that the autoplay
property was only set after muted
and playsinline
were provided.
The second issue was browser inconsistency in regards to HTML attributes and properties. For example, you could make an autoplayed, muted player using HTML attributes like so:
<video autoplay muted playsinline>
This did work but if we tried to do it programmatically like the following, it didn’t:
video.muted = true;
video.autoplay = true;
It didn’t work because the video wasn’t actually muted on some iOS devices. Instead, we had to make sure to set the attribute and the property; different cross sections of devices wouldn’t work with just a call to setAttribute
. This lead us to the following:
video.muted = true;
// muted is a boolean attribute, so, any value turns it on, using the name of the attribute is an often used convention
video.setAttribute(‘muted’, ‘muted’);
video.autoplay = true;
video.setAttribute(‘autoplay’, ‘autoplay’);
The muted
issue cascaded further. When using Video.js to ask if the video was muted, we sometimes got the wrong answer. Video.js used the property value to check if something was set, but in some cases, if the feature was set using the attribute, the property wasn’t set to reflect that value. So with an embed code like the following:
<video muted>
We would get false
when we asked the player for the muted value:
player.muted();
// -> false
So, internally, we made sure that each setting set both the attribute and the property and each getter looked at both.
The new state of Autoplay
Now that these changes are available, autoplay works great, right? No so fast.... Because of the way that autoplay rejection works when the right conditions aren’t met (i.e.muted
on Safari 11, iOS, and Chrome for Android as well as the addition of playsinline
on iPhones) it’s hard to detect whether Autoplay was rejected or that the user just hit play and immediately followed it with a pause. This is something that ad SDKs need to take into account and something that we’ve noticed as Known Issues below. On mobile, most ad SDKs have already updated for proper support but there is no official support for Safari 11 from the ad SDKs at the time of this writing.
The Play Promise
In recent years, browsers have updated to return a Promise, a representation of an asynchronous process and its return value, from the play function. The promise allows you to know whether a play succeeded. If the promise is resolved, the play succeeded; if the promise is rejected, the play failed. Previously, two of the main reasons why the promise was rejected are that the video couldn’t be played and that the user (or another script) paused the video shortly after playing but before the video was able to play successfully.
Now, we have a new rejection reason for the play promise. If you are on a platform that will reject autoplay if the video is not muted or silent (or allowed to play inline on iPhone), the promise will be rejected.
The Play Promise is also the only way (currently) to know whether autoplay was rejected, which is why browser vendors recommend switching to the play()
method over the autoplay
attribute.
Recommendation
With the changes that we’ve made, the autoplay
attribute will work and continue to do so to the best of its ability. However, switching to using the play()
method is recommended.
First, browser vendors themselves recommend using it over the autoplay
attribute and it’s generally a good idea to follow their advice.
Second, using the play()
method allows you, as a developer of the player, to know whether autoplay has succeeded when you listen to the promise:
player.play().then(function() {
// play succeeded!
})
.catch(function(error) {
// play failed
console.error(error.message);
});
Then, if autoplay is rejected, you can put up a message or have an alternative.
Third, if the play()
method is used on the player, the player can store the promise that is created and use it internally to provide even better handling around edge cases that arise from rejected autoplay.
To summarize, you can continue using the autoplay
attribute and things should work, but to get the best support, you should consider moving away from the autoplay
attribute and use the play()
method to take advantage of the play promise that it returns.
The Future of Autoplay
It seems like the future of autoplay is, in some respects, also the death of autoplay. Apple is going with a fairly strict policy on autoplay: only allow muted autoplay across the board, and require a user gesture for playing back unmuted. The upside of this is that desktop and mobile are consistent. Google is going with a slightly more relaxed policy: only muted autoplay is allowed unless the user has interacted with the website in that session, or it’s a site that the user often goes to to view video. Google’s Unified Autoplay, however, is probably what the future of autoplay should be. It puts restrictions on autoplay to help the user, and at the same time makes it so websites can mostly continue working as is. In the end, I hope that all browsers work together to maintain consistent autoplay restrictions, freeing the developer from targeting browser specific autoplay behavior. Hopefully, the future of autoplay is a unified one.
Known Issues
- Silent ads (ads that do not have an audio track) will autoplay on platforms that reject autoplay without muted. However, when the ad finishes, content will not resume because the player is not muted and no user action was given. This applies to IMA and FreeWheel.
- If the player is muted, autoplay begins, and the user unmutes the ad, the content will not resume because no user action was given and the player is no longer muted.
- In FreeWheel, if the ad contains audio but the platform rejects autoplay (for example: on Safari 11), FreeWheel will think that there was an issue with the ad when autoplay is rejected. It will wait the ad timeout interval and then switch back to the content, which will not autoplay because the player is not muted and no user action was given.
- If autoplay is rejected but playsinline was set, the Brightcove Player will hide the big play button and show the control bar. However, the poster image should still be visible in most cases. This is due to how autoplay rejection works when using the
autoplay
attribute.