Skip to content

Solution for: "Sticky" Controls #2577

@speculees

Description

@speculees

Support for element focus:

In interactive 3D applications, if a user holds down a key (e.g., the 'W' key to move forward) and then switches focus to another application (e.g., clicks on a different browser tab, a desktop application, or the operating system taskbar/dock), the browser dispatches a blur event to the application's window (or target DOM element), but it does not necessarily fire a corresponding keyup event for the key that was held down.

This results in the KeyboardControls store keeping the state for that key as true indefinitely. When the user refocuses the application, the useFrame loop still reads the key as being pressed, and the corresponding action (e.g., movement) continues without user input—a "sticky" or "stuck" control issue.

Suggested implementation:

Add a new useEffect block for resetting State on Blur

The added useEffect block specifically listens for the blur event on the target element (window by default, or a specified domElement).
When the blur event occurs (indicating the application has lost focus), the listener immediately resets the entire Zustand state for all controls back to their initial false state:

function onBlur() {
  const initialState = map.reduce(
    (prev, cur) => ({ ...prev, [cur.name]: false }),
    {} satisfies KeyboardControlsState
  )
  set(initialState)
}

This ensures that even if a keyup event is missed, all control inputs are guaranteed to be in a released state when the application is not in focus, preventing the "sticky" behavior when focus is regained.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions