JavaScript Screen Wake Lock Table
| Piece | What it does | Field note |
|---|---|---|
navigator.wakeLock.request('screen') | Keep screen on | Recipe apps, dashboards, workout timers - the legit uses |
wakeLock sentinel.release() | Give it back | Battery is the user's - release the moment the feature ends |
visibilitychange re-acquire | The auto-drop dance | Screen lock DROPS on tab hide - re-request on visible |
User gesture + HTTPS | The gates | Same family as clipboard/camera - and the OS may still refuse |
sentinel.released / onrelease | The state read | The system can revoke - listen, don't assume |
NO wake lock type = system | Screen only today | CPU wakelocks are not a web thing - background work uses SW |
Battery courtesy | The ethics | The screen is the biggest battery cost - the feature must earn it |
vs video autoplay | The old hack | Playing silent video to keep awake = the hack wake lock replaces |
Screen Wake Lock keeps the display awake while a feature needs it: navigator.wakeLock.request('screen') returns a sentinel whose release() gives the screen back - recipe steps, workout timers, dashboards and turn-by-turn views are the honest uses.
Bottom line: the screen is the biggest battery cost a page controls. Two platform rules shape the lifecycle: the lock DROPS when the tab is hidden (re-acquire on visibilitychange), and the system may revoke it at any time (listen to onrelease rather than assuming you hold it).
The honest part: wake locks are a borrowed power - the OS may still refuse, the user can see the indicator, and the silent-video-playing hack this API replaces was exactly the kind of battery abuse that made platforms gate it so tightly.
How to use
- Request inside a gesture: let sentinel = await navigator.wakeLock.request('screen') from the feature's start button - gesture and HTTPS are the gates.
- Re-acquire on return: document.addEventListener('visibilitychange', async () => { if (visible && wantLock) sentinel = await relock(); }) - hiding the tab always drops it.
- Release the moment the feature ends: sentinel.release() in stop handlers, on navigation away, and when the user pauses - the lock is theirs, borrowed.
Frequently asked questions
When is a wake lock justified, and when is it abuse?
Justified when the user's eyes are on the screen but their hands are busy or away: a recipe's step view, a workout timer, a dashboard being monitored, turn-by-turn directions. The test: without the lock, the feature FAILS (the screen dims mid-recipe) rather than merely being nicer. Abuse is everything else: keeping the screen on so ads stay visible, holding the lock across idle periods 'just in case', or requesting it on page load before the user opted into the feature. The signal of respect: the lock is requested inside a deliberate start action, released the moment the feature ends or pauses, and the UI shows that the screen is being kept awake.
Why does the wake lock drop when the tab is hidden, and how do I handle it?
Platform priority: a hidden tab cannot be what the user is looking at, so holding the screen on for it is definitionally wrong - the browser drops the lock automatically on visibilitychange. The consequence: returning to the tab finds no lock, and your recipe app's screen dims. The handling is the re-acquire dance: track the DESIRED state separately from the actual sentinel (a boolean 'the user asked for keep-awake'), and on visibilitychange-visible, if desired and no sentinel, request again. This desired-versus-actual split is also how you handle system revocations - onrelease fires, you mark actual as none, and the same reconciliation loop restores it if it should be held.
How does the wake lock API replace the silent-video hack?
By naming the intent. The old trick: play a muted, tiny, hidden video with autoplay - platforms kept screens awake during video playback, so the video kept the screen awake. It worked, at the cost of real decode battery, a media session icon appearing, MediaSession confusion and a workaround so common browsers added heuristics against it. The wake lock API states the intent ('keep the screen on') directly, with no decode cost beyond the display itself, no phantom media session, and explicit user-visible signaling. Any codebase still shipping the hidden-video hack on a wake-lock-supporting browser is burning battery and shipping a media session artifact for nothing.
What happens when the system revokes the lock mid-feature?
Your feature keeps running; the screen may dim. The sentinel fires the released event and released returns true - the system decided (thermal, battery saver, policy) that the display may sleep. The correct response is informational, not adversarial: update the UI ('screen lock paused - tap to re-enable'), stop assuming visibility of live content, and re-request on the next user gesture if they want it back. Fighting revocation - re-requesting in a loop - is both a battery attack and self-defeating: platforms escalate against pages that hammer locked APIs. The wake lock is a granted courtesy; released events are the system exercising its side of the contract.