Skip to content

Input Bindings

An InputBinding maps an action to a physical input. There are three binding types: button, axis, and dualAxis.

Discrete on/off. Tracks pressed, justPressed, justReleased.

// Keyboard key (uses KeyboardEvent.code values)
{ type: 'button', source: { kind: 'key', code: 'Space' } }
// Mouse button
{ type: 'button', source: { kind: 'pointerButton', button: 'left' } }
// Chord — all keys must be held simultaneously
{ type: 'button', source: { kind: 'chord', inputs: [
{ kind: 'key', code: 'ControlLeft' },
{ kind: 'key', code: 'KeyZ' },
] } }

Chord disambiguation is built-in: when a chord is active, its constituent keys won’t fire standalone actions.

1D value, typically -1 to 1.

// Key pair — negative/positive
{ type: 'axis', source: { kind: 'keyAxis', negative: 'KeyA', positive: 'KeyD' } }
// Mouse axis — raw value
{ type: 'axis', source: { kind: 'mouseAxis', axis: 'movementX' } }

mouseAxis supports 'x', 'y', 'movementX', and 'movementY'.

2D value { x, y }. The value field returns the clamped vector length.

// Composed from two axis inputs
{ type: 'dualAxis', source: {
kind: 'dualAxis',
x: { kind: 'keyAxis', negative: 'KeyA', positive: 'KeyD' },
y: { kind: 'keyAxis', negative: 'KeyS', positive: 'KeyW' },
} }
// Pointer position (absolute)
pointerPosition()
// Pointer movement (delta)
{ type: 'dualAxis', source: { kind: 'pointerMovement' } }

wasd() and arrowKeys() return ready-made DualAxisInput objects:

import { wasd, arrowKeys } from '@shell/input-manager';
map.bind('move', { type: 'dualAxis', source: wasd() });
map.bind('move', { type: 'dualAxis', source: arrowKeys() });

There are also helpers that return a complete DualAxisBinding:

import { dualAxisWASD, dualAxisArrowKeys, key } from '@shell/input-manager';
map.bind('move', dualAxisWASD()); // same as { type: 'dualAxis', source: wasd() }
map.bind('attack', key('KeyF')); // same as { type: 'button', source: { kind: 'key', code: 'KeyF' } }

InputManagerPlugin automatically polls the browser Gamepad API. The helpers use standard-layout button and axis indexes:

import {
GamepadButton,
GamepadStick,
dualAxisWASD,
gamepadButton,
gamepadStick,
gamepadTrigger,
key,
} from '@shell/input-manager';
map.bind('move', dualAxisWASD(), gamepadStick(GamepadStick.Left));
map.bind('jump', key('Space'), gamepadButton(GamepadButton.South));
map.bind('throttle', gamepadTrigger(GamepadButton.RightTrigger));

Browsers may not expose a connected gamepad until the user presses a button while the page is focused.

Bindings without a gamepad option accept input from any connected controller. When multiple controllers are active, the strongest stick, axis, or trigger value wins. Pass { gamepad: 1 } to select an exact controller for local multiplayer. Sticks use a radial deadzone of 0.15 and invert Y to match the positive-up keyboard presets; both can be overridden:

gamepadStick(GamepadStick.Right, { gamepad: 1, deadzone: 0.2, invertY: false });

When multiple axis bindings target the same action, the input with the strongest magnitude wins.

TypeDescription
ButtonInputKeyboard, pointer, chord, or gamepad button input
AxisInputKey, mouse, gamepad axis, or analog gamepad button input
DualAxisInputComposed axis, pointer, or gamepad stick input
InputBindingButtonBinding | AxisBinding | DualAxisBinding
FunctionReturnsDescription
key(code)ButtonBindingShortcut for a key button binding
dualAxisWASD()DualAxisBindingShortcut for WASD dual-axis binding
dualAxisArrowKeys()DualAxisBindingShortcut for arrow key dual-axis binding
wasd()DualAxisInputWASD dual-axis input source
arrowKeys()DualAxisInputArrow key dual-axis input source
gamepadButton()ButtonBindingStandard gamepad button
gamepadTrigger()AxisBindingAnalog value of a gamepad button
gamepadAxis()AxisBindingIndividual gamepad axis
gamepadStick()DualAxisBindingLeft or right gamepad stick
pointerPosition()DualAxisBindingAbsolute viewport pointer position

Bindings are combined by default. Enable sourceSelection: 'last-active' when keyboard/mouse and gamepad bindings should act as mutually exclusive control schemes:

import {
GamepadStick,
InputManagerPlugin,
InputMapData,
InputSourceId,
gamepadStick,
pointerPosition,
} from '@shell/input-manager';
world.addPlugin(
InputManagerPlugin.config({
sourceSelection: 'last-active',
initialActiveSource: InputSourceId.Dom,
globalMap: new InputMapData().bind('look', pointerPosition(), gamepadStick(GamepadStick.Right)),
}),
);

Fresh keyboard or pointer activity selects InputSourceId.Dom. A new gamepad button press or axis crossing gamepadActivityThreshold (default 0.25) selects InputSourceId.Gamepad. Held inputs do not repeatedly reclaim control, and simultaneous activity keeps the current source.

Use resources.get('inputSources').activeSourceId to select input prompts or interpret bindings such as absolute pointer coordinates versus a directional stick vector.