Keep the display active exactly when it matters
Some web applications need a screen that stays on: a cooking recipe you do not want to tap while stirring, a video call without camera movement, or a progress bar that runs for minutes. The Screen Wake Lock API solves exactly this problem without tricks like invisible videos or dummy timers.
Table of Contents
- 1. Why screens fall asleep in the first place
- 2. The core mechanism: navigator.wakeLock.request()
- 3. Releasing locks correctly
- 4. Reacting to visibility changes
- 5. Feature detection and fallback
- 6. Typical use cases
- 7. Battery consumption and responsible use
- 8. Security and permission model
- 9. A complete practical example
- 10. Summary
- 11. FAQ
1. Why screens fall asleep in the first place
Operating systems dim and lock screens after a period of inactivity to save battery. That behavior is correct in almost every situation, but it becomes a problem the moment a web page is meant to actively guide a user who is not constantly touching the screen. A recipe widget, a workout timer, or a full-screen presentation are typical candidates.
Before the Wake Lock API, developers reached for workarounds: playing an invisible, endlessly looping video, or scrolling the page by a pixel every few seconds via JavaScript. Both approaches worked unreliably, wasted battery unnecessarily, and felt like a hack because that is exactly what they were. The native API replaces these tricks with an explicit request controlled by the browser.
2. The core mechanism: navigator.wakeLock.request()
The entry point is navigator.wakeLock.request('screen'). The call returns a promise that resolves with a WakeLockSentinel object once the browser agrees to the request. Currently the specification only defines the type 'screen', though future extensions for other wake states are already being discussed.
It matters that the request runs in a secure context (HTTPS) and, in most cases, as a reaction to a user gesture, similar to the Fullscreen or Notifications API. A lock requested purely on page load without any interaction can be rejected by the browser or may not even be offered at all.
let wakeLockSentinel = null;
async function requestWakeLock() {
try {
wakeLockSentinel = await navigator.wakeLock.request('screen');
console.log('Wake Lock active');
wakeLockSentinel.addEventListener('release', () => {
console.log('Wake Lock was released');
});
} catch (err) {
console.error(`Wake Lock failed: ${err.name}, ${err.message}`);
}
}
document.querySelector('#start-cooking').addEventListener('click', requestWakeLock);
3. Releasing locks correctly
An active wake lock is a resource you must not forget to release. As soon as the action is finished, whether the recipe is done or the upload has completed, call sentinel.release(). The sentinel object becomes invalid afterward and must be requested again if needed. A second, repeated call to release() on the same object does not throw an error in most implementations, but it is still advisable to manage your own state cleanly instead of relying on this tolerant behavior.
A frequently overlooked edge case: the browser automatically releases the lock once the tab moves to the background, gets minimized, or the screen locks. When the user returns to the page, the old sentinel is already invalid, even if release() was never explicitly called. Your own logic must detect this state and request the lock again when appropriate.
async function releaseWakeLock() {
if (wakeLockSentinel) {
await wakeLockSentinel.release();
wakeLockSentinel = null;
}
}
document.querySelector('#stop-cooking').addEventListener('click', releaseWakeLock);
4. Reacting to visibility changes
Since the lock automatically expires when moved to the background, it is worth listening to the document's visibilitychange event. When the tab becomes visible again and a lock was previously desired, the application can re-request the wake lock without a new user gesture, because the original consent is considered still valid.
This combination of a visibility listener and a re-request makes the wake lock logic robust against app switching on mobile devices, where users briefly open another app and then return. Without this mechanism, the screen could fall asleep again after returning, even though the activity is technically still running.
document.addEventListener('visibilitychange', async () => {
if (wakeLockWanted && document.visibilityState === 'visible') {
wakeLockSentinel = await navigator.wakeLock.request('screen');
}
});
5. Feature detection and fallback
The API is not yet available everywhere, older mobile browsers and some desktop browsers do not fully support it. A simple check for 'wakeLock' in navigator is enough to determine whether the feature is offered before calling it.
If support is missing, the application should not simply stay silent, it should give the user a subtle hint, for example a note to keep the screen on manually or to increase the display timeout in system settings. It is important that the core functionality of the page remains fully usable without the wake lock, the API is a pure convenience feature.
6. Typical use cases
Besides recipe apps and video conferencing tools, presentation software, kiosk systems, fitness timers, and dashboards in control rooms all benefit from the API. Anywhere content is meant to be viewed over an extended period without touch interaction, the wake lock is a fitting solution.
Loading screens for lengthy uploads or file conversions are another good example: the user is actively waiting for a result but does not want to constantly tap the screen just to keep it awake. A wake lock during the loading process, released immediately upon completion, noticeably improves the experience here.
In education, interactive learning apps where a user follows a longer explainer video or a guided exercise also benefit from a stable wake lock, since a screen falling asleep mid-way interrupts the learning flow and forces an unlock right in the middle of a thought process. The same applies to meditation and breathing apps that display visual guidance over several minutes while the user's hands are not free to touch the screen. Self-checkout terminals and digital signage in waiting areas that run on a web page frequently rely on the Wake Lock API too, avoiding separate kiosk software while still staying visible at all times.
7. Battery consumption and responsible use
A permanently bright screen is one of the biggest battery drains on mobile devices. The Wake Lock API is therefore not a free pass to use it wherever convenient, but a targeted tool for clearly scoped time periods with real need.
Good practice means giving the user control: a visible toggle that shows whether the screen is currently being kept awake and that can be disabled at any time. This keeps it transparent why the battery drains faster and leaves the user in charge of their device. An automatic deactivation after a sensible maximum duration, for example after two hours of uninterrupted activity, is also a good safeguard against locks accidentally staying active forever when an application was forgotten in the background.
8. Security and permission model
Unlike camera or location access, the browser does not show a visible permission dialog for the Wake Lock API, control happens implicitly through the secure context and the user gesture requirement. Some browsers additionally allow restricting the behavior for embedded iframes via a Permissions-Policy header such as Permissions-Policy: screen-wake-lock=().
This is particularly relevant for pages with third-party content: an embedded ad widget should normally not be able to keep the entire page's screen awake. The Permissions-Policy lets you assign this capability specifically only to your own, trusted frames.
9. A complete practical example
In practice, requesting, releasing, and visibility handling are best encapsulated in a small reusable class or utility function that any component can use independently. This keeps the actual application logic free of API details while the wake lock state is managed centrally.
The snippet below shows a pattern that combines requesting, automatic restoration after a tab switch, and clean cleanup when leaving the page. The table afterward compares the workarounds that were common before with the native API.
class WakeLockManager {
#sentinel = null;
#wanted = false;
async enable() {
this.#wanted = true;
if (!('wakeLock' in navigator)) return false;
try {
this.#sentinel = await navigator.wakeLock.request('screen');
return true;
} catch {
return false;
}
}
async disable() {
this.#wanted = false;
if (this.#sentinel) {
await this.#sentinel.release();
this.#sentinel = null;
}
}
init() {
document.addEventListener('visibilitychange', async () => {
if (this.#wanted && document.visibilityState === 'visible' && !this.#sentinel) {
await this.enable();
}
});
}
}
const wakeLock = new WakeLockManager();
wakeLock.init();
| Approach | Reliability | Battery cost | Browser support |
|---|---|---|---|
navigator.wakeLock.request() |
High, official API | Controlled, explicit lock | Chrome, Edge, Opera, partial Safari |
| Invisible looping video | Medium, OS dependent | Unnecessarily high due to video decoding | Broad but unofficial |
| Periodic micro-scrolling | Low, feels like a bug | Increased through constant reflows | Broad but unofficial |
| No mechanism | Screen sleeps after timeout | Minimal | Always available |
Mironsoft
Modern browser APIs, performance, and maintainable JavaScript
JavaScript that holds up in the real browser, not just in the tutorial?
We review existing frontend code for outdated patterns, unnecessary dependencies, and performance traps, then replace them with modern, native browser APIs that mean less bundle weight and less maintenance burden.
Code Review
Systematically finding outdated patterns, unnecessary dependencies, and memory leaks.
Performance Optimization
Improving bundle size, load time, and runtime performance with modern APIs.
Modernization
Deliberately introducing native browser APIs instead of heavy libraries.
10. Summary
Screen Wake Lock API: The Essentials at a Glance
Entry point
navigator.wakeLock.request('screen') returns a promise with a WakeLockSentinel.
Release
Always call sentinel.release() once the activity is finished.
Background
Locks expire automatically on tab switch, re-request via visibilitychange.
Fallback
Check feature detection and keep core functionality usable without the lock.