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.

AUTOPLAY BLOCKED

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

hero-video.html
<!-- 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>
All three must be present: 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.

video-autoplay.js
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)

audio-video.js
// 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);
  }
});
Important: There is no way around the user-gesture requirement for audio. Apple enforces this at the OS level. Any library or hack that claims otherwise will eventually stop working after a Safari update.

Browser Support

BrowserMin VersionStatusNotes
Safari iOS 10+10+PartialAutoplay only with muted + playsinline; audio requires user gesture
Safari iOS < 10< 10Not supportedNo autoplay at all — requires user tap
Chrome iOSAllPartialSame WebKit engine — requires muted + playsinline
Chrome Android53+SupportedMuted autoplay allowed; unmuted requires user gesture
FirefoxAllPartialUnmuted autoplay may be blocked by browser policy
Safari macOS11+PartialMuted autoplay allowed; unmuted blocked by default

Further Reading