Skip to content

Keyboard Input

Open the editor to try keyboard-controlled sketches live.

How It Works

The keys object tracks which keys are currently held. Each key is a property set to true while pressed and false when released.

javascript
if (keys.ArrowUp) {
  state.y -= 100 * dt;
}
if (keys[' ']) {
  // space bar held
}

WARNING

keys is read-only. Do not assign keys.foo = true.

Key Naming

Names come from event.key:

Keykeys property
Arrow Upkeys.ArrowUp
Arrow Downkeys.ArrowDown
Arrow Leftkeys.ArrowLeft
Arrow Rightkeys.ArrowRight
W / wkeys.w
A / akeys.a
S / skeys.s
D / dkeys.d
R / rkeys.r
Spacekeys[' ']
Enterkeys.Enter
Shiftkeys.Shift
Any letterLowercase, e.g. keys.p

Check multiple keys to support both arrow and WASD control:

javascript
if (keys.ArrowUp || keys.w) state.dir = { x: 0, y: -1 };
if (keys.ArrowDown || keys.s) state.dir = { x: 0, y: 1 };

Game Loop Pattern

For games, use a timer-based tick inside your frame function:

javascript
if (!state.init) {
  // game state init
  state.timer = 0;
  state.tickInterval = 0.12; // seconds per game tick
  state.init = true;
}

state.timer += dt;
if (state.timer >= state.tickInterval) {
  state.timer = 0;
  // read keys, update game state, check collisions
}

Common Patterns

Continuous Movement (Platformer)

javascript
if (!state.init) {
  state.x = w / 2;
  state.y = h / 2;
  state.vx = 0;
  state.vy = 0;
  state.init = true;
}

const speed = 200;
const jumpForce = -400;
const gravity = 800;

// Horizontal input
if (keys.ArrowLeft || keys.a) state.vx = -speed;
else if (keys.ArrowRight || keys.d) state.vx = speed;
else state.vx *= 0.8; // friction

// Jump (only when grounded)
if ((keys.ArrowUp || keys.w || keys[' ']) && state.y >= h - 20) {
  state.vy = jumpForce;
}

// Gravity
state.vy += gravity * dt;

// Integrate
state.x += state.vx * dt;
state.y += state.vy * dt;

// Floor
if (state.y > h - 20) {
  state.y = h - 20;
  state.vy = 0;
}

return {
  particles: [{ x: state.x, y: state.y, r: 12, color: '#8B5CF6' }],
  text: { x: state.x.toFixed(0), y: state.y.toFixed(0) },
};

Restart on Key Press

javascript
if (state.gameOver && keys.r) {
  state.init = false; // triggers re-init next frame
}

Toggle with Single Press

Since keys tracks held state (not edge triggers), use a cooldown or flag:

javascript
if (!state.init) {
  state.showTrail = true;
  state.init = true;
}

if (keys.t && !state.lastT) {
  state.showTrail = !state.showTrail;
}
state.lastT = keys.t;

Multi-Key Combos

javascript
// diagonal movement
let dx = 0, dy = 0;
if (keys.ArrowLeft || keys.a) dx -= 1;
if (keys.ArrowRight || keys.d) dx += 1;
if (keys.ArrowUp || keys.w) dy -= 1;
if (keys.ArrowDown || keys.s) dy += 1;

// normalize diagonal
if (dx !== 0 && dy !== 0) {
  dx *= 0.707;
  dy *= 0.707;
}

state.x += dx * speed * dt;
state.y += dy * speed * dt;

Lifecycle

Key pressed
  → keys[key] = true (on keydown)
  → your frame code reads it
  → Key released
  → keys[key] = false (on keyup)
  → Window loses focus
  → all keys cleared (on blur)

Limitations

  • No keypress events (character input) — only raw key state
  • Keys persist for the duration of the press; check every frame or use a tick timer
  • Browser shortcuts may intercept some keys (e.g. Ctrl+R, F5)
  • Module files do not receive keys — pass it as a function argument

See Also