-
Notifications
You must be signed in to change notification settings - Fork 0
Controllers
Since v0.5.0. steam.controllers is the curated layer over ISteamInput:
controller handles, action sets, action data, haptics, and the device
callbacks. It is called controllers because the generated ISteamInput class
already owns steam.input.
Steam Input does not give you buttons. It maps every controller, whatever the hardware, to the actions named in your app's action manifest, and the user rebinds them in the Steam UI. So the flow is fixed: init once, list for the connected controllers, actionSet and digitalAction / analogAction for the handles at startup, then runFrame plus digital / analog once per frame.
Nothing here is async. Every call reads the local Steam client. The
Controllers instance is created lazily and cached on the Steam object.
Every handle (controller, action set, action) is a 64-bit value, so a bigint;
origins, colors and speeds are plain numbers.
The action-data calls were the last of the generator's skips to be bound. They work since 0.3.0, which is what makes this layer possible at all.
init(explicitRunFrame?: boolean): boolean
shutdown(): booleaninit starts Steam Input and must run before anything else here; shutdown
stops it and releases the controller handles.
explicitRunFrame (default false) decides who updates the action data. Pass
true and the values only change when runFrame is called, which
is what a game loop wants. Pass false and Steam updates on its own schedule.
import { init } from 'steamwand.js';
const steam = init({ appId: 480 });
steam.controllers.init(true);
console.log(steam.controllers.list());
steam.controllers.shutdown();
steam.close();runFrame(): voidRefreshes the action data for every controller. Call it once per frame when
init(true) was used. Without it the values from digital,
analog and motion never change, which is the usual reason
a first Steam Input build reads nothing but zeroes.
list(): bigint[]The input handles of the currently connected controllers, at most 16
(STEAM_INPUT_MAX_COUNT). Empty when none is connected.
type(handle: bigint): numberWhat kind of controller a handle belongs to, as an ESteamInputType: 0
unknown, 2 Xbox 360, 3 Xbox One, 5 PS4, 13 PS5, 14 Steam Deck, and more. Use it
to pick a button-prompt style when you are not using the
glyphs.
actionSet(name: string): bigintThe handle of an action set, by the name in the action manifest. Handles never change while the app runs, so look them up once at startup.
Throws Error: steamwand: no action set named '<name>' in the action manifest
when Steam returns 0n, which means the name is misspelled or the manifest is
not the one Steam loaded.
digitalAction(name: string): bigint
analogAction(name: string): bigintThe handle of a digital (on or off) or analog (stick, pad or trigger) action,
by manifest name. Both throw
Error: steamwand: no <kind> action named '<name>' in the action manifest for
a name Steam does not know.
activateActionSet(handle: bigint, actionSet: bigint): voidMakes one action set the active one for a controller. Only one is active at a time, which is how a game switches between, say, walking and driving. Steam has no result for this, so it cannot fail from JavaScript.
currentActionSet(handle: bigint): bigintThe active action set of a controller, or 0n when none is active.
activateActionSetLayer(handle: bigint, layer: bigint): void
deactivateActionSetLayer(handle: bigint, layer: bigint): void
deactivateAllActionSetLayers(handle: bigint): voidA layer sits on top of the active action set and overrides only the actions it
binds, leaving the rest alone. Layers stack, so activateActionSetLayer may be
called more than once, and the two deactivate calls remove one layer or all of
them. Layer handles come from actionSet like any other.
digital(handle: bigint, action: bigint): DigitalActionThe current state of a digital action: state is whether it is pressed,
active whether it is bound at all in the active action set. A false
active makes state meaningless, so check it before reacting to a false
press.
steam.controllers.init(true);
const [pad] = steam.controllers.list();
const fire = steam.controllers.digitalAction('fire');
steam.controllers.runFrame();
if (pad && steam.controllers.digital(pad, fire).state) console.log('firing');analog(handle: bigint, action: bigint): AnalogActionThe current state of an analog action: the two axes, the EInputSourceMode
that says how to read them, and the same active flag. The scale depends on
mode: -1 to 1 for a joystick, a delta for a mouse-like source.
motion(handle: bigint): InputMotionData_tThe gyro and accelerometer of a controller: the rotation quaternion, the acceleration, and the angular velocity, as the raw generated struct. Only hardware with motion sensors (Steam Controller, Steam Deck, DualShock, Switch pads) reports anything; the rest return zeroes.
vibrate(handle: bigint, leftSpeed: number, rightSpeed: number): void
vibrateExtended(
handle: bigint,
leftSpeed: number, rightSpeed: number,
leftTriggerSpeed: number, rightTriggerSpeed: number,
): voidvibrate runs the two main rumble motors, 0 to 65535 each. The speeds hold
until they are set again, so pass zeroes to stop. vibrateExtended adds the
trigger motors, which only reach hardware that has them, for example the Xbox
One and DualSense pads.
triggerHaptic(
handle: bigint,
location: number,
intensity: number,
gainDb?: number,
otherIntensity?: number,
otherGainDb?: number,
): voidPlays one haptic click on a controller's haptic speakers, which is Steam
Controller and Steam Deck hardware only. location is an
EControllerHapticLocation: 1 left, 2 right, 3 both. intensity is 0 to 255,
gainDb a signed loudness offset normally between -25 and 6. The two other
parameters apply to the second pad when location is both.
setLedColor(handle: bigint, r: number, g: number, b: number, flags?: number): voidSets the color of a controller's LED, DualShock and DualSense only. flags is
an ESteamInputLEDFlag: 1 sets the color (the default), 2 restores the user's
default and ignores the color. Steam has no result, so it cannot fail from
JavaScript.
showBindingPanel(handle: bigint): booleanOpens the Steam overlay on the binding screen for a controller. It needs the
overlay, so it returns false when the overlay is disabled or the app runs
outside Steam.
glyphForOrigin(origin: number, size?: number): string
originName(origin: number): stringglyphForOrigin returns the absolute path of the PNG glyph for one input
origin: the button picture to show in a prompt, matching whatever hardware the
action is bound to right now. size is an ESteamInputGlyphSize: 0 small, 1
medium (the default), 2 large. originName returns the human readable name of
the same origin, for example A Button, in the Steam client language.
Origins come from steam.input.GetDigitalActionOrigins and its analog twin,
which this layer does not wrap. Both calls give an empty string for an origin
Steam does not know.
enableDeviceCallbacks(): voidTurns the device callbacks on. Without this, none of the three subscriptions
below ever fires, because Steam does not send
SteamInputDeviceConnected_t, SteamInputDeviceDisconnected_t or
SteamInputConfigurationLoaded_t until it is called. Steam has no way to turn
them off again.
onConnected(listener: (handle: bigint) => void): () => void
onDisconnected(listener: (handle: bigint) => void): () => voidSubscribe to controllers appearing and going away, with the input handle of each, and return unsubscribe functions. Both need enableDeviceCallbacks first.
steam.controllers.init(true);
steam.controllers.enableDeviceCallbacks();
const off = steam.controllers.onConnected((handle) => console.log('pad', handle));
// later: off(); steam.controllers.shutdown();onConfigurationLoaded(
listener: (event: SteamInputConfigurationLoaded_t) => void,
): () => voidFires when Steam applies a binding configuration to a controller, which also happens on connect. The decoded struct says which device it was, who made the configuration, and whether it drives the Steam Input or the gamepad API. Passed through unchanged, because every field of it is worth having.
Returned by digital.
| Field | Type | Meaning |
|---|---|---|
state |
boolean |
True while the action is pressed. |
active |
boolean |
True while the action is bound in the active action set. A false makes state meaningless. |
Returned by analog.
| Field | Type | Meaning |
|---|---|---|
x |
number |
Horizontal value. Range depends on mode. |
y |
number |
Vertical value, same scale as x. |
mode |
number |
EInputSourceMode: how to read x and y (joystick, trigger, mouse, and so on). |
active |
boolean |
True while the action is bound. A false makes x and y meaningless. |
InputMotionData_t and SteamInputConfigurationLoaded_t are the generated
structs, unchanged. See Flat API.
| Shape | When |
|---|---|
Error: steamwand: no action set named '<name>' in the action manifest |
actionSet got 0n back. |
Error: steamwand: no digital action named '<name>' in the action manifest |
digitalAction got 0n back. |
Error: steamwand: no analog action named '<name>' in the action manifest |
analogAction got 0n back. |
Those three are the whole error surface. Every other method either returns a boolean Steam gave it or calls a function with no result, so a controller that ignores a rumble or an LED command reports nothing.
The rest of ISteamInput is on the raw generated steam.input:
- The origin lookups that feed glyphForOrigin:
GetDigitalActionOrigins,GetAnalogActionOrigins,GetActionOriginFromXboxOrigin,TranslateActionOrigin. - The name and glyph variants:
GetGlyphSVGForActionOrigin,GetGlyphForActionOrigin_Legacy,GetStringForDigitalActionName,GetStringForAnalogActionName,GetStringForXboxOriginandGetGlyphForXboxOrigin. -
GetActiveActionSetLayers, which reads back the layer stack this layer only writes. -
GetDeviceBindingRevision,GetRemotePlaySessionID,GetSessionInputConfigurationSettings,GetControllerForGamepadIndexandGetGamepadIndexForController. -
SetInputActionManifestFilePath, for an app that ships its manifest outside the Steam depot. -
Legacy_TriggerHapticPulseandLegacy_TriggerRepeatedHapticPulse, the Steam Controller pulse calls that predateTriggerSimpleHapticEvent. -
BWaitForData,BNewDataAvailableandStopAnalogActionMomentum. -
SetDualSenseTriggerEffect, the adaptive trigger control. - Everything on
ISteamController, the deprecated interface Steam Input replaced. It is generated assteam.controllerand should not be used in new code.
Flat API explains the calling convention.
Next: System for the Steam Deck check that usually sits next to this layer.