Screen Wake Lock API: Prevent Screen Dimming in JavaScript
The Screen Wake Lock API prevents a device’s screen from dimming or turning off while your web app needs it to stay on. It’s useful for reading apps, video playback, navigation, and any scenario where hands are occupied.
Feature detection
Before using the API, check for support:
if ('wakeLock' in navigator) {
// supported
}
Support is in Chrome, Safari, and Firefox across desktop and mobile. Once you confirm the API is available, you can request a screen wake lock through navigator.wakeLock.request(). The method takes 'screen' as its only argument and returns a promise because the browser may still deny the request at runtime, even when feature detection passed, if the page is hidden or the device is in a low-power state.
Requesting a wake lock
let wakeLock = null;
async function requestWakeLock() {
try {
wakeLock = await navigator.wakeLock.request('screen');
console.log('Wake lock active');
} catch (err) {
console.error('Wake lock failed:', err);
}
}
requestWakeLock();
navigator.wakeLock.request('screen') returns a Promise that resolves with a WakeLockSentinel object. The request can fail if the system is in low power mode, battery is critical, or the page is not visible.
The WakeLockSentinel object
The sentinel is your handle to the lock. Store it and use it to release later:
const sentinel = await navigator.wakeLock.request('screen');
sentinel.release(); // manually release
sentinel.released; // boolean — true if already released
sentinel.type; // "screen" for screen wake lock
Once release() is called, the sentinel is done. The system can also release it automatically.
Automatic release on visibility change
The wake lock is automatically released when the page becomes hidden. If the user switches tabs or minimizes the browser, the lock is gone. To re-acquire when the page becomes visible again:
let wakeLock = null;
async function requestWakeLock() {
if (wakeLock !== null) return;
try {
wakeLock = await navigator.wakeLock.request('screen');
} catch (err) {
console.error('Wake lock request failed:', err);
}
}
document.addEventListener('visibilitychange', async () => {
if (document.visibilityState === 'visible') {
await requestWakeLock();
}
});
This matters for single-page apps where the user might navigate away and come back. Since the API does not expose a direct way to query whether a wake lock is currently active, you need to track that state yourself. The next example wraps the lock in a small manager class that holds an internal sentinel reference and listens for release events to stay in sync with what the system is doing.
Checking lock status
You can’t query whether a wake lock is currently active directly. Track it yourself:
class WakeLockManager {
#sentinel = null;
get isActive() {
return this.#sentinel !== null && !this.#sentinel.released;
}
async acquire() {
if (this.isActive) return;
try {
this.#sentinel = await navigator.wakeLock.request('screen');
this.#sentinel.addEventListener('release', () => {
this.#sentinel = null;
});
} catch (err) {
console.error('Failed to acquire wake lock:', err);
}
}
async release() {
if (this.#sentinel) {
await this.#sentinel.release();
this.#sentinel = null;
}
}
}
Using the 'release' event on the sentinel, you can keep the manager’s state in sync if the system releases the lock. This event fires whenever the lock is released, whether your code called release() or the browser did it automatically. Listening for it is the only reliable way to detect lock loss because there is no polling API for lock status. The next snippet isolates the release event so you can see how it behaves on its own.
Handling the release event
The sentinel emits 'release' when the system releases the lock:
const sentinel = await navigator.wakeLock.request('screen');
sentinel.addEventListener('release', () => {
console.log('Wake lock released by system');
// e.g., user switched apps, battery low
});
This event is not cancelable. When it fires, the lock is already gone, so your handler should focus on cleanup or preparing to re-request. In most applications you do not use the release event in isolation. You tie wake lock lifecycle to a specific activity like video playback, acquiring the lock when the activity starts and releasing it when it stops. The next example binds wake lock directly to a video element’s play, pause, and ended events so the lock only stays active while the video is actually running.
Complete example: video player
class VideoWakeLock {
#video = null;
#sentinel = null;
constructor(video) {
this.#video = video;
}
async activate() {
if (!('wakeLock' in navigator)) return;
if (this.#sentinel) return;
try {
this.#sentinel = await navigator.wakeLock.request('screen');
} catch (err) {
console.warn('Wake lock not available:', err);
}
}
async deactivate() {
if (this.#sentinel) {
await this.#sentinel.release();
this.#sentinel = null;
}
}
}
const video = document.querySelector('video');
const wakeLock = new VideoWakeLock(video);
video.addEventListener('play', () => wakeLock.activate());
video.addEventListener('pause', () => wakeLock.deactivate());
video.addEventListener('ended', () => wakeLock.deactivate());
Permissions and restrictions
Even with the API available in the browser, a wake lock request can still be denied. The page must be visible; a background tab always gets rejected. The Permissions Policy header can also restrict access, so check your server configuration if requests fail with a NotAllowedError. You set the policy like this:
Permissions-Policy: screen-wake-lock=self
Without the permission, request() throws a NotAllowedError.
When to use it
Good fits:
- Recipe apps, keep screen on while hands are busy
- Video players, don’t let the screen sleep mid-playback
- Navigation, turn-by-turn directions without touching the phone
- Presentations, keep the slides visible during a talk
- Camera scanner, QR/barcode scanning where you hold the device
Don’t use it for things that don’t need the screen on. The API is a power user feature, not a general “my app is open” tool.
When to use wake lock
Wake lock makes sense when the user is clearly trying to keep the screen in view for an active task. Navigation, reading, scanning, and long-form playback are all good examples. In those moments, the screen turning off would interrupt the job the app is helping with. The API is a polite request to stay awake, not a permanent override, so it fits best when the app is already in a focused state and the user would expect the display to remain visible.
It is usually a bad fit for background use or vague “keep my app open” behavior. A screen that stays on without a strong reason wastes power and can annoy users. If the app can do the same job without holding the screen awake, that path is usually better. The right test is simple: does the user need to watch the screen continuously right now? If the answer is no, the lock probably should not be active.
Handling state changes
Wake lock is not a set-it-and-forget-it feature. Visibility changes, battery conditions, and system policy can all release the lock. That means your code should expect the sentinel to disappear and should know how to request it again when conditions allow. A small manager object can keep that logic in one place and keep the rest of the app from worrying about the timing details. The important part is to treat release events as normal, not exceptional.
It also helps to connect wake lock state to the UI. If a media app is actively playing, the interface can show that the screen is being held awake. If the lock drops, the app can pause, warn, or simply request it again on visibility change depending on the use case. That feedback helps users understand why the screen is staying on and gives them a clearer sense of control over the experience.
Power And Trust
Any API that affects battery life should be used with care. Wake lock is no different. The app should only request it when the user has clearly entered a mode that benefits from it, and it should release it as soon as that need ends. That habit protects battery life and builds trust because the app behaves like a good guest on the device instead of trying to stay active for no reason.
The best wake lock code is quiet. It requests the lock when needed, renews it when appropriate, and gets out of the way when the user no longer needs it. That simplicity is part of the feature’s value. It solves a narrow problem very well without asking the rest of the app to change much.
A final safety check
Wake lock should always be visible to the user in some form, even if that is only a simple status message. If the app is holding the screen awake, the user should know why. If the lock drops, the app should be ready to recover or to let the device sleep normally. That small amount of transparency keeps the feature from feeling mysterious.
The best wake lock code is the code that knows when to stop. Request it for the active task, release it when the task ends, and keep the rest of the app focused on the user experience rather than the power management details.
That restraint is what makes the feature feel respectful rather than intrusive.
It also gives the app a cleaner story for battery use, which matters any time a feature stays active for more than a moment.
That small bit of care keeps the feature aligned with the user’s intent.
And that is usually the best standard for power-sensitive features.
If the user did not ask for constant screen use, the app should not assume it.
That rule keeps the feature easy to trust.
It also keeps battery use in check.
See Also
- /guides/javascript-gamepad-api/, interact with gamepads while building interactive experiences
- /guides/javascript-web-animations-api/, smooth animations that may need to stay active during playback
- /guides/javascript-pwa-guide/, PWAs can use wake lock to keep running in the background