
How to Enable Autoplay Videos in Low Power Mode on iOS and macOS
Autoplay in Safari can turn off in Low Power Mode. Below is a tested approach: playsinline, error handling for play() and fallback.
What Really Blocks Autoplay in Safari
Safari often allows muted videos to autoplay, but on iOS, the user or the system can disable this. In practice, autoplay on iPhones is not guaranteed . Therefore, you need to check whether the video has actually started, and if not, show a "play" button or a light, animated preview. This ensures a consistent experience, even in power-saving mode.
Proven Approach: Progressive Improvement
First, implement a standard <video> with muted , playsinline , autoplay , and a light preload. Then try to trigger playback via video.play() . If the browser rejects the promise (e.g., in low-power mode), seamlessly switch to an alternative—an animated image (GIF/WebP/APNG) or an explicit "Play" button. This respects user preferences and doesn't break the project.
<div class="hero-video"> <video id="heroVideo" preload="metadata" autoplay muted playsinline loop poster="https://site.com/video-poster.jpg" class="fullscreen-video"> <source src="https://site.com/video.mp4" type="video/mp4"> <!-- Optional: add WebM for broader support --> <source src="https://site.com/video.webm" type="video/webm"> </video> <!-- Fallback shown if autoplay is blocked (eg, iOS Low Power Mode) --> <img id="fallbackGif" src="https://site.com/video-fallback.gif" alt="Animated preview" style="display:none" /> <button id="playButton" class="button button--primary" aria-controls="heroVideo" aria-label="Play video" hidden>Play</button> </div> <!-- Styles (example) --> <style> .fullscreen-video { width: 100%; height: auto; } .button--primary { position:absolute; left: 1rem; bottom: 1rem; } @media (prefers-reduced-motion: reduce) { .hero-video video { display: none; } #fallbackGif { display: block !important; } } </style> <!-- Script --> <script> (function () { var v = document.getElementById('heroVideo'); var fallback = document.getElementById('fallbackGif'); var btn = document.getElementById('playButton'); function showFallback() { if (v) v.style.display = 'none'; if (fallback) fallback.style.display = 'block'; if (btn) btn.hidden = false; } function tryAutoplay() { if (!v || !v.play) return showFallback(); v.muted = true; // important for mobile autoplay v.playsInline = true; var p = v.play(); if (p && typeof p.then === 'function') { p.then(function () { // autoplay succeeded; keep video visible }).catch(function () { // autoplay blocked (eg, iOS Low Power Mode) showFallback(); }); } else { // Older browsers showFallback(); } } document.addEventListener('DOMContentLoaded', tryAutoplay); if (btn) { btn.addEventListener('click', function () { if (fallback) fallback.style.display = 'none'; if (v) { v.style.display = ''; v.play().catch(function(){ /* user can tap again if needed */ }); } btn.hidden = true; }); } })(); </script> Why It Works (and What to Avoid) <div class="hero-video"> <video id="heroVideo" preload="metadata" autoplay muted playsinline loop poster="https://site.com/video-poster.jpg" class="fullscreen-video"> <source src="https://site.com/video.mp4" type="video/mp4"> <!-- Optional: add WebM for broader support --> <source src="https://site.com/video.webm" type="video/webm"> </video> <!-- Fallback shown if autoplay is blocked (eg, iOS Low Power Mode) --> <img id="fallbackGif" src="https://site.com/video-fallback.gif" alt="Animated preview" style="display:none" /> <button id="playButton" class="button button--primary" aria-controls="heroVideo" aria-label="Play video" hidden>Play</button> </div> <!-- Styles (example) --> <style> .fullscreen-video { width: 100%; height: auto; } .button--primary { position:absolute; left: 1rem; bottom: 1rem; } @media (prefers-reduced-motion: reduce) { .hero-video video { display: none; } #fallbackGif { display: block !important; } } </style> <!-- Script --> <script> (function () { var v = document.getElementById('heroVideo'); var fallback = document.getElementById('fallbackGif'); var btn = document.getElementById('playButton'); function showFallback() { if (v) v.style.display = 'none'; if (fallback) fallback.style.display = 'block'; if (btn) btn.hidden = false; } function tryAutoplay() { if (!v || !v.play) return showFallback(); v.muted = true; // important for mobile autoplay v.playsInline = true; var p = v.play(); if (p && typeof p.then === 'function') { p.then(function () { // autoplay succeeded; keep video visible }).catch(function () { // autoplay blocked (eg, iOS Low Power Mode) showFallback(); }); } else { // Older browsers showFallback(); } } document.addEventListener('DOMContentLoaded', tryAutoplay); if (btn) { btn.addEventListener('click', function () { if (fallback) fallback.style.display = 'none'; if (v) { v.style.display = ''; v.play().catch(function(){ /* user can tap again if needed */ }); } btn.hidden = true; }); } })(); </script>
- Progressive enhancement: We first attempt autoplay, and if that fails, we show a fallback or "Play" button. The user always sees the action or has a clear choice.
- Compliance with standards:
muted,playsinline,autoplayand lightpreloadare the minimum that works as widely as possible. - Avoid tricks:
<img>doesn't play MP4. If you need movement, use animated GIF/WebP/APNG.
Optional: Embedded Players (YouTube/Vimeo)
If you use iFrames, you can request muted autoplay, but the system may still block it. So always have a "Play" button overlay or a preview image handy.
<!-- Przykład Vimeo --> <iframe src="https://player.vimeo.com/video/12345?background=1&autoplay=1&muted=1&loop=1&playsinline=1" allow="autoplay; fullscreen; picture-in-picture" loading="lazy" style="width:100%;aspect-ratio:16/9;border:0"></iframe> What do you gain? <!-- Przykład Vimeo --> <iframe src="https://player.vimeo.com/video/12345?background=1&autoplay=1&muted=1&loop=1&playsinline=1" allow="autoplay; fullscreen; picture-in-picture" loading="lazy" style="width:100%;aspect-ratio:16/9;border:0"></iframe>
This approach maintains a consistent experience across iOS and desktop. It preserves the design intent while still adhering to browser rules and power-saving constraints. The result? Engagement even when autoplay is disabled.
Summary
Treat autoplay on Apple devices as a "nice-to-have" rather than a requirement. Use muted videos, check play() score, and if blocked, show an animated preview or button. This combines efficiency, accessibility, and communication consistency.