JavaScript Idle Detection Table

PieceWhat it doesField note
IdleDetector.requestPermission()Explicit askSeparate permission - 'allow idle detection' prompt
IdleDetector.start({threshold})Begin watchingthreshold: 60s minimum - granularity is user privacy
detector.userStateactive / idleThe USER is idle - distinct from the SCREEN being idle
detector.screenStatelocked / unlockedScreen lock is a separate signal - pause on lock too
onchangeThe transition eventBoth states fire one event - branch on both fields
Chromium-onlyThe support gateFirefox/Safari no - feature-detect and degrade to timers
Privacy postureWhy so gatedKnowing human presence is surveillance-adjacent - hence permission
Legit usesStatus appsChat availability, draft auto-save, kiosk session resets - NOT tracking
Reference: the MDN IdleDetector reference. Idle Detection answers 'is the human still here': requestPermission (a dedicated prompt), then start({threshold: 60_000}) - and the detector reports userState (active/idle) and screenState (locked/unlocked) with an onchange per transition. The API is Chromium-only and deliberately gated: knowing human presence is surveillance-adjacent, hence the separate permission and the 60-second minimum threshold. Bottom line: chat availability, draft auto-save and kiosk session resets are the honest uses - auto-draft-save being the killer app: save 'before' the user walks away and loses the tab. Related tools: permissions table (the state-reading family), localStorage table (the auto-save target), and wake lock table (the screen-side sibling).

Idle Detection answers 'is the human still here': requestPermission (a dedicated prompt), then start({threshold: 60_000}) - and the detector reports userState (active/idle) and screenState (locked/unlocked) with one onchange per transition.

Bottom line: the API is Chromium-only and deliberately gated - knowing human presence is surveillance-adjacent, hence the separate permission, the 60-second minimum threshold, and active-tab requirements. The honest uses: chat availability, kiosk session resets, and draft auto-save ('save before the user walks away and loses the tab').

The honest part: idle detection is NOT activity tracking - the states are coarse (active/idle at a minute granularity), the user granted a named permission, and the design intent is presence for the CURRENT app, not behavior history.

How to use

  1. Gate and start: const ctrl = new AbortController(); await IdleDetector.requestPermission(); await detector.start({ threshold: 60_000, signal: ctrl.signal }).
  2. Branch on both fields: detector.onchange = () => { if (detector.userState === 'idle') markAway(); if (detector.screenState === 'locked') pause(); } - user and screen idle independently.
  3. Auto-save on away: on idle, flush the draft to localStorage - the user who walked away mid-sentence comes back to everything.

Frequently asked questions

Why does Idle Detection have its own permission and a 60-second floor?

Because presence is sensitive. Knowing a human is at the machine (versus merely a tab being open) enables status surveillance, engagement pressure and behavior profiling - so the API demands a DEDICATED permission prompt (not bundled with any other grant), enforces a minimum threshold of 60 seconds (no fine-grained 'they stopped moving 3 seconds ago'), and only reports while the page is visible and focused. Each gate answers a specific abuse: the prompt is informed consent, the floor is coarse granularity, the visibility rule scopes it to the app the user is actually using. An idle API without these would be a keylogger's dream.

What is the difference between userState and screenState?

Two independent signals. userState (active/idle) is about the HUMAN: keyboard, mouse, direct interaction with the window. screenState (locked/unlocked) is about the MACHINE: the session may be unlocked while the human is idle (reading a book next to the PC), or locked while they just stepped out. Features branch differently: chat availability keys on userState (reading, not typing, still means present), while auto-save and security-sensitive pauses key on screenState (a locked session means the user cannot interact at all). The onchange fires for either transition - the handler must branch on both fields rather than assuming which changed.

Why is auto-saving drafts the killer use for idle detection?

Because it fixes the worst data-loss story at the exact right moment. Users walk away mid-sentence, the tab gets closed by accident or memory pressure, and hours of writing vanish. Idle detection says precisely 'the user stopped interacting a minute ago' - the moment to flush the draft to localStorage or the server WITHOUT interrupting an active typewriter (saving mid-typing is fine, but idle is the unambiguous 'they are not here to see a flicker'). The pattern: on idle, save; on active again, notify 'we saved your draft while you were away'. A presence signal used for preservation, not surveillance - the API's best-natured application.

How should a feature handle the Chromium-only reality?

Feature-detect and degrade to the old tools. if (!('IdleDetector' in window)) - fall back to the classic heuristics: debounced input/mousemove listeners for in-page idleness (they cannot see 'user left entirely', but they cover in-page idle), or plain timers. The graceful degradation for chat availability is a manual status toggle; for auto-save, the fallback is periodic or on-input saving (which arguably should exist ANYWAY as the base layer - idle detection is the optimization that saves at the perfect moment, not the only save). Never gate the core function on the API: idle detection refines the timing, the persistence exists independently.

Related tools