Raw mouse input lands in the Pointer Lock API

Desktop operating systems apply mouse acceleration by default: the faster you move the physical mouse, the further the on-screen pointer travels for the same distance. That's convenient for general pointer control, but it's a problem for first-person shooters and other pointer-locked games, where the camera rotation should map one-to-one to physical mouse movement. In those titles, competitive players expect fast and slow movements of the same distance to produce the same rotation.

Starting with Chrome 88, web apps can opt out of that OS-level adjustment. The updated Pointer Lock API lets a page request unadjusted mouse movement data, which gaming platforms such as Google Stadia and Nvidia GeForce Now have already started using.

Requesting a pointer lock

Pointer lock is the mechanism that hides the cursor and delivers mouse motion events to a specific element, which is essential for immersive games where the pointer would otherwise leave the page. The movementX and movementY properties on mousemove events report how far the pointer has moved since the previous event, but those values are only meaningful while the pointer stays inside the document.

// Request pointer lock on a canvas element
const canvas = document.querySelector('canvas');
canvas.requestPointerLock();

// Listen for successful lock or errors
document.addEventListener('pointerlockchange', () => {
  if (document.pointerLockElement === canvas) {
    console.log('Pointer locked');
  }
});
document.addEventListener('pointerlockerror', () => {
  console.error('Pointer lock failed');
});

Calling requestPointerLock() on a target element initiates the capture; watch the pointerlockchange and pointerlockerror events to track its state.

Opting out of acceleration

To get raw, unaccelerated data, pass an options object to requestPointerLock():

// Disable OS-level mouse acceleration
const promise = canvas.requestPointerLock({ unadjustedMovement: true });

promise.then(() => {
  console.log('Mouse acceleration disabled');
}).catch(() => {
  console.error('Failed to disable mouse acceleration');
});

When this succeeds, the movementX and movementY values in subsequent mousemove events reflect raw physical movement rather than acceleration-adjusted deltas.

The new promise returned by requestPointerLock() also makes it possible to switch between accelerated and raw modes without releasing the lock. If a mode change request fails, the original lock remains active and the promise rejects; no pointer lock events fire for the failed request.

Support and limitations

The Pointer Lock API itself is widely supported across browsers, but disabling OS-level acceleration is more restricted. As of October 2020, only Chromium-based browsers such as Chrome and Edge offer the unadjustedMovement option. On the operating system side, the facility works on ChromeOS, Windows, and macOS Catalina 10.15.1 and later; Linux support is planned.

For developers who want to explore the behavior, a sample is available on CodePen, and the feature's design rationale is documented in the explainer. The specification PR, the ChromeStatus entry, and the tracking bug track ongoing work.