Skip to content

Steam Input

Controller and gamepad support via Steam Input API.

Overview

Steam Input provides a unified API for all controller types:

  • Xbox controllers
  • PlayStation controllers (DualShock 4, DualSense)
  • Nintendo Switch Pro Controller
  • Steam Controller
  • Generic gamepads

The API is action-based, meaning you define actions (like "Jump", "Move") in Steam and query their state, rather than checking individual buttons.

Setup

1. Configure Actions in Steamworks

Before using Steam Input, configure your game's actions in the Steamworks Partner Portal:

  1. Go to App Admin > Steam Input
  2. Create an Action Set (e.g., "GameControls")
  3. Define Digital Actions (buttons) like "jump", "fire", "pause"
  4. Define Analog Actions (sticks/triggers) like "move", "look"
  5. Set up default bindings for each controller type

2. Initialize Steam Input

javascript
// Initialize Steam Input (call once at game start)
const success = await steam.initInput(false);
if (success) {
  console.log('Steam Input ready');
}

Methods

initInput(explicitlyCallRunFrame)

Initialize the Steam Input system.

javascript
await steam.initInput(false);

Parameters:

  • explicitlyCallRunFrame (boolean): If true, you must call inputRunFrame() manually. If false, Steam handles it automatically.

Returns: Promise<boolean> - true if successful

shutdownInput()

Shutdown Steam Input. Call when your game exits.

javascript
await steam.shutdownInput();

inputRunFrame(reserved)

Process input events. Only needed if initInput(true) was called.

javascript
// In your game loop
await steam.inputRunFrame(false);

getConnectedControllers()

Get all connected controller handles.

javascript
const controllers = await steam.getConnectedControllers();
// Returns array of controller handle strings: ["12345", "67890"]

Returns: Promise<string[]> - Array of controller handles

getActionSetHandle(name)

Get handle for an action set defined in Steamworks.

javascript
const actionSet = await steam.getActionSetHandle('GameControls');

Returns: Promise<string> - Action set handle

activateActionSet(controllerHandle, actionSetHandle)

Activate an action set for a controller.

javascript
const controllers = await steam.getConnectedControllers();
const actionSet = await steam.getActionSetHandle('GameControls');

for (const controller of controllers) {
  await steam.activateActionSet(controller, actionSet);
}

getDigitalActionHandle(name)

Get handle for a digital action (button).

javascript
const jumpAction = await steam.getDigitalActionHandle('jump');

getAnalogActionHandle(name)

Get handle for an analog action (stick/trigger).

javascript
const moveAction = await steam.getAnalogActionHandle('move');

getDigitalActionData(controllerHandle, actionHandle)

Get current state of a digital action.

javascript
const data = await steam.getDigitalActionData(controller, jumpAction);
// { state: true/false, active: true/false }

if (data.active && data.state) {
  player.jump();
}

Returns:

typescript
{
  state: boolean,   // true = pressed
  active: boolean   // true = action is bound and available
}

getAnalogActionData(controllerHandle, actionHandle)

Get current state of an analog action.

javascript
const data = await steam.getAnalogActionData(controller, moveAction);
// { mode: 1, x: 0.5, y: -0.3, active: true }

if (data.active) {
  player.velocity.x = data.x * speed;
  player.velocity.y = data.y * speed;
}

Returns:

typescript
{
  mode: number,     // Input source mode
  x: number,        // X axis (-1.0 to 1.0)
  y: number,        // Y axis (-1.0 to 1.0)
  active: boolean   // true = action is bound and available
}

getInputTypeForHandle(controllerHandle)

Get the type of controller.

javascript
const type = await steam.getInputTypeForHandle(controller);

Returns: Promise<number> - Controller type enum:

ValueType
0Unknown
1Steam Controller
2Xbox 360
3Xbox One
4Generic XInput
5PS4
6Apple MFi
7Android
8Switch Pro
9Steam Deck
10PS3
11PS5

Complete Example

javascript
// Initialize
let controllers = [];
let actionSet, jumpAction, moveAction;

async function initControls() {
  if (!await steam.initInput(false)) {
    console.log('Steam Input not available');
    return;
  }
  
  // Get action handles (defined in Steamworks)
  actionSet = await steam.getActionSetHandle('GameControls');
  jumpAction = await steam.getDigitalActionHandle('jump');
  moveAction = await steam.getAnalogActionHandle('move');
  
  // Activate for all controllers
  controllers = await steam.getConnectedControllers();
  for (const c of controllers) {
    await steam.activateActionSet(c, actionSet);
  }
  
  console.log(`${controllers.length} controller(s) connected`);
}

// Game loop
async function update() {
  // Refresh controller list periodically
  controllers = await steam.getConnectedControllers();
  
  for (const controller of controllers) {
    // Check jump button
    const jump = await steam.getDigitalActionData(controller, jumpAction);
    if (jump.active && jump.state) {
      player.jump();
    }
    
    // Read movement stick
    const move = await steam.getAnalogActionData(controller, moveAction);
    if (move.active) {
      player.velocity.x = move.x * player.speed;
      player.velocity.y = move.y * player.speed;
    }
  }
}

// Cleanup
async function cleanup() {
  await steam.shutdownInput();
}

Notes

  • Steam Input requires action configuration in the Steamworks Partner Portal
  • Without configured actions, getDigitalActionData and getAnalogActionData will return active: false
  • For simple gamepad support without Steam, consider using the browser's Gamepad API (navigator.getGamepads())
  • Steam Input works on Windows, macOS, and Linux
  • The Steam Deck automatically uses Steam Input for all games

Browser Gamepad API Alternative

If you don't need Steam-specific features, the browser's Gamepad API works without configuration:

javascript
function pollGamepad() {
  const gamepads = navigator.getGamepads();
  for (const gp of gamepads) {
    if (!gp) continue;
    
    // Buttons
    if (gp.buttons[0].pressed) player.jump(); // A button
    
    // Sticks
    player.velocity.x = gp.axes[0] * speed; // Left stick X
    player.velocity.y = gp.axes[1] * speed; // Left stick Y
  }
}

The browser API works in all GemShell builds, even without Steam.