arcade2d
Class

Keyboard

input/keyboard.ts:74

Game-scoped input sampler that tracks the set of physical keys currently held on the keyboard.

Like Keyboard's sibling import('./mouse').Mouse, this component lives at the Game tier rather than on a World: keyboard events are page-global, fire whether a world is mounted or not, and the same physical keyboard is shared across world swaps (menu → gameplay → game-over). Game code reads keyboard state via the convenience accessors on the game or world — game.getKeyboardState() or world.getKeyboardState() — rather than touching this component directly; the class itself is exported so callers writing custom bootstrap, or users who want to swap in a custom input source (e.g. a recorded-input variant for tests), have something concrete to construct and register.

Physical vs logical keys

Held state is keyed by KeyboardEvent.code (the physical key) rather than KeyboardEvent.key (the logical character). This is the standard choice for game input — WASD movement keeps working on AZERTY or Dvorak layouts without per-layout remapping, because the four keys in the top-left of the letter block always report as KeyW/KeyA/KeyS/ KeyD regardless of what character they actually produce. See KeyboardState for the cross-reference to MDN's code-value list.

Snapshot semantics

The component follows the canonical "input sampler" pattern, identical to import('./mouse').Mouse:

  1. DOM events update a private pending set as they arrive — this is asynchronous and can happen at any time relative to the engine's update tick.
  2. Keyboard.onPreUpdate copies the pending set into a private snapshot once per game tick, before the active world's update phase runs.
  3. Keyboard.getState returns a KeyboardState derived from the snapshot, so every component reading the keyboard during a single tick sees the same held-key set.

Event sourcing

Listeners are attached during Keyboard.onAdded and removed in Keyboard.onDestroy:

  • keydown and keyup are listened on window. The canvas itself does not receive keyboard events unless explicitly focused (which requires a tabindex and is fragile across browsers), so window- level listening is the standard choice for browser games.
  • blur on window clears the pending set. Without this, a key held when the user alt-tabs away never receives its keyup and stays "stuck down" forever — a class of bug that's both common and very annoying to debug.

The engine does not call event.preventDefault() on any of these — game code is free to do that itself if it wants to suppress browser defaults like Space scrolling the page or Tab moving focus. Suppressing globally here would break browser UX for any non-gameplay UI rendered on the same page.

Auto-repeat

Browsers fire repeated keydown events while a key is held, with event.repeat === true. The component does not filter these — adding to the pending set is idempotent and the held-state semantics are unchanged. Edge-triggered "just pressed this frame" detection is not provided by this component; layer that on at the action-mapping/GameControls tier when it lands.

Constructors

#
constructor(host: Game): Keyboard

Parameters

Properties

#
enabled: boolean

Per-component gate on the three update hooks (onPreUpdate, onUpdate, onPostUpdate). When explicitly false, the engine skips all three for this component during the host's tick — useful for temporarily pausing behaviour (e.g. a freeze powerup) without removing the component and losing its internal state.

Does not gate onAdded or onDestroy; those always fire so a host can never end up with a half-attached component.

readonly #
host: Game

The host this component is attached to. Stored read-only; subclasses access it as this.host directly, or through the tier-appropriate aliases (this.world, this.game) on the per-tier abstract bases.

Accessors

readonly #
game: Game

The Game this component is attached to — identical to AbstractComponent.host at this tier, exposed under the game name so subclass code reads the same on every tier.

Methods

#
getState(): KeyboardState

Returns a fresh KeyboardState per call — game code may stash the returned object for the duration of a frame without worrying about mid-frame mutation. The contained downKeys set is a fresh clone of the internal snapshot, exposed as ReadonlySet<string> so callers cannot accidentally mutate engine state through it.

Returns

KeyboardState
#
onAdded(): void

Lifecycle hook that is called when the component is added to the host object. Should not be called directly.

Returns

void
#
onDestroy(): void

Lifecycle hook that is called when the host object is destroyed. Should not be called directly.

Returns

void
#
onPreUpdate(): void

Returns

void
#
onUpdate(_update: WorldUpdate, _deps: Record): void

Lifecycle hook for the main update phase. Called once per world tick, after every component's onPreUpdate and before any onPostUpdate.

Parameters

Returns

void
ESC