Phaser AE

State machines and game flow

Free packv3.0.0-4Logic

A brain for every object that has modes, and a phase graph for the game itself. Patrol, chase and dying become three named states; attract, play, dying and game over become four named phases; and neither needs a tangle of flags.

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

Released October 14, 2026 · updated October 20, 2026

State machines and game flow: 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

fsm

function fsm(): Pack<{ fsm: FsmApi }, {}, { fsm: FsmData }, { brain?: Brain }>

The fsm pack: game.fsm (brains, plus phase graphs and sequences for the game's own flow), the fsm columns (state u32, time f32) with go/is methods on h.fsm, the brain spawn field, and a simulate step before collision. No options.

Helpers

createPhases

function createPhases<C>(ctx: C, defs: Record<string, PhaseDef<C>>): Phases<C>

Creates a Phases graph over the shared context ctx.

createSequence

function createSequence(steps: SceneStep[], opts: { loop?: boolean } = {}): Sequence

Creates a Sequence of timed steps.

Classes

Phases

class Phases<C>

A phase graph. Transitions are queued and applied between frames, never inside update, so a phase cannot be torn down while its own code is running.

  • readonly ctx: C — The shared game context, passed to every phase callback.
  • time: number — Seconds since this phase was entered.
  • prev: string | null — The phase left behind, or null.
  • constructor(ctx: C, defs: Record<string, PhaseDef<C>>)
  • get name(): string | null — The active phase name, or null before the first go().
  • in(...names: string[]): boolean — True while name is any of these — reads better than a chain of ===.
  • go(next: string): void — Queue a transition. Applied at the top of the next update.
  • restart(): void — Back to this phase's restartAt, or a fresh entry into it. The death → respawn edge: restartAt is what stops a restart re-running an intro.
  • own<T extends Disposable | (() => void)>(thing: T): T — Dispose of this when the phase exits — shapes, trails, starfields, bodies, or a plain function. Retained objects outlive a phase otherwise, and a vector game that forgets one leaves it burning on the screen for good.
  • at(t: number, fn: (ctx: C) => void): void — Run fn once, t seconds into this phase. Cleared on exit.
  • every(period: number, fn: (ctx: C) => void): void — Run fn every period seconds while this phase is active.
  • update(dt: number): void — Applies a queued transition, then runs the phase, its timers and its edges. Call once a frame.

Sequence

class Sequence

A timed run of named steps. Attract loops; a cut scene does not.

  • readonly steps: SceneStep[] — The steps, in play order.
  • loop: boolean — True to start again after the last step. Default true.
  • time: number — Seconds into the current step.
  • index: number — Index of the current step.
  • finished: boolean — True once a non-looping sequence has run out.
  • onStep: ((name: string, index: number) => void) | null — Called as each step starts.
  • constructor(steps: SceneStep[], opts: { loop?: boolean } = {})
  • get current(): string — The name of the current step, or an empty string when there are no steps.
  • get frac(): number — 0..1 through the current step — for fades and slides.
  • update(dt: number): void — Advances the clock and moves to the next step when the current one is done. Call once a frame.
  • go(step: string | number): void — Jump to a step by name (or index), resetting its clock.
  • reset(): void — Back to the first step with the clock at zero.

Types

Brain

interface Brain<S extends string = string, H extends Handle = Handle>

A state machine definition: pass as brain at spawn or to game.fsm.brain.

  • initial: S — The state entered when the brain is attached.
  • states: Record<S, StateDef<S, H>> — Every state by name; go to a name not here throws.

Disposable

interface Disposable

Anything the phase system can dispose of when its phase exits.

  • kill?(): void — Removes the object from play. Called on phase exit when present.
  • destroy?(): void — Frees the object. Called on phase exit when present.

FsmApi

interface FsmApi

What game.fsm presents.

  • brain<S extends string>(h: Handle, brain: Brain<S>): void — Give a handle a brain and enter its initial state now; replaces any brain it had without calling exit.
  • go(h: Handle, state: string): void — Transition h; same rules as h.fsm.go.
  • state(h: Handle): string | null — The current state's name, or null when h has no brain.
  • define<S extends string>(brain: Brain<S>): Brain<S> — A brain helper: states as a typed union, checked at the call site.
  • phases<C>(ctx: C, defs: Record<string, PhaseDef<C>>): Phases<C> — The game's own flow as a phase graph over a context object: the same as the createPhases helper.
  • sequence(steps: SceneStep[], opts?: { loop?: boolean }): Sequence — A timed run of named steps, for an attract mode or a cut scene: the same as the createSequence helper.

FsmData

interface FsmData

What h.fsm presents: the columns plus the methods the pack attaches.

  • readonly state: number — The current state as its INDEX in the brain's state list (a roster column). For the name use is() or game.fsm.state(h).
  • readonly time: number — Seconds in the current state; reset to 0 by go.
  • go(state: string): void — Transition: exit on the old state, enter on the new. Throws on an unknown state or a handle without a brain.
  • is(state: string): boolean — True when the current state is state.

PhaseDef

interface PhaseDef<C>

One phase: its callbacks, edges and timed exit.

  • enter?(ctx: C, from: string | null): void — Entered — from is the phase left behind, or null on the first entry.
  • update?(ctx: C, dt: number, time: number): void — Once a frame while active, before the edges are tested.
  • exit?(ctx: C, to: string | null): void — Left — to is where it is going. Owned objects are disposed after this.
  • to?: PhaseEdge<C>[] — Tested in order after update; the first true one wins.
  • after?: number — Leave for then automatically after this many seconds.
  • then?: string — Where after goes.
  • restartAt?: string — restart() returns here instead of re-entering this phase.

PhaseEdge

interface PhaseEdge<C>

An outward edge: when when reads true, go to go.

  • when(ctx: C): boolean — Tested each frame; true means take this edge.
  • go: string — The name of the phase to enter.

SceneStep

interface SceneStep

One step of a Sequence: a name and how long it holds.

  • name: string — The label current reports.
  • time: number — Seconds this step holds.

StateDef

interface StateDef<S extends string, H extends Handle = Handle>

One state of a brain. Every hook is optional.

  • enter?(h: H, from: S | null): void — Called on entering the state; from is null on the first entry.
  • tick?(h: H, dt: number): void — Called every simulate step while current, with dt in seconds.
  • exit?(h: H, to: S): void — Called on leaving the state for to.