Pointer Lock API

Master mouse lock for immersive web applications. Learn to implement first-person controls, 3D interactions, and delta-based input in modern browsers.

What is Pointer Lock?

Pointer Lock is a browser API that allows web applications to take exclusive control of mouse input. When activated, the system cursor is hidden, and all mouse movement data is delivered as relative delta values rather than absolute screen coordinates.

Key characteristics:

  • System cursor disappears entirely
  • Mouse movement delivered as delta values (movementX, movementY)
  • No screen boundary constraints
  • All mouse events target the locked element
  • Persists until explicitly exited or user triggers unlock gesture

Pointer Lock has transformed what's possible in web-based experiences. First-person shooter games can now deliver camera controls that rival native applications, providing the precise, continuous mouse tracking that competitive gaming demands. 3D modeling and visualization tools benefit similarly--designers can manipulate objects in any direction without cursor boundary interruptions. Interactive kiosks and digital signage applications use pointer lock to create polished, app-like experiences that hide the distracting system cursor. For teams building immersive web experiences, pointer lock represents an essential capability for creating applications that feel as responsive as desktop software.

This API is essential for any web application requiring sustained, cursor-free mouse interaction. By removing positional constraints and delivering raw movement data, pointer lock enables experiences that were previously impossible in browser contexts.

Core API Methods

The Pointer Lock API provides three primary methods for controlling lock state. requestPointerLock() initiates lock on an element, exitPointerLock() releases it at the document level, and pointerLockElement provides read-only state access. These methods form the foundation for implementing mouse lock in any web application.

requestPointerLock()

The primary method for initiating pointer lock, extending the Element interface. Returns a Promise that resolves when lock is acquired.

Key aspects:

  • Must be called within a user activation context (click event handler)
  • Returns Promise for asynchronous handling and state transitions
  • Call on the element that should receive mouse events (typically a canvas)
  • Can include options object for raw input configuration

This asynchronous design allows applications to prepare their interface before lock takes effect, such as showing loading states or adjusting game UI elements. The user activation requirement prevents malicious scripts from trapping users in unresponsive pages, as browsers block programmatic locking without user gesture.

Requesting Pointer Lock
1const canvas = document.querySelector('#gameCanvas');2 3// Basic request4canvas.requestPointerLock();5 6// With options for raw input7canvas.requestPointerLock({8 unadjustedMovement: true9});
Exiting Pointer Lock
1document.exitPointerLock();2 3// The cursor reappears at its original position4// Mouse events resume normal behavior

exitPointerLock()

Released from the Document interface to exit pointer lock. Works regardless of which element originally requested the lock.

What happens on exit:

  • The cursor reappears at its original position
  • Mouse events resume normal position-based behavior
  • All mouse events return to standard targeting
  • Applications should listen for pointerlockchange to detect this transition

This document-level scope means any part of your application can trigger exit, and the system handles cleanup consistently. Users can also exit via the Escape key by default, providing a universal escape path.

Permission-Based Access (Chrome 131+)

Starting with Chrome 131, using the Pointer Lock API requires explicit user permission. The browser prompts users for permission on the first lock request, and subsequent requests check the permission state through the Permissions API.

Handling the three states:

  • granted: Pointer Lock is available, proceed with request
  • prompt: Browser will show permission dialog on request
  • denied: Permission was denied, provide alternative interactions

This permission model improves user control while maintaining the API's utility for legitimate use cases. Applications should implement graceful handling for all three states and provide clear feedback when pointer lock is unavailable. Consider showing a "Click to enable mouse control" button that triggers the permission prompt rather than automatically requesting on every interaction.

Checking and Requesting Pointer Lock Permission
1// Check current permission state2const { state } = await navigator.permissions.query({ name: 'pointer-lock' });3 4if (state === 'granted') {5 // Pointer Lock is available6 canvas.requestPointerLock();7} else if (state === 'prompt') {8 // Will show permission prompt on request9 canvas.requestPointerLock();10} else {11 // Permission denied12 console.log('Pointer Lock permission was denied');13}

Understanding Mouse Movement Data

When pointer lock is active, mouse events include movementX and movementY properties representing delta values since the last event.

