The Problem
A video with the autoplay attribute plays automatically on desktop and Android, but sits paused on iOS — even for silent background videos, hero loops, or ambient animations. The browser silently ignores the autoplay attribute without throwing any errors.
Video paused — autoplay blocked
Root Cause
Apple introduced video autoplay restrictions in iOS 10 as a response to user complaints about mobile data usage and battery drain. The policy: video can autoplay only if it is muted AND marked with the playsinline attribute. Audio (or unmuted video) requires an explicit user gesture — a tap.
Without playsinline, iOS opens the video in its full-screen native player, which also blocks autoplay. Without muted, even playsinline videos won't autoplay.
Fix — Required attributes for autoplay
<!-- All three attributes are required for iOS autoplay -->
<video
autoplay
muted
playsinline
loop
preload="auto"
>
<source src="hero.mp4" type="video/mp4">
<source src="hero.webm" type="video/webm">
</video>autoplay requests it, muted satisfies iOS policy, and playsinline prevents the fullscreen player from launching.Fix — Handle autoplay failure gracefully
The video.play() method returns a Promise that rejects if autoplay is blocked. Use this to show a play button as fallback.
const video = document.querySelector('video');
const playPromise = video.play();
if (playPromise !== undefined) {
playPromise
.then(() => {
// Autoplay started — hide any play button overlay
document.querySelector('.play-overlay')?.remove();
})
.catch((error) => {
// Autoplay was blocked — show play button
document.querySelector('.play-overlay')?.classList.add('visible');
console.warn('Autoplay blocked:', error);
});
}Fix — Videos that need audio (user-gesture required)
// Videos with audio must start from a user interaction
const playBtn = document.querySelector('#play-btn');
const video = document.querySelector('video');
playBtn.addEventListener('click', async () => {
video.muted = false; // unmute now that we have a gesture
try {
await video.play();
playBtn.style.display = 'none';
} catch (err) {
console.error('Play failed:', err);
}
});Browser Support
| Browser | Min Version | Status | Notes |
|---|---|---|---|
| Safari iOS 10+ | 10+ | Partial | Autoplay only with muted + playsinline; audio requires user gesture |
| Safari iOS < 10 | < 10 | Not supported | No autoplay at all — requires user tap |
| Chrome iOS | All | Partial | Same WebKit engine — requires muted + playsinline |
| Chrome Android | 53+ | Supported | Muted autoplay allowed; unmuted requires user gesture |
| Firefox | All | Partial | Unmuted autoplay may be blocked by browser policy |
| Safari macOS | 11+ | Partial | Muted autoplay allowed; unmuted blocked by default |
Further Reading
- WebKit Blog: "New Video Policies for iOS"The original announcement of iOS 10 autoplay restrictions and playsinline attribute.
- MDN: HTMLMediaElement.play()Returns a Promise — lets you detect and handle autoplay failures gracefully.
- Apple Developer: Media AutoplayApple's official documentation on delivering video content for Safari on iOS.