Input
Keyboard polling. Input answers one question — is this key held right now? — and you ask it
every frame inside setLoop. There are no key events (no keydown/keyup listeners); for taps
and drags on objects use pointer events instead.
At a glance
setLoop(dt => {
if (Input.key('ArrowRight')) player.x += 200 * dt
if (Input.key('ArrowLeft')) player.x -= 200 * dt
})Polling
Input.key(code: string): boolean // true while the key is heldcode is a KeyboardEvent.code-style string — the physical key, not the typed character:
'ArrowRight', 'KeyW', 'Space', 'Enter', 'ShiftLeft'. WASD is 'KeyW'/'KeyA'/
'KeyS'/'KeyD' regardless of keyboard layout.
polling, not events — multiply movement by dt (seconds) so speed is
frame-rate-independent.
"Pressed this frame" — edge detection
Input.key is level-triggered (true every frame the key is down). For actions that must fire
once per press (jump, shoot), keep a wasDown map and latch it at the end of the frame:
const wasDown: Record<string, boolean> = {}
const pressed = (code: string) => Input.key(code) && !wasDown[code]
setLoop(dt => {
if (pressed('Space')) player.jump() // fires once per key-down
if (Input.key('ArrowRight')) player.x += 200 * dt // fires every frame while held
wasDown['Space'] = Input.key('Space') // latch at end of frame
})Latch every key you edge-detect — one line per key at the bottom of the loop.
Pitfalls
// ✗ level-triggered check for a one-shot action — jumps every frame while Space is held
if (Input.key('Space')) player.jump()
// ✓ edge-detect with the wasDown recipe above
if (pressed('Space')) player.jump()
// ✗ character codes — layout-dependent and not what Input.key expects
Input.key('w')
// ✓ physical key codes
Input.key('KeyW')See also
- Pointer events & gestures — touch/mouse taps and drag tracking
- Conventions —
setLoopanddtin seconds