Open dev tools on almost any recipe site mid-scroll and check the Elements panel. Buried near the bottom, there's a <video> tag. It's one pixel. It's muted. It's on an infinite loop. And if you actually watch what it's playing — nothing. It's a single frame of black, looping forever, going nowhere.
That's not debug cruft someone forgot to delete. It's load-bearing. Rip it out and the page's actual job — keeping your phone's screen on while your hands are covered in flour — quietly breaks.
The problem it's solving
Mobile browsers don't give a web page a button that says "don't lock the screen." There's no document.staySharp(). The OS owns that timer, and for a long time the only way JavaScript could influence it was indirectly — by doing something the OS already treats as a signal that the user is still engaged.
Actively playing video is one of those signals. Every mobile OS defers the screen-lock timer while media is playing, because pausing a movie mid-scene to lock the screen would be a terrible experience. So people found the seam: play something, even if that something is deliberately nothing.
<video id="keep-awake" muted playsinline loop>
<source src="silent-1px.mp4" type="video/mp4" />
</video>
<script>
document.getElementById("keep-awake").play();
</script>
muted and playsinline are load-bearing too — without them, most mobile browsers either block the autoplay outright or hijack the screen with a fullscreen player. Libraries like NoSleep.js packaged exactly this pattern for years, because it worked, and because there wasn't anything better to reach for.
Where it falls apart
It's a real technique and it did the job — but borrowing "video playback" as a proxy for "keep the screen awake" comes with everything you'd expect from repurposing the wrong primitive:
- Autoplay policies fight you. Some mobile browsers still block autoplaying video unless it happens inside a user gesture, so the trick can silently fail on exactly the browser version where you need it most.
- It costs a decoder, not a flag. A one-pixel video is small, but "small" isn't "free" — you're spinning up video decoding hardware and a render loop to communicate a boolean.
- Nothing tells you it stopped working. If the tab is backgrounded in a way the browser doesn't treat as "still playing," the video pauses and your screen locks — with no error, no event, nothing to catch.
- It's semantically a lie. Anyone reading that markup six months from now has to reverse-engineer why there's a phantom video before they can safely touch it.
The workaround earned its keep because the platform had a real gap. In 2019, Chrome closed it.
The API that says what it means
🎮 Try it yourself
▶️ Open the interactive playground →
Runs right in your browser — poke at it and watch the concept react live.
The Screen Wake Lock API does exactly one thing, and its name doesn't lie about it:
let wakeLock = null;
async function requestWakeLock() {
try {
wakeLock = await navigator.wakeLock.request("screen");
console.log("Screen will stay on");
} catch (err) {
// Fails if the document isn't visible, isn't a secure context,
// or the platform refuses for its own reasons.
console.error(`${err.name}: ${err.message}`);
}
}
navigator.wakeLock.request("screen") — "screen" is currently the only lock type the spec defines — returns a promise for a WakeLockSentinel. While that sentinel is alive, the screen won't dim or lock on its own. No <video>, no decoder, no lying markup. It needs a secure context (HTTPS or localhost) and the document has to actually be visible when you call it, or the promise rejects with a NotAllowedError.
Releasing it is just as direct:
async function releaseWakeLock() {
await wakeLock?.release();
wakeLock = null;
}
That would be the whole post, except the API has one behavior that catches almost everyone the first time.
The part the happy path hides
Switch away from the tab holding the lock — another window, another app, the phone's lock button — and the browser releases the wake lock for you. Silently. No exception. No rejected promise. Your wakeLock variable still points at a sentinel object; it's just that the sentinel's job is now over, and nothing forces you to notice.
Come back to the tab, and the screen goes right back to locking on schedule — because as far as the platform's concerned, you never asked it not to. If your only test was "click the button once, watch the screen stay on for thirty seconds," you'll ship this and find out from a support ticket instead.
The sentinel does expose a release event, and MDN's own guide handles the whole thing with a visibilitychange listener that re-requests the lock the moment the tab becomes visible again:
document.addEventListener("visibilitychange", async () => {
if (wakeLock !== null && document.visibilityState === "visible") {
await requestWakeLock();
}
});
One wrinkle worth planning for yourself: that condition only checks "did we ever request a lock," not "does the user still want one." If you also let people manually turn the feature off, null out wakeLock on that path too — otherwise switching tabs and back will quietly turn a lock the user explicitly released back on.
Ship it without breaking the browsers that don't have it
Feature-detect before you touch any of this:
if ("wakeLock" in navigator) {
requestWakeLock();
} else {
// Fall back to whatever you were doing before, or do nothing —
// a screen that locks isn't a crash.
}
Support is solid across Chrome, Edge, and Opera, and Safari added it in 16.4. Firefox has historically lagged on this one — check caniuse.com for the current state before you rely on it, and always keep the feature-detect branch rather than assuming.
The one-pixel video, decoded
Next time a cooking site keeps your screen alive while you're up to your elbows in dough, you'll know what's happening under the hood — and whether it's a phantom <video> tag or three lines calling an API built for exactly that job.
If you're still shipping the video trick somewhere, what's holding you back from swapping it — browser support, or just not knowing the replacement existed?
🧠 Test yourself
Think it clicked? Take the 7-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
🚀 Want more like this? Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.