Learn

/

Input Handling

Input Handling

5 patterns

Input buffering, dead zones, action mapping, and frame-independent input. You'll hit this when jumps feel unresponsive, analog sticks drift, or rebinding keys requires a code change.

Avoid
document.addEventListener("keydown", (e) => {
  if (e.code === "Space") player.jump();
  if (e.code === "KeyX") player.attack();
  if (e.code === "ShiftLeft") player.dash();
  if (e.code === "KeyE") player.interact();
});

// Adding gamepad support means
// duplicating every binding
gamepad.onButtonPress(0, () => player.jump());
gamepad.onButtonPress(2, () => player.attack());
document.addEventListener("keydown", (e) => {
  if (e.code === "Space") player.jump();
  if (e.code === "KeyX") player.attack();
  if (e.code === "ShiftLeft") player.dash();
  if (e.code === "KeyE") player.interact();
});

// Adding gamepad support means
// duplicating every binding
gamepad.onButtonPress(0, () => player.jump());
gamepad.onButtonPress(2, () => player.attack());

Prefer
// Define actions, not keys
const actions = new ActionMap({
  jump:     [Key.Space, Pad.A],
  attack:   [Key.X, Pad.X],
  dash:     [Key.ShiftLeft, Pad.LB],
  interact: [Key.E, Pad.Y],
});

function update() {
  if (actions.justPressed("jump")) player.jump();
  if (actions.justPressed("attack")) player.attack();
  if (actions.justPressed("dash")) player.dash();
  if (actions.justPressed("interact")) player.interact();
}

// Rebinding is a data change, not a code change
actions.rebind("jump", Key.W);
// Define actions, not keys
const actions = new ActionMap({
  jump:     [Key.Space, Pad.A],
  attack:   [Key.X, Pad.X],
  dash:     [Key.ShiftLeft, Pad.LB],
  interact: [Key.E, Pad.Y],
});

function update() {
  if (actions.justPressed("jump")) player.jump();
  if (actions.justPressed("attack")) player.attack();
  if (actions.justPressed("dash")) player.dash();
  if (actions.justPressed("interact")) player.interact();
}

// Rebinding is a data change, not a code change
actions.rebind("jump", Key.W);
Why avoid

Hardcoding physical key checks scatters input logic across the codebase and locks players into a fixed control scheme. Supporting a second input device means duplicating every binding. Letting players remap keys requires rewriting the event handlers instead of just swapping a data table.

Why prefer

An action mapping layer separates 'what the player wants to do' from 'which physical button they pressed.' Game logic only references action names, so adding gamepad support, remapping keys, or supporting multiple control schemes is a data change. Players expect rebindable controls, and this pattern makes that trivial.

Game Programming Patterns: Command
Avoid

No deadzone (raw input)


Prefer

Circular deadzone (radius 0.2)

Why avoid

Passing raw stick values straight to movement means the character drifts even when the player is not touching the stick. Physical sticks rarely rest at exactly (0, 0) due to manufacturing tolerances. Per-axis clamping (square deadzone) is better than nothing but creates diagonal bias where the deadzone corners let more input through than the edges.

Why prefer

A circular deadzone ignores input below a magnitude threshold, filtering out stick drift and noise. Radial remapping then rescales the remaining range so that the first detectable movement starts from zero, not from the deadzone edge. This prevents both idle drift and the 'dead spot' where small intentional movements are lost.

Game Developer: Thumbstick Dead Zones
Avoid

Jump only while grounded


Prefer

Coyote time: 120ms grace window

Why avoid

Requiring the player to press jump on the exact frame they are grounded feels unresponsive. Players running off a ledge lose their jump the instant they leave the ground, which feels like a bug. Players pressing jump one frame before landing get nothing, which feels laggy. Both are solvable with small timing windows.

Why prefer

Coyote time gives a brief grace period after leaving a platform, so pressing jump one frame late still works. Input buffering remembers a jump press for a few frames, so pressing slightly before landing still triggers. Together they make platforming feel responsive and forgiving, which players perceive as 'tight controls' rather than leniency.

Game Maker's Toolkit: Platformer Mechanics
Avoid

Fixed height: hold duration ignored


Prefer

Variable height: hold duration controls arc

Why avoid

A fixed-height jump always applies the same impulse regardless of how the player presses the button. Every jump is identical, which feels robotic and removes a layer of skill expression. Players cannot adjust mid-air to thread through tight gaps or land on small platforms because the arc is predetermined.

Why prefer

Variable jump height ties the arc to how long the player holds the button. Releasing early cuts the upward velocity, producing a short hop. Holding longer lets the full impulse play out. This gives players fine-grained control over their trajectory, which is essential for precise platforming. Nearly every modern platformer from Mario to Celeste uses this technique.

Game Maker's Toolkit: Platformer Mechanics
Avoid

Raw analog input


Prefer

Aim assist with magnetism radius

Why avoid

Raw analog input without any assistance makes it extremely difficult to track moving targets precisely. Analog sticks have lower resolution and higher latency than mice, so demanding the same precision from both devices creates an unfair gap. Players experience constant overshooting and frustration rather than the intended challenge.

Why prefer

Aim assist applies a subtle velocity bias toward nearby targets when the crosshair is within a magnetism radius. The player still controls the crosshair, but the assist compensates for the imprecision of analog sticks. Most console shooters use some combination of aim slowdown (reduced sensitivity near targets) and bullet magnetism. The goal is to match the precision that mouse input gets for free.

Game Developer: Aim Assist in Halo