# Screen Wake Lock API

> navigator.wakeLock.request("screen") keeps the display on while the page is visible. WakeLockSentinel, each NotAllowedError condition, auto-release, iOS caveat.

`navigator.wakeLock.request("screen")` asks the operating system not to dim or lock the display while the document stays visible, and resolves with a `WakeLockSentinel` that you release yourself or that the browser releases for you the moment the page is hidden. The lock is the right tool for a recipe view, a presentation, or a live tracking screen; it does nothing for background work, because a hidden document can neither hold nor request one.

Chrome 84, Edge 84, Firefox 126, and Safari 16.4 on macOS implement the API without caveats. Safari on iOS 16.4 to 18.3 is recorded as partial in browser-compat-data: the lock has no effect in a standalone Home Screen web app ([WebKit bug 254545](https://webkit.org/b/254545)), and the entry lists iOS 18.4 as the first full release.

## Syntax

```js
navigator.wakeLock.request()
navigator.wakeLock.request(type)

sentinel.release()
```

`request()` returns a `Promise<WakeLockSentinel>`. `release()` on the sentinel returns a `Promise<undefined>` and fires a `release` event on the sentinel when the lock is gone, whoever released it. Both interfaces are `[SecureContext]` and exposed on `Window` only: `navigator.wakeLock` is `undefined` on `http://` origins other than `localhost`, and workers have no wake lock at all.

## Parameters

`request()` takes one optional argument; the sentinel it resolves with exposes three members and one event.

| Name | Type | Required | Description |
|---|---|---|---|
| `type` | `WakeLockType` (enum) | No, defaults to `"screen"` | The only value the specification defines is `"screen"`. A `"system"` type (keep the CPU awake with the screen off) was discussed and dropped; passing it is a WebIDL enum violation. |

| `WakeLockSentinel` member | Type | Description |
|---|---|---|
| `released` | `boolean` (read-only) | `false` while the lock is held; `true` after `release()` resolves or after the browser released it. Chrome 87 added the attribute (Chrome 84 to 86 shipped the sentinel without it). |
| `type` | `WakeLockType` (read-only) | Echoes the requested type, `"screen"`. |
| `release()` | `Promise<undefined>` | Removes this sentinel from the document's active locks and releases the platform lock when no other sentinel of the same type remains. |
| `release` event | `Event` | Fired once per sentinel when it is released, including releases the browser performs on page hide, document unload, or OS intervention. |

Several sentinels can be live at once; the platform lock is released when the last one is. The browser also releases every sentinel when `document.visibilityState` becomes `"hidden"` and does not restore it when the page becomes visible again.

## Exceptions

`request()` rejects with a `DOMException` or a `TypeError`. Every rejection the specification defines carries the same name.

| Exception | Condition |
|---|---|
| `NotAllowedError` | The document is not fully active; or the `screen-wake-lock` Permissions Policy denies the document (default allowlist `'self'`); or the browser denies this lock type for the document; or `document.visibilityState` is `"hidden"` at call time or by the time the permission check finishes; or the `screen-wake-lock` permission resolves to `"denied"`. |
| `TypeError` | `type` is not a member of the `WakeLockType` enum (WebIDL conversion fails before the algorithm starts). |

A low battery or a power-saving mode is a reason the specification lets the browser deny the lock (section 12, Security considerations), and in Chromium that denial surfaces as `NotAllowedError`. Failure further down, at the operating-system level, is deliberately hidden: the specification notes that a lock the OS refused is indistinguishable from an acquired one, so the promise still resolves and `released` stays `false`. `release()` does not reject; calling it on a sentinel that is already released resolves immediately.

:::observed
Chrome rejects `navigator.wakeLock.request("screen")` from a background tab with `NotAllowedError: The requesting page is not visible`, from a document under `Permissions-Policy: screen-wake-lock=()` with `NotAllowedError: Access to Screen Wake Lock features is disallowed by permissions policy`, and `navigator.wakeLock.request("system")` with `TypeError: Failed to execute 'request' on 'WakeLock': The provided value 'system' is not a valid enum value of type WakeLockType.` The three strings are in Chromium's [`wake_lock.cc`](https://chromium.googlesource.com/chromium/src/+/main/third_party/blink/renderer/modules/wake_lock/wake_lock.cc) (chromium.googlesource.com).
:::

## Examples

Both examples feature-detect `navigator.wakeLock` and run a fallback when it is missing (Firefox before 126, Safari before 16.4, Android WebView builds that strip the permission).

### Holding the screen during a workout and re-acquiring after a tab switch

The lock is lost whenever the page is hidden, so the `visibilitychange` listener requests it again when the page returns and the workout is still running. When the API is absent the app shows a one-line hint instead of silently letting the screen time out.

```js
let sentinel = null;
let workoutRunning = false;

async function keepScreenOn() {
  if (!('wakeLock' in navigator)) {
    document.querySelector('#hint').textContent =
      'Raise your screen timeout in system settings; this browser has no wake lock.';
    return;
  }
  try {
    sentinel = await navigator.wakeLock.request('screen');
    sentinel.addEventListener('release', () => {
      sentinel = null;
    });
  } catch (err) {
    console.warn(`${err.name}: ${err.message}`);
  }
}

document.querySelector('#start').addEventListener('click', async () => {
  workoutRunning = true;
  await keepScreenOn();
});

document.querySelector('#stop').addEventListener('click', async () => {
  workoutRunning = false;
  await sentinel?.release();
});

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible' && workoutRunning && !sentinel) {
    keepScreenOn();
  }
});
```

`sentinel?.release()` is safe after the browser already released the lock: the `release` listener has set `sentinel` to `null`, so the optional chain skips the call.

### Binding a checkbox to the real lock state

A UI control that claims the screen is being kept awake has to follow the sentinel, not the click, because the browser can release the lock without the user touching the checkbox. Reading `released` in the `release` handler keeps the two in step.

```js
const box = document.querySelector('#keep-awake');
let sentinel = null;

box.disabled = !('wakeLock' in navigator);

box.addEventListener('change', async () => {
  if (box.checked) {
    try {
      sentinel = await navigator.wakeLock.request('screen');
      sentinel.addEventListener('release', () => {
        box.checked = !sentinel.released; // false once the browser lets go
      });
    } catch (err) {
      box.checked = false; // rejected: hidden page, policy, or denial
    }
  } else {
    await sentinel?.release();
  }
});
```

Disabling the checkbox when `navigator.wakeLock` is missing tells the user up front that the option is unavailable here rather than failing on the first toggle.

## See also

- [Media Session API](/reference/capabilities/media-session/), the companion for playback screens that also need lock-screen controls
- [Idle Detection API](/reference/capabilities/idle-detection/), for reacting to the user walking away instead of preventing the screen from locking
- [PWAs on iOS and Safari](/reference/platforms/ios-safari/), where the Home Screen web-app caveat lives
- [Screen Wake Lock API: request() method](https://www.w3.org/TR/screen-wake-lock/#the-request-method) (w3.org)
- [Screen Wake Lock API: Security considerations](https://www.w3.org/TR/screen-wake-lock/#security-considerations) (w3.org)
- [WebKit bug 254545: Screen Wake Lock in Home Screen web apps](https://webkit.org/b/254545) (webkit.org)
- [Stay awake with the Screen Wake Lock API](https://developer.chrome.com/docs/capabilities/web-apis/wake-lock) (developer.chrome.com)