Reputation: 642
I'm trying to figure out how Safari 11's (and iOS') autoplay restrictions are implemented and I don't understand why the following doesn't start playing the audio file:
/*
Call stack, this doesn't work 💩
*/
const btn = document.createElement('BUTTON')
const textLabel = document.createTextNode('Play')
const audio = new window.Audio()
audio.src = 'https://raw.githubusercontent.com/vnglst/autoplay-tutorial/master/mp3/winamp.mp3'
// audio.controls = true
btn.appendChild(textLabel)
document.getElementById('root').appendChild(btn)
document.getElementById('root').appendChild(audio)
btn.onclick = e => {
window
.fetch(`https://api.github.com/repos/vnglst/autoplay-tutorial/contents/mp3/modem-sound.mp3`)
.then(resp => resp.json())
.then(json => {
audio.src = json.download_url
audio.play()
})
}
<div id='root'/>
Whereas Safari is fine with the following:
/*
Call stack, using a fake Promise. This works 👍
*/
const btn = document.createElement('BUTTON')
const textLabel = document.createTextNode('Play')
const audio = new window.Audio()
audio.src = 'https://raw.githubusercontent.com/vnglst/autoplay-tutorial/master/mp3/modem-sound.mp3'
// audio.controls = true
btn.appendChild(textLabel)
document.getElementById('root').appendChild(btn)
document.getElementById('root').appendChild(audio)
const mockedPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const src = 'https://raw.githubusercontent.com/vnglst/autoplay-tutorial/master/mp3/winamp.mp3'
return resolve(src)
}, 500)
})
btn.onclick = (e) => {
mockedPromise.then(src => {
audio.src = src
audio.play()
})
}
<div id='root'/>
Does anybody know how Safari determines whether something is an autoplay or not? I'm not looking for a work around (starting and pausing helps for instance) but I'm trying to figure out how this works.
(some more background information on Safari's new autoplay policy can be found here: https://webkit.org/blog/7734/auto-play-policy-changes-for-macos/)
Upvotes: 22
Views: 6194
Reputation: 4667
You need to change your algorithm from
user clicks -> loads audio information -> changes source -> play
to
user clicks -> changes source -> play
Basically you need to change src
in audio
and start playback right after user clicks play button.
iOS prevents autoplay when intermediary fetching is present.
Upvotes: 1
Reputation: 425
Autoplay
audio.autoplay = true;
Please note that with mobile devices W3C have specified the user must trigger the playback in order to avoid mobile data charges.
Therefore with mobile devices, you will need to present the user with the button to trigger the autoplay.
const btn = document.createElement('BUTTON');
const textLabel = document.createTextNode('Play');
const audio = new window.Audio();
audio.src = 'https://raw.githubusercontent.com/vnglst/autoplay-tutorial/master/mp3/modem-sound.mp3';
audio.autoplay = true;
// audio.controls = true
btn.appendChild(textLabel)
document.getElementById('root').appendChild(btn);
document.getElementById('root').appendChild(audio);
const mockedPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const src = 'https://raw.githubusercontent.com/vnglst/autoplay-tutorial/master/mp3/winamp.mp3';
return resolve(src);
}, 500);
})
btn.onclick = (e) => {
mockedPromise.then(src => {
audio.src = src;
audio.play();
})
}
<div id='root'/>
Upvotes: 0