controls reference
Read controls by position, independent of the letters painted on the player's controller. Import from @controller-plus/sdk/controls, the package root, or /sdk/src/controls.mjs on your controller origin.
Input and constants
ControlState is { axes: number[4], buttons: number[16], motion?: { gyro?: number[3], accel?: number[3], orientation?: number[4] }, custom?: Record<string, number> }. Axes are −1…1; buttons are 0…1. Right/down are positive. custom is a local helper field, not part of normal wire input. Motion vectors are bounded to −100…100 and quaternion components to −1…1 by the JSON protocol; units must match your sensor adapter.
| Index | BUTTONS[index] |
Classic label | Standard label |
|---|---|---|---|
| 0 | face.south |
B | A |
| 1 | face.east |
A | B |
| 2 | face.west |
Y | X |
| 3 | face.north |
X | Y |
| 4 | shoulder.left |
L | L |
| 5 | shoulder.right |
R | R |
| 6 | trigger.left |
L2 | L2 |
| 7 | trigger.right |
R2 | R2 |
| 8 | menu.select |
SELECT | SELECT |
| 9 | menu.start |
START | START |
| 10 | stick.left.press |
Left stick press | Left stick press |
| 11 | stick.right.press |
Right stick press | Right stick press |
| 12 | dpad.up |
Up | Up |
| 13 | dpad.down |
Down | Down |
| 14 | dpad.left |
Left | Left |
| 15 | dpad.right |
Right | Right |
BUTTONS and AXES are frozen arrays. AXES is ['stick.left.x', 'stick.left.y', 'stick.right.x', 'stick.right.y']. CLASSIC_LABELS and STANDARD_LABELS are frozen maps of the four face positions to the letters above. They do not contain the other labels in the table.
Functions
| Full signature | Parameters, defaults and return |
|---|---|
NEUTRAL(): ControlState |
New zero-filled arrays on every call; no motion. |
clamp(value, low = -1, high = 1): number |
Clamp a number inclusively. Does not validate finite numbers; NaN remains NaN. |
deadzone(value, threshold = 0.12): number |
Return zero inside the threshold; rescale the remaining range to full travel. Supply a finite threshold below 1. Does not clamp or validate. |
controlValue(input, id): number |
Find a positional button/axis, then input.custom[id]; missing values return zero. Requires an input object. |
movement(input): [number, number] |
Add left stick and D-pad, clamping each axis to −1…1. Does not normalize diagonal length. |
fromGamepad(gamepad, threshold = 0.12): ControlState |
Read four axes and sixteen .buttons[i].value values from a browser Gamepad-like object; clamp and apply the axis deadzone. Missing entries become zero. |
quantize(input): ControlState |
Round supplied axes/buttons to three decimals and clone optional motion. Does not validate length, range or finite values; does not copy custom. |
These are pure helpers with no events or network traffic. They do not throw custom errors; invalid argument shapes can cause ordinary JavaScript errors. Use protocol validation at a trust boundary.
Read input
import { NEUTRAL, BUTTONS, AXES, CLASSIC_LABELS, STANDARD_LABELS,
movement, controlValue, clamp, deadzone, fromGamepad, quantize
} from '@controller-plus/sdk/controls';
const input = NEUTRAL();
input.buttons[15] = 1;
input.buttons[0] = 1;
console.log(movement(input)); // [1, 0]
console.log(controlValue(input, 'face.south')); // 1
console.log(BUTTONS[0], AXES[0], CLASSIC_LABELS, STANDARD_LABELS);
console.log(clamp(2), deadzone(0.05)); // 1, 0
const pad = fromGamepad({ axes: [0.5], buttons: [{ value: 1 }] });
console.log(quantize(pad));
Keep input per playerId and sample it in the simulation loop. See Read and map input for edge-triggered actions, disconnects, physical controllers and motion.