Phaser AE

2D controls

Free packv3.0.0-02DLogic

Game code reads intents and axes, never keys. Name the actions your game has, fire, jump, dash, anything, and the pack drives them from the keyboard, gamepads and touch buttons, for up to four players, with rebinding built in.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 19, 2026

2D controls: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

controls2d

function controls2d(opts: ControlsOptions = {}): Pack<{ controls: ControlsApi }>

The 2D controls pack. Samples every player's intents once per fixed step (controls-2d.intents hook) from keyboard, gamepad and touch codes, and returns controls. Requires a 2D world, for the pointer's world position.

Functions

resolveBindings

function resolveBindings(opts: ControlsOptions = {}): PlayerBindings[]

The bindings the options describe, one per player. Throws on a malformed set.

Constants

DEFAULT_BINDINGS

const DEFAULT_BINDINGS: PlayerBindings

The defaults, for one player: WASD and the arrows move; Space or J fire, E or K action, Escape pause; pad A, B and Start; the first connected pad, its left stick moving and its right stick aiming.

Types

AimOrigin

interface AimOrigin

A point the aim axis is measured from: the player, usually. Anything with a live x and y will do.

  • readonly x: number — World x.
  • readonly y: number — World y.

Axis

type Axis = 'move' | 'aim'

The two axes: move from keys or a stick, aim from the pointer or the right stick, measured from the aim origin.

ControlsApi

interface ControlsApi extends PlayerControls

game.controls: player 0's controls, every player's, and the pointer.

  • player(n: number): PlayerControls — Player n's controls. Throws for a player the options did not configure.
  • readonly players: number — How many players are configured.
  • readonly pointer: { x: number; y: number; down: boolean; pressed: boolean; released: boolean } | null — The pointer in WORLD units, or null. pressed / released went down / up since the last step, latched by the kernel: read them for a tap, not down from step to step, which misses a click shorter than a frame.

ControlsOptions

interface ControlsOptions

Manifest options.

  • bindings?: { move?: Partial<MoveKeys>; intents?: Record<string, IntentBinding | null>; pad?: number | 'any' | 'none'; stick?: 'left' | 'right' } — Overrides for the single player, merged over DEFAULT_BINDINGS: move replaces the directions it names, intents replaces the intents it names (null removes one), pad and stick replace. Ignored when players is given.
  • players?: PlayerBindings[] — Two or more players, each bound in full (nothing is merged, so two players never share the defaults). A player without pad gets the pad in its own slot. At most 4.
  • aimDeadzone?: number — A pointer closer than this, world units, to the aim origin gives aim {0, 0}. Default 12.

IntentBinding

interface IntentBinding

What drives one intent: any key in keys or any pad button in pad.

  • keys?: string[] — Key codes (KeyboardEvent.code, or a code a touch control presses, such as 'Touch:fire').
  • pad?: string[] — Gamepad button names: a b x y lb rb lt rt back start ls rs up down left right.

MoveKeys

interface MoveKeys

Key codes (KeyboardEvent.code) per direction; any held key counts.

  • up: string[] — Keys that push up (negative y).
  • down: string[] — Keys that push down (positive y).
  • left: string[] — Keys that push left (negative x).
  • right: string[] — Keys that push right (positive x).

PlayerBindings

interface PlayerBindings

One player's bindings.

  • move?: MoveKeys — Keys per direction for the move axis. Omit for a pad-only player.
  • intents: Record<string, IntentBinding> — The intents, by any name the game chooses (fire, jump, dash…).
  • pad?: number | 'any' | 'none' — Which gamepad drives this player: a slot index, 'any' for the first connected pad, or 'none'.
  • stick?: 'left' | 'right' — The pad's stick for the move axis; the other stick aims. Default 'left'.

PlayerControls

interface PlayerControls

One player's controls: intents and axes, never keys.

  • readonly index: number — This player's index, from 0.
  • axis(name: Axis): { x: number; y: number } — A direction of length ≤ 1. move: the keys (digital) or else the move stick (analogue); the game cannot tell. aim: the aim stick when it is pushed, else a unit vector from the aim origin to the pointer; {0, 0} with neither, or with the pointer within aimDeadzone of the origin.
  • pressed(intent: string): boolean — Held this fixed step. An intent this player has no binding for throws, naming the bound ones.
  • justPressed(intent: string): boolean — True for exactly one fixed step, even for a key tapped down and up between two steps.
  • justReleased(intent: string): boolean — True for the one fixed step after the intent was let go.
  • aimFrom(origin: AimOrigin | null): void — Measure aim from this point (the player); null measures from the camera's centre. Scene-scoped.
  • bind(intent: string, binding: IntentBinding | null): void — Rebind an intent, or add a new one; null removes it. Lasts until rebound: a settings screen calls it.
  • bindMove(keys: MoveKeys | null): void — Rebind the move keys; null leaves the move axis to the pad.
  • usePad(pad: number | 'any' | 'none'): void — Change which pad drives this player.
  • bindings(): PlayerBindings — A copy of the current bindings, for a settings screen or a save.