Reading Movement Deltas
1document.addEventListener('mousemove', (event) => {2 console.log(`Moved: ${event.movementX}px horizontally, ${event.movementY}px vertically`);3});

movementX and movementY

These values represent the change in pointer position since the last mouse move event, measured in pixels.

Key points:

  • Read-only properties returning double values
  • Accumulate across events for total distance tracking
  • Return zero when pointer lock is not active (except mousemove/pointermove)
  • Zero when pointer is clipped by browser boundaries

Practical applications:

  • Camera rotation: Accumulate movementX for yaw (horizontal) and movementY for pitch (vertical) rotation in first-person views
  • Object manipulation: Apply movement deltas to translate or rotate 3D objects in modeling applications
  • Drawing tools: Use deltas for brush strokes that follow mouse travel regardless of screen boundaries
  • Game character control: Integrate movement values for character facing direction or aiming

The delta-based approach means applications must track their own internal cursor or camera position by summing movement values over time, but this provides the continuous, boundary-free input that immersive experiences require.

Advanced Configuration: unadjustedMovement

The unadjustedMovement option requests raw mouse input without platform-level mouse acceleration. Essential for gaming applications requiring consistent sensitivity.

What is mouse acceleration? Operating systems apply mouse acceleration by default, making slow movements more precise while allowing fast movements to cover greater distances. This works well for general desktop use but creates inconsistency in gaming scenarios where 1:1 mapping between physical movement and on-screen response matters.

When raw input matters:

  • Competitive gaming where consistent muscle memory is critical
  • Professional 3D modeling requiring predictable control
  • Precision design work like vector graphics or CAD applications
  • Any application where user develops spatial muscle memory

Platform support and fallback: Not all platforms support this option. The Promise rejects with NotSupportedError if the underlying system cannot fulfill the request. Implement fallback logic that catches this error and retries without the option or provides adjusted sensitivity controls.

Using unadjustedMovement
1canvas.requestPointerLock({2 unadjustedMovement: true3});4 5// Promise rejects with NotSupportedError if platform doesn't support it6// Implement fallback logic for unsupported platforms

Event Handling and State Management

Proper event handling is crucial for responsive pointer lock implementations.

pointerlockchange Event

Fires on Document whenever lock state changes. No additional data--check pointerLockElement to determine current state.

When it fires:

  • Lock is successfully acquired
  • Lock is released (via exitPointerLock(), user gesture, or browser policy)

Using it for state consistency: Event handlers should be idempotent--able to run multiple times without causing issues. Maintain a boolean state flag that tracks transitions between locked and unlocked states. Update UI elements (showing "Click to Play" when unlocked, hiding cursors when locked) based on these transitions to keep interface state synchronized with actual lock state.

Handling pointerlockchange
1document.addEventListener('pointerlockchange', () => {2 if (document.pointerLockElement === canvas) {3 console.log('Pointer lock active');4 updateGameState('playing');5 } else {6 console.log('Pointer lock released');7 updateGameState('paused');8 }9});
Handling pointerlockerror
1document.addEventListener('pointerlockerror', (event) => {2 console.error('Pointer lock error occurred');3 handleLockFailure();4});5 6// Common causes:7// - Permission denials8// - Missing user activation9// - Sandbox restrictions10// - Conflicts with other locks

pointerlockerror Event

Indicates lock request failure. No error details included--applications must infer causes from context.

Common causes and strategies:

  • Permission denials: User previously denied permission; provide UI to re-prompt or offer alternative interactions
  • Missing user activation: Request outside click handler; restructure code to call within user gesture
  • Sandbox restrictions: iframe or restricted context; consider top-level deployment
  • Conflicts with other locks: Another element has lock; exit that lock first

User feedback approaches: Provide clear, non-intrusive feedback when lock fails. Show a "Click to enable mouse control" button that guides users through proper activation rather than displaying error messages. For permission denials, explain why pointer lock is needed and offer alternative input methods when possible.

Practical Implementation: First-Person Camera

A complete example demonstrating how all components work together in a first-person camera controller.

