# Idle Detection API: watching for user and screen inactivity

> How the IdleDetector interface reports whether the user is active or idle and whether the screen is locked or unlocked, its permission model and 60-second minimum threshold, and where it is (and is not) implemented.

**In one line:** The Idle Detection API's `IdleDetector` interface lets a page ask
whether the user has interacted with the screen or device within a given threshold,
and whether the screen is locked, firing a `change` event whenever either state flips.

## Requesting permission

Per MDN, starting an `IdleDetector` requires the `idle-detection` permission, and
`IdleDetector.requestPermission()` requires transient user activation — it must be
called from within a user gesture, such as a click handler:

```js
startButton.addEventListener('click', async () => {
  const permission = await IdleDetector.requestPermission();
  if (permission !== 'granted') {
    console.error('Idle detection permission denied.');
    return;
  }
  await startIdleDetector();
});
```

## Starting the detector

`start()` takes a `threshold` in milliseconds and an optional `AbortSignal`. Per the
WICG specification, `start()` rejects with a `TypeError` when `threshold` is below
60,000ms (60 seconds) — there is no way to ask for finer-grained reporting:

```js
async function startIdleDetector() {
  const controller = new AbortController();
  const idleDetector = new IdleDetector();

  idleDetector.addEventListener('change', () => {
    console.log(`User: ${idleDetector.userState}, Screen: ${idleDetector.screenState}`);
  });

  await idleDetector.start({
    threshold: 60_000,
    signal: controller.signal,
  });
}
```

`userState` reports `"active"` or `"idle"`; `screenState` reports `"locked"` or
`"unlocked"`. Per MDN, both return `null` before `start()` is called, and `start()`
gives them their initial values.

## Where it is supported

Per MDN's compatibility data, `IdleDetector` ships only in Chromium-based browsers,
from Chrome version 94, and requires a secure context. Firefox and Safari do not
implement it.

## Feature detection and fallback

```js
async function watchIdleState(onChange) {
  if (!('IdleDetector' in window)) {
    // Unsupported: there is no OS-level idle/lock state to report here, so
    // just report unknown instead of guessing.
    onChange({ userState: 'unknown', screenState: 'unknown' });
    return;
  }

  const permission = await IdleDetector.requestPermission();
  if (permission !== 'granted') {
    onChange({ userState: 'unknown', screenState: 'unknown' });
    return;
  }

  const idleDetector = new IdleDetector();
  idleDetector.addEventListener('change', () => {
    onChange({ userState: idleDetector.userState, screenState: idleDetector.screenState });
  });
  await idleDetector.start({ threshold: 60_000 });
}
```

## Practical checklist

- [ ] Feature-detect with `'IdleDetector' in window` before referencing the class —
      it does not exist in Firefox or Safari.
- [ ] Call `IdleDetector.requestPermission()` from inside a user gesture; calling it
      without transient user activation fails per spec.
- [ ] Do not pass a `threshold` below 60,000ms — `start()` rejects with a
      `TypeError` instead of just reporting more slowly.
- [ ] Treat `userState` and `screenState` as `null` before `start()` is called;
      do not read them synchronously right after construction.
- [ ] Provide a working fallback experience for Firefox and Safari, which do not
      currently implement this API, rather than degrading silently.

## Where to go next

- [Screen Wake Lock API](/reference/capabilities/wake-lock/) — another
  device-state capability API.
- [PWAs on Firefox](/reference/platforms/firefox/) — more on
  Firefox's platform capability support.