Screen Wake Lock API: Lifecycle and Privacy Boundaries
Manage screen wake locks with visibility recovery, explicit release, and a useful fallback when browser mediation ends the lock.
BotBrowser Team
Want the structured docs for Platform?
This article lives in the editorial library. For step-by-step setup, reference material, and ongoing updates, jump into the docs section.
The Screen Wake Lock API lets a page request that the browser keep the display from dimming while a user-visible task needs it. The request is temporary and browser-mediated: visibility changes, policy, power conditions, or explicit release can end it. BotBrowser can validate request, release, and visibility-recovery paths in a controlled profile, but it cannot keep a physical display awake or guarantee support on every runner.
TL;DR
- Feature-detect
navigator.wakeLockand request only for a clear, user-visible purpose. - Keep the
WakeLockSentinel, listen forrelease, and release it when the task ends. - Re-acquire after
visibilitychangeonly when the task still needs it and capability remains available. - A wake lock is not background execution, battery access, identity evidence, or a screen-on guarantee.
- Keep the page useful with a fallback when the browser declines or releases the lock.
Contents
Request and release
Call navigator.wakeLock.request('screen') from an intentional workflow such as reading a recipe or presenting a kiosk step. Handle rejection as a normal capability result. Store the sentinel, update the UI with a short status, and attach a release listener. Never assume a resolved promise means the display will remain awake for the whole route.
Release the sentinel when the user leaves the task, pauses playback, submits the final step, or chooses a power-saving option. Make cleanup idempotent because route teardown and browser release can race. Do not use a lock to hide an idle timeout or to keep an unobserved background job alive.
Visibility and privacy boundaries
Browsers commonly release screen locks when a document becomes hidden. On visibilitychange, read the current task state and re-request only when the document is visible, the user still expects the display to stay awake, and the API is available. A failed re-acquisition should expose the ordinary controls and a manual continuation path.
The API does not expose battery percentage, device identity, or a reliable power profile. A granted or denied request is a browser observation for one context, not proof about a person, device, location, or other browser. Keep telemetry to the purpose, browser tuple, outcome, and retention needed for support; do not join it with account or hardware fingerprints.
Testing with BotBrowser
Use a synthetic fixture that records capability detection, request, explicit release, hidden-to-visible recovery, and fallback UI. Test unsupported API, rejected request, release before teardown, duplicate cleanup, and a visibility return after the task ended. Assert visible outcomes and status text rather than a physical monitor state.
BotBrowser can compare declared browser builds and isolated profiles for these paths. It cannot control operating-system power policy, measure battery, keep a physical display awake, or turn a successful request into a universal support claim. Keep fixture identifiers synthetic and do not retain user content.
A customer-ready implementation pattern.
The most reliable implementation keeps the wake-lock lease beside the task that owns it. A video player, recipe stepper, presentation, or warehouse checklist can each have a small controller with three states: idle, requested, and released. The controller should be able to explain why it is in each state to the user interface. This makes a browser release an ordinary state transition instead of an exception that leaves a stale “screen will stay on” message visible.
Start with a capability check at the point where the user enables the feature. Do not make page load depend on it. A useful control can say “Keep screen awake while presenting” and remain available even when navigator.wakeLock is missing. When the user turns it on, call request('screen') inside that user action, catch the promise rejection, and show a compact status such as “Screen wake lock unavailable; presentation controls remain available.” This communicates the outcome without revealing implementation details or asking the user to troubleshoot a technical error.
Keep the sentinel in one place and make release idempotent. A route change, an explicit toggle, and a browser-originated release event may all arrive close together. Calling a cleanup function more than once should be harmless. Clear the stored sentinel before awaiting another request, remove event listeners with the same function reference, and update the control only after the state transition is known. This prevents duplicate listeners and prevents an old sentinel from being mistaken for the current lease.
let sentinel = null;
let wantsWakeLock = false;
async function enableScreenWakeLock() {
wantsWakeLock = true;
if (document.visibilityState !== 'visible' || !('wakeLock' in navigator)) return false;
try {
sentinel = await navigator.wakeLock.request('screen');
sentinel.addEventListener('release', onBrowserRelease, { once: true });
return true;
} catch {
sentinel = null;
return false;
}
}
function onBrowserRelease() {
sentinel = null;
renderWakeLockStatus('released');
}
async function disableScreenWakeLock() {
wantsWakeLock = false;
const current = sentinel;
sentinel = null;
if (current && !current.released) await current.release();
}
document.addEventListener('visibilitychange', async () => {
if (document.visibilityState === 'visible' && wantsWakeLock && !sentinel) await enableScreenWakeLock();
});
The example intentionally treats a rejected request and a later release as normal product states. Your UI can offer a manual “Continue without wake lock” action, keep the same task controls, and let the user continue reading or presenting. A fallback should not repeatedly prompt, spin a timer, or simulate a permanently active display. If the task has ended while the document was hidden, leave wantsWakeLock false so returning to the tab does not unexpectedly acquire a new lease.
Visibility recovery without surprising the user.
Visibility recovery needs a task-level decision, not a blind retry. A tab may become hidden because the user opened another app, locked the workstation, switched windows, or navigated away. When it becomes visible, check that the task is still active, the user preference is still enabled, and the page still has the capability. If any condition is false, keep the lock released. If all are true, request again and report the resulting status.
Avoid treating visibilitychange as proof that a physical display is available. A visible document can still be affected by operating-system settings, external monitors, browser policy, or power-saving behavior. The useful promise to a customer is narrower: while the browser grants a screen wake lock, the page asks the browser not to dim the display for this task. That wording remains accurate when a runner, laptop policy, or browser version declines the request.
For multi-step workflows, persist only the user-level intent that is needed to restore the task, such as keepScreenAwake: true for the current presentation. Do not persist a sentinel, a device identifier, battery data, or a hidden “last granted” fingerprint. Recompute capability and request state after navigation or a fresh profile. This both simplifies recovery and reduces privacy exposure.
Privacy and power boundaries for product teams.
Wake Lock answers a narrow question: whether the browser accepted a request for a screen lock in the current context. It does not answer whether a person is present, whether a device is trusted, where the device is, how much battery remains, or whether another application is using power. Do not turn an accepted request into an attendance signal or a hardware classification signal.
Telemetry can remain useful without becoming a device profile. Record the feature purpose, coarse browser version if support debugging requires it, request outcome, release reason, and a short retention window. Aggregate counts for unsupported, rejected, released, and successfully reacquired states. Avoid collecting page contents, account identifiers, exact device details, or a cross-site identifier just to understand whether the control worked. Give administrators a way to disable optional diagnostics and document the retention period.
Power-respecting UX is part of the feature. Provide a visible toggle, a status that can be understood at a glance, and an explicit way to stop keeping the screen awake. Release when the user submits the final step, pauses a presentation, leaves a kiosk flow, or selects battery saver. If the browser releases the lock, do not fight the decision with rapid retries. A short status plus the ordinary navigation controls is more trustworthy than a hidden workaround.
A focused compatibility fixture.
For a support or release check, build a small fixture rather than relying on a full application route. The fixture should expose buttons for “Request,” “Release,” and “End task,” a checkbox for the user intent, and a status log with timestamps. Add a synthetic visibility control in the harness or test runner so the same state machine can be exercised for hidden and visible transitions. The expected records are straightforward: capability detected or absent, request fulfilled or rejected, release observed, visibility recovery attempted or skipped, and fallback displayed.
Run the fixture in an isolated BotBrowser profile for each supported browser build. BotBrowser is useful here because it can validate the JavaScript lifecycle and compare controlled profiles. It can verify that a request is made only for the intended fixture, that explicit cleanup occurs, and that returning from hidden state does not create an extra request after the task ended. It cannot verify a physical monitor, operating-system sleep policy, battery percentage, or support on every runner. Those limitations belong in the test result and in customer documentation.
Keep the fixture deterministic. Use synthetic task names, deterministic timestamps where possible, and a bounded log. Test at least these cases:
- The API is absent and the fallback remains usable.
- The request is rejected and no retry loop starts.
- The request succeeds and explicit release updates the status.
- The browser releases the sentinel and the UI reflects that release.
- The document becomes hidden and visible while the task is active, causing at most one intentional re-acquisition.
- The task ends while hidden, so visibility recovery does not request a new lock.
- Cleanup runs twice without throwing or leaving a listener behind.
For each case, assert the status text and state transitions. Do not assert that a screen stayed physically lit. A passing fixture demonstrates that your application honors the browser contract; it does not override that contract.
Troubleshooting common outcomes.
“The request works locally but not in a customer runner” is usually a capability or policy difference, not an application error. First confirm that the feature is detected in the target profile and that the request is made while the document is visible. Then inspect the recorded outcome and release reason. Compare browser build and profile configuration through the controlled BotBrowser fixture, while keeping the customer’s page content out of diagnostics.
If users report that the screen dims after switching tabs, explain that hidden documents commonly lose the lock. Ensure the task intent is retained and that re-acquisition occurs only after visibility returns. If users report a stale status, check that the release listener clears the sentinel and that route teardown calls the same idempotent cleanup path. If battery usage is a concern, make the default opt-in and release promptly when the workflow is idle.
The practical contract is therefore small and testable: ask for a screen lock for a visible task, react to the browser’s answer, release it when the task ends, and keep the task useful when the answer is no. That contract gives customers predictable behavior without promising capabilities that belong to the operating system or the physical display.
Teams can turn this contract into a supportable rollout by assigning ownership for each visible state. The product owner decides when the control appears and what “task active” means. The frontend owner owns the sentinel, event listener, and cleanup path. The support owner receives a short outcome code rather than a browser trace: unsupported, rejected, released, reacquired, or disabled-by-user. This division gives customers a clear answer when a presentation dims without suggesting that BotBrowser or the application can override a browser or operating-system decision.
Before enabling the control for a shared workstation, verify the user journey with the actual workflow: start a presentation, switch away and back, pause it, finish it, and reopen it. Confirm that the control label follows the state, that the fallback does not remove navigation, and that a second tab cannot inherit an old lease. On a managed kiosk, document who can change power policy separately from who can change application preferences. The Screen Wake Lock API only covers the browser request; a device-management policy may still dim or sleep the display.
When a customer asks for a guarantee that the display never dims, explain the boundary and offer a scoped alternative. A site can keep its controls visible, re-acquire a browser-granted lock after visibility returns, and show a manual continuation path. It cannot promise a physical screen state, infer battery health, or use a wake-lock result as proof that an operator is present. Setting that expectation early avoids both surprise and unnecessary collection of device data.
Operational checklist for a real workflow.
Before enabling the feature, name the task that owns the lease and the person who can stop it. A presentation may own the lease from the first slide until the closing screen. A warehouse checklist may own it only while an item is being scanned. A recipe page may offer it while a cooking step is active, then release it when the timer or step is complete. This ownership rule keeps a route transition from leaving a lock attached to an unrelated page. It also gives support staff a clear question to ask: is the task still active, or did the user merely leave the tab open?
Treat permission and capability as separate observations. Screen Wake Lock does not use a persistent permission prompt in the same way as camera or location, yet a browser can reject the request because the document is hidden, the platform is constrained, or a browser policy disallows it. Your interface should therefore show an ordinary feature status, not a permission promise. The user can turn the control on, see that the request was accepted or unavailable, and continue with the same task either way. Do not block sign-in, checkout, navigation, or data entry on a wake-lock result.
Evidence that a release is intentional.
For each workflow, define the user-visible events that end the lease: an explicit toggle, completion, pause, route change, sign-out, or a browser release event. Record one bounded outcome for the event and clear the sentinel reference. If a user returns to a visible document, the controller may request again only when the task state and user intent still agree. A short transition log can distinguish accepted, released-by-browser, released-by-user, unavailable, and reacquired. Those labels are enough to explain behavior without storing page text or a persistent device identifier.
Test the transition table with a fake sentinel as well as a browser fixture. The fake should support released, a one-time release event, and a rejected request so cleanup can be exercised deterministically. Then run the same table in an isolated BotBrowser profile: visible request, hidden release, visible recovery, explicit stop, and task completion while hidden. Compare the status log and request count for each case. This demonstrates that application state follows the browser contract; it does not claim that a laptop panel, external monitor, or operating-system sleep setting was controlled.
Privacy review before rollout.
Keep wake-lock telemetry optional and short-lived. A useful record contains the task category, coarse browser release channel when needed for compatibility support, outcome label, and release reason. It should not contain the document title, typed content, account history, exact hardware model, battery level, or a cross-site identifier. If an administrator exports diagnostics, provide a redacted summary and a retention date. Delete the summary when the support question is resolved. A successful request is not presence detection, attendance evidence, or a signal that a trusted operator is in front of the screen.
Finally, make the fallback part of acceptance criteria. When the API is absent or a request is rejected, the task must still expose its normal navigation, pause, save, and completion controls. When the browser releases the lock, show a concise status and let the user continue without repeated prompts. This is the behavior that remains dependable across browser versions, managed profiles, remote sessions, and power policies, and it is the boundary BotBrowser can validate with repeatable synthetic evidence.
Conclusion
Treat a screen wake lock as a short-lived lease around a user-visible task. Detect capability, release deliberately, recover after visibility changes, and keep a first-class fallback. The W3C Screen Wake Lock specification, MDN Screen Wake Lock API, MDN WakeLockSentinel, and W3C Privacy Principles define the browser and privacy boundaries. See BotBrowser advanced features for controlled compatibility fixtures.
Related reading
Sources
Related Articles
Take BotBrowser from research to production
The guides cover the model first, then move into cross-platform validation, isolated contexts, and scale-ready browser deployment.