API in Capabilities
Keyboard Lock and Pointer Lock
Published
Keyboard Lock lets a page capture physical-key presses that the operating system makes available, while Pointer Lock hides the cursor and delivers relative pointer movement to one element. Both are intended for focused interfaces such as games and remote-access applications.
MDN marks both Keyboard.lock() and the promise-returning form of requestPointerLock() as limited availability. Keyboard Lock is also restricted to secure contexts, so an interface needs independent checks and a usable unlocked path.
Where it is supported
Section titled “Where it is supported”MDN marks both APIs as limited availability. Keyboard Lock requires a secure context and JavaScript-initiated fullscreen; Pointer Lock support and the promise-returning request form vary across browsers.
Syntax
Section titled “Syntax”await navigator.keyboard.lock(["KeyW", "KeyA", "KeyS", "KeyD"]);navigator.keyboard.unlock();
await element.requestPointerLock();document.exitPointerLock();Calling keyboard.lock() without a list requests all valid key codes that the operating system allows. A later call replaces the earlier set. The Keyboard Lock specification dispatches locked keys while the document is fullscreen. Pointer Lock keeps clientX, clientY, screenX, and screenY constant while movementX and movementY continue to report relative motion.
Parameters
Section titled “Parameters”keyboard.lock(keyCodes) accepts an optional sequence of KeyboardEvent.code values. element.requestPointerLock(options) accepts an optional unadjustedMovement boolean; when it is true, the Pointer Lock specification requests movement without operating-system mouse acceleration.
Exceptions
Section titled “Exceptions”Keyboard Lock’s lock() algorithm requires the active top-level browsing context; it does not check transient user activation. Chrome’s documentation requires JavaScript-initiated fullscreen before Keyboard Lock. The promise rejects with InvalidStateError outside the active top-level browsing context, InvalidAccessError for an invalid code, and AbortError when a later lock request supersedes a pending one.
Pointer Lock requires transient activation unless the document previously released a successful lock through exitPointerLock(). A sandboxed iframe needs the allow-pointer-lock token, and a request for unsupported unadjusted movement rejects with NotSupportedError under the Pointer Lock algorithm.
Examples
Section titled “Examples”The example reports locked controls after Pointer Lock is confirmed and the Keyboard Lock promise fulfills. On-screen controls remain available when either request fails.
Locking controls with an unlocked fallback
Section titled “Locking controls with an unlocked fallback”The first click enters fullscreen. A second click requests Pointer Lock. After pointerlockchange confirms that lock, the page requests Keyboard Lock. If an API is absent, the interface keeps ordinary keyboard and pointer handling and reports that focused controls are unavailable.
<section id="control-surface" tabindex="0" aria-label="Interactive control surface"> <p id="lock-status" role="status">Use the buttons or click this area to start.</p> <output id="position">x: 0, y: 0</output> <div id="screen-controls" aria-label="On-screen controls"> <button type="button" data-dx="0" data-dy="-1">Up</button> <button type="button" data-dx="-1" data-dy="0">Left</button> <button type="button" data-dx="1" data-dy="0">Right</button> <button type="button" data-dx="0" data-dy="1">Down</button> </div></section>
<script type="module">const surface = document.querySelector("#control-surface");const status = document.querySelector("#lock-status");const position = document.querySelector("#position");const controls = document.querySelector("#screen-controls");let x = 0;let y = 0;
function move(dx, dy) { x += dx; y += dy; position.value = `x: ${x}, y: ${y}`;}
controls.addEventListener("click", (event) => { const button = event.target.closest("button[data-dx]"); if (!button) return; move(Number(button.dataset.dx), Number(button.dataset.dy));});
surface.addEventListener("click", async (event) => { if (event.target.closest("#screen-controls")) return; const canLockKeyboard = "keyboard" in navigator && "lock" in navigator.keyboard; const canLockPointer = "requestPointerLock" in surface; const canFullscreen = "requestFullscreen" in surface;
if (!canLockKeyboard || !canLockPointer || !canFullscreen) { status.textContent = "Use the on-screen controls."; return; }
if (document.fullscreenElement !== surface) { try { await surface.requestFullscreen(); status.textContent = "Click the surface again to lock controls."; } catch (error) { status.textContent = `Fullscreen was not entered: ${error.name}`; } return; }
const request = surface.requestPointerLock(); request?.catch((error) => { status.textContent = `Pointer Lock failed: ${error.name}`; });});
document.addEventListener("pointerlockchange", async () => { if (document.pointerLockElement !== surface) { navigator.keyboard?.unlock?.(); status.textContent = "Controls are unlocked."; return; }
try { await navigator.keyboard.lock(["KeyW", "KeyA", "KeyS", "KeyD"]); status.textContent = "Controls locked. Press Escape to leave."; } catch (error) { document.exitPointerLock(); status.textContent = `Keyboard Lock failed: ${error.name}`; }});
document.addEventListener("pointerlockerror", () => { status.textContent = "Pointer Lock failed; use the on-screen controls.";});document.addEventListener("mousemove", (event) => { if (document.pointerLockElement !== surface) return; move(event.movementX, event.movementY);});</script>The event handler waits for confirmed Pointer Lock before reporting success. It releases Pointer Lock if Keyboard Lock fails, releases Keyboard Lock when Pointer Lock ends, and keeps the defined on-screen controls usable when support is absent or a request fails. Relative pointer movement and button presses update the same position output.
Keeping the on-screen fallback available
Section titled “Keeping the on-screen fallback available”The example never hides #screen-controls. Its button handler calls the same move() function as relative pointer movement, so the fallback remains usable before a lock starts and after a request fails.
See also
Section titled “See also”- User activation, for the activation state used by Pointer Lock requests
- Screen Capture API, for sharing a display in remote-access interfaces
- Keyboard Lock (wicg.github.io)
- Pointer Lock 2.0 (w3c.github.io)
Specifications
| Specification | Status |
|---|---|
| Keyboard Lock | WICG draft |
| Pointer Lock 2.0 | W3C draft |