First-Person Camera Controller Implementation
1class FirstPersonCamera {2 constructor(canvas) {3 this.canvas = canvas;4 this.pitch = 0;5 this.yaw = 0;6 this.sensitivity = 0.1;7 8 this.setupEventListeners();9 }10 11 setupEventListeners() {12 this.canvas.addEventListener('click', async () => {13 try {14 await this.canvas.requestPointerLock();15 } catch (error) {16 console.error('Failed to acquire pointer lock:', error);17 }18 });19 20 document.addEventListener('pointerlockchange', () => {21 this.onLockStateChange();22 });23 24 document.addEventListener('mousemove', (event) => {25 if (document.pointerLockElement === this.canvas) {26 this.updateRotation(event.movementX, event.movementY);27 }28 });29 }30 31 updateRotation(deltaX, deltaY) {32 this.yaw += deltaX * this.sensitivity;33 this.pitch -= deltaY * this.sensitivity;34 this.pitch = Math.max(-90, Math.min(90, this.pitch));35 this.render();36 }37 38 onLockStateChange() {39 const isLocked = document.pointerLockElement === this.canvas;40 this.updateInterface(isLocked);41 }42 43 updateInterface(locked) {44 document.body.classList.toggle('pointer-locked', locked);45 }46 47 render() {48 console.log(`Camera: yaw=${this.yaw.toFixed(1)}°, pitch=${this.pitch.toFixed(1)}°`);49 }50}

Browser Compatibility and Platform Considerations

Current Support: Chrome, Firefox, Safari, and Edge all implement the core Pointer Lock API.

Testing requirements:

  • Test on all target browsers, including mobile browsers
  • Verify unadjustedMovement behavior on each platform
  • Check iframe implementations across different embedding contexts

Fallback strategies:

  • Detect capability via feature detection
  • Provide alternative interactions when pointer lock unavailable
  • Gracefully degrade to standard mouse events for non-critical features
  • For mobile, implement touch-based alternatives that provide similar functionality

iframe considerations: Cross-origin iframes need the allow="pointer-lock" attribute on the iframe element for lock requests to succeed. Some browsers apply additional security policies to iframe-originated lock requests, so test thoroughly across target browsers. When possible, host critical pointer lock functionality at the top level to avoid iframe restrictions.

Platform behavior differences: Mouse acceleration and raw input behavior vary across Windows, macOS, and Linux. The unadjustedMovement option aims to normalize this, but perfect consistency remains challenging. Provide sensitivity adjustment options that account for platform differences and allow users to calibrate control to their preferences.

Security and Privacy

Security model: Pointer lock requires user activation (typically a click) to prevent malicious scripts from silently taking control of mouse input. This design protects users from pages that might otherwise trap them in unresponsive experiences. Chrome's additional permission requirement (starting version 131) adds another layer of user control.

User protection mechanisms:

  • Transient activation requirements mean users must recently interact with the page
  • Visibility checks ensure locked pages cannot maintain lock when backgrounded
  • Users can manage permissions globally through browser settings
  • Sandbox restrictions limit lock capability in restricted contexts

Accessibility considerations: Pointer lock creates challenges for users relying on assistive technologies. Screen readers and magnification software may behave unexpectedly when the visible cursor disappears. Design for accessibility by providing alternative input methods, clear instructions for exiting lock mode, and ensuring keyboard navigation remains functional. Our web development best practices emphasize inclusive design that works for all users.

Best practices for accessibility:

  • Remind users of the ESC unlock gesture with visible indicators
  • Add a "Press ESC to exit" message during active lock
  • Provide keyboard-only alternatives for mouse-dependent actions
  • Consider adding a visible crosshair or cursor replacement for users who need visual feedback
  • Test with screen readers and provide appropriate ARIA live regions for state changes

The default unlock gesture--pressing Escape--provides a universal escape path. Applications should remind users of this gesture, especially in immersive experiences where the escape key might be used for other purposes.

Performance Best Practices

Efficient event handling: Pointer lock can generate high-frequency mouse events that overwhelm handlers if not properly optimized. Keep event handlers minimal--delegate heavy processing to requestAnimationFrame-scheduled updates rather than processing directly in callbacks.

Batching strategies: Accumulate movement deltas in variables and process them in your game loop or animation frame. This ensures consistent frame-rate behavior regardless of mouse polling frequency and prevents processing overhead from affecting event delivery.

Memory management: For long-running applications, periodically check for memory leaks in event handler closures and accumulated state. The high-frequency nature of mouse events makes even small inefficiencies significant over time.

Optimization techniques:

  • Use movementX/movementY values immediately and discard them to prevent memory accumulation
  • Batch state updates and render in requestAnimationFrame callbacks
  • Clean up event listeners when components unmount or features deactivate
  • Exit lock on pagehide/visibilitychange handlers to ensure clean state restoration

Common pitfalls to avoid:

  • Processing heavy computations directly in mousemove handlers
  • Accumulating unbounded arrays instead of scalar values
  • Forgetting to clean up listeners on component unmount
  • Not handling visibility changes that may interrupt input
Efficient Event Batching
1let pendingMovementX = 0;2let pendingMovementY = 0;3 4document.addEventListener('mousemove', (event) => {5 pendingMovementX += event.movementX;6 pendingMovementY += event.movementY;7});8 9function gameLoop() {10 if (pendingMovementX !== 0 || pendingMovementY !== 0) {11 updatePlayerPosition(pendingMovementX, pendingMovementY);12 pendingMovementX = 0;13 pendingMovementY = 0;14 }15 requestAnimationFrame(gameLoop);16}

Common Use Cases

First-Person Games: Pointer lock enables precise camera control essential for FPS perspectives. Mouse movement directly controls view direction without cursor boundaries, allowing continuous rotation in any direction. This provides the responsive, boundary-free aiming that competitive gaming requires. Implementation tip: Apply sensitivity scaling to movement deltas and clamp vertical rotation to prevent camera flipping.

3D Modeling and Visualization: Professional 3D tools require precise object manipulation in all directions. Pointer lock's delta-based input provides the consistent control these applications need, and the infinite movement range prevents interruptions during detailed work. Implementation tip: Combine pointer lock with keyboard modifiers for different transformation modes (rotate, pan, scale).

Interactive Exhibits and Kiosks: Public-facing displays benefit from hiding the cursor, which can distract from content or reveal system UI elements. Pointer lock creates polished, app-like experiences in web-based kiosk interfaces. Implementation tip: Reacquire lock on kiosk idle timeout to maintain immersive state during unattended operation. Our team specializes in building custom web applications that leverage advanced browser APIs for unique client needs.

Design Tools: Vector graphics editors, image manipulation tools, and similar applications benefit from continuous cursor-free input. Zooming, panning, and object manipulation feel more direct when the system cursor doesn't interfere. Implementation tip: Use delta magnitude for brush stroke intensity or tool sensitivity.

Summary

The Pointer Lock API enables sophisticated interactive applications by transforming mouse input from positional to delta-based delivery. Understanding this API opens possibilities for web experiences that rival native applications.

Key takeaways:

  1. Core methods: requestPointerLock(), exitPointerLock(), and pointerLockElement form the foundation
  2. Movement data: movementX and movementY provide delta values for continuous tracking
  3. Permission model: Chrome 131+ requires explicit user permission through the Permissions API
  4. Raw input: unadjustedMovement option provides mouse input without system acceleration
  5. Events: pointerlockchange and pointerlockerror enable robust state management
  6. Security: User activation requirements and accessibility considerations ensure safe implementation
  7. Performance: Event batching and proper resource cleanup maintain smooth experiences

Getting started recommendations: Begin with a simple first-person camera implementation to understand the core concepts. Add unadjustedMovement support once basic functionality works. Implement proper error handling and fallback strategies before expanding to complex use cases. Test across target browsers early in development to identify compatibility issues.

For teams building interactive web applications, pointer lock represents an essential capability that bridges the gap between web and native experiences. Our web development services can help you implement this and other advanced browser APIs for your next project.

Frequently Asked Questions

Ready to Build Immersive Web Experiences?

Our team of web development experts can help you implement advanced APIs like Pointer Lock for cutting-edge web applications.