How to avoid PointerLockControls error

Hello!

I want to create a pause menu, but I have this error appearing when the player spam clicks to lock the mouse when locking and unlocking the mouse quickly.
Uncaught (in promise) DOMException: The user has exited the lock before this request was completed.

I was wondering if there was a way or a trick to avoid it as 1) it doesn’t lock the mouse 2) make a huge framerate drop each time it happens.

You can store the time when the pointer lock was activated and then whenever you need to toggle it again you check if it was toggled/activated within the last 100 ms or so.

let pointerLockActivatedAt = null;

function inactivatePointerLock() {
    const now = performance.now()
    if (pointerLockActivatedAt != null && now - pointerLockActivatedAt < 100) {
        return
    }
    // do your thing
}
function activatePointerLock() {
    const now = performance.now()
    pointerLockActivatedAt = now
}
2 Likes

Seems to be a about a 1-second time window in Chrome before pointer can be locked again.

Sorry, no way to avoid it. As per the spec:

A requestPointerLock() call immediately after the default unlock gesture MUST fail even when transient activation is available, to prevent malicious sites from acquiring an unescapable locked state through repeated lock attempts. On the other hand, a requestPointerLock() call immediately after a programmatic lock exit (through a exitPointerLock() call) MUST succeed when transient activation is available, to enable applications to move frequently between interaction modes, possibly through a timer or remote network activity.

In other words, if the lock was exited from code then it can be re-entered immediately. If it was exited by the user pressing the default exit key (usually ESC) then immediate re-entry MUST fail.

Similar to suggested above, I capture the pointerLockError event and set a timer. If there’s an error within a second of exiting, I give the player a message telling them to wait a second. When I first encountered this it wasn’t obvious that it was a timing related issue and not something else. Too bad the default DOMException error isn’t more self-explanatory.

I solve this problem by delaying the display of the button that re-enables pointer lock mode. It’s worked as intended every time I tested it.

controls.addEventListener('unlock', () => {
  ... //other code
  setTimeout(() => {
    startButton.style.display = 'block'
  }, 1000)
})

Working Example : Follow Cam - Three.js Tutorials
image