Phaser AE

Waves, difficulty and goals

Pro packv3.0.0-0Arcade

What a level throws at the player and what counts as beating it. A wave plan schedules the arrivals, a difficulty curve holds every number that rises with the level, and goals are the win and lose rules, each with a label and a progress bar's worth of state.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 19, 2026

Waves, difficulty and goals: 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

wavePlan

function wavePlan(): Pack<{ wavePlan: WavePlanApi }>

The wave-plan pack: wave plans, difficulty curves, goals and catalogues. Pure state, so it needs no backend and runs headless. No options.

Helpers

createCatalogue

function createCatalogue(wanted: readonly string[]): Catalogue

Creates a Catalogue of the names to capture.

createCurve

function createCurve<K extends string>( ranges: Record<K, CurveRange>, opts: CurveOptions<string> = {}, ): DifficultyCurve<K>

Creates a DifficultyCurve from named ranges.

createWaves

function createWaves(opts: WavePlanOptions): WavePlan

Creates a WavePlan.

goals

const goals: { clear(left: () => number, label?: string): Objective; count(need: number, got: () => number, label?: string): Objective; survive(seconds: number, label?: string): Objective; ... 7 more ...; any(list: Objective[], label?: string): Objective; } …

The objective templates. Each returns a plain Objective.

Classes

Catalogue

class Catalogue

One of each — Web Wars' trophy room. Not a counter: WHICH ones you have is the game, and the missing list is what the HUD shows.

  • readonly wanted: readonly string[] — The full list of names to capture.
  • constructor(wanted: readonly string[])
  • capture(name: string): boolean — True if this one was new.
  • has(name: string): boolean — True if name has been captured.
  • get have(): string[] — The wanted names already captured, in wanted order.
  • get missing(): string[] — The wanted names not yet captured.
  • get complete(): boolean — True when every wanted name is held.
  • clear(): void — Removes every captured name.
  • objective(label = 'CAPTURE'): Objective — As an Objective, so it binds to a HUD like any other.

DifficultyCurve

class DifficultyCurve<K extends string>

Difficulty as data. Every number a wave, an enemy or a drain rate needs is declared once as a range and read as curve.at(level).speed — so raising the ramp is one edit, not a hunt through the game for magic numbers.

  • level: number — The current level. Set it as the game advances.
  • constructor(ranges: Record<K, CurveRange>, opts: CurveOptions<string> = {})
  • at(level = this.level): Record<K, number> — Every value at level (default: the current one).
  • value(name: K, level = this.level): number — One value, without building the object.
  • unlocked(name: string, level = this.level): boolean — Has this enemy type been introduced yet?
  • available(level = this.level): string[] — Everything introduced by level — the pool a wave draws from.

WavePlan

class WavePlan

A declarative spawn schedule. loop counts completed passes through the list, which is the number every per-loop mutator is a function of.

  • index: number — Index into waves.
  • loop: number — Completed passes through the whole list.
  • time: number — Seconds into the current wave.
  • readonly waves: WaveDef[] — The wave list, as given to createWaves.
  • finished: boolean — A non-looping plan that has run out.
  • constructor(opts: WavePlanOptions)
  • get wave(): WaveDef | undefined — The current wave, or undefined when there is none.
  • get spent(): boolean — Every entry has fired and (if given) clear() reads true.
  • start(index = 0): void — Starts the wave at index with its clock at zero. Default 0.
  • update(dt: number): void — Fires the spawn entries that are due and advances a spent wave. Call once a frame.
  • next(): void — Force the next wave — a superzapper, or a debug skip.
  • reset(): void — Back to wave 0 with loop at 0 — a new game.

Types

CurveOptions

interface CurveOptions<K extends string>

Configures createCurve.

  • over?: number — Level at which every range reaches its to. Default 10.
  • ease?: 'linear' | 'ease' | 'step' — Shape of the ramp. ease starts gently; step snaps per level.
  • unlock?: Partial<Record<K, number>> — Minimum level at which a named thing appears at all.

CurveRange

type CurveRange = [number, number]

[from, to] — the value at level 0 and at the level the curve tops out.

Objective

interface Objective

A win/lose rule a HUD can draw. Get one from goals or Catalogue.objective.

  • readonly label: string — The name the HUD shows.
  • readonly progress: number — 0..1. A HUD can draw any objective without knowing which kind it is.
  • readonly done: boolean — True once the objective is met.
  • readonly failed: boolean — True once the objective is lost.
  • update(dt: number): void — Steps the rule. Call once a frame.
  • reset(): void — Back to the start: not done, not failed, progress 0.

SpawnEntry

interface SpawnEntry

One scheduled spawn within a wave.

  • type: string — Passed straight back to onSpawn; the game decides what it means.
  • count?: number — How many. Default 1.
  • at?: number — Seconds after the wave starts. Default 0.
  • every?: number — Seconds between them when count > 1. Default 0 — all at once.
  • data?: unknown — Anything else the spawner needs: a lane, a depth, a formation.

WaveDef

interface WaveDef

One wave: what spawns, and what holds it open.

  • name?: string — A label for the HUD or the wave card.
  • spawn: SpawnEntry[] — The spawn entries this wave fires.
  • clear?(): boolean — Hold the wave open until this reads true, even once everything has spawned. Omit and the wave ends when its last entry has fired.

WavePlanApi

type WavePlanApi = typeof kit

game.wavePlan: every builder and type this pack carries, as one namespace.

WavePlanOptions

interface WavePlanOptions

Configures createWaves.

  • waves: WaveDef[] — The waves, played in order.
  • loop?: boolean — Start again at wave 0 with loop incremented. Default true.
  • onSpawn(entry: SpawnEntry, index: number, plan: WavePlan): void — Called once for each spawn. index counts the fires of this entry, from 0.
  • onWave?(wave: WaveDef, index: number, plan: WavePlan): void — Fired as each wave starts.
  • onLoop?(loop: number, plan: WavePlan): void — Fired when the last wave of a loop clears.