Phaser AE

Scores, lives and high scores

Free packv3.0.0-0Arcade

The numbers a game keeps about the player rather than the world: a score, a stock of lives, a fuel gauge that warns once, an extra ship every 10,000 points, the line the announcer says, the high-score table with three initials, and the turns between players. It is plain state, stepped with your frame time, and it draws nothing, so any HUD can read it.

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

Released October 19, 2026

Scores, lives and high scores: 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

scoreboard

function scoreboard(): Pack<{ scoreboard: ScoreboardApi }>

The scoreboard pack: score, lives and other meters with award rules, callouts, high scores, initials and players. Pure state, so it needs no backend and runs headless. No options.

Helpers

createCues

function createCues(opts: CueOptions = {}): Cues

Creates a Cues system.

createHighScores

function createHighScores(opts: { size?: number; seed?: ScoreEntry[] } = {}): HighScores

Creates a HighScores table.

createInitials

function createInitials(opts: { length?: number; chars?: string; initial?: string } = {}): Initials

Creates an Initials picker.

createPlayers

function createPlayers<A>(count: number, opts: { lives?: number; teams?: number[] } = {}): Players<A>

Creates a Players set of count slots.

createResources

function createResources<K extends string>(defs: Record<K, MeterOptions>): Resources<K>

Named meters, updated together, with award rules on top.

const res = createResources({
  score: {},
  lives: { value: 3, onEmpty: () => flow.go('gameover') },
  fuel:  { value: 100, max: 100, rate: -4, warn: 25, onEmpty: () => ship.die() },
});
res.award({ of: 'score', every: 10000, give: (r) => r.lives.add(1) });

Meter names live on the object, so avoid all, get, add, award and update as meter names.

flash

function flash(time: number, hz = 4, duty = 0.5): boolean

On/off at hz, from a running clock — the flashing warning lamp, the selected menu row, an invulnerable ship. duty is the fraction lit.

Classes

Cues

class Cues

The commander's taunt, the docking confirmation, the low-oxygen warning. The part worth having is not the playback — it is PRIORITY and COOLDOWN, without which a threshold crossing fires every frame and two warnings talk over each other.

  • text: string | null — The subtitle currently up, or null.
  • key: string | null — The key currently holding, or null.
  • constructor(opts: CueOptions = {})
  • define(key: string, def: CueDef): this — Adds one cue definition under key.
  • load(defs: Record<string, CueDef>): this — Define several at once.
  • say(key: string): boolean — Fire a cue. Returns false if it was on cooldown or outranked.
  • when(test: () => boolean, key: string): this — Fire key whenever test turns true — the RISING edge only. This is how a warning binds to a resource: cues.when(() => fuel.low, 'fuel-low').
  • update(dt: number): void — Steps the clock, tests the watches, and drops a cue past its hold time. Call once a frame.
  • reset(): void — Drop the current cue and clear every cooldown (a new game).

HighScores

class HighScores

A sorted high-score table with a fixed number of rows. Create one with createHighScores.

  • readonly size: number — The maximum number of rows. Default 8.
  • entries: ScoreEntry[] — The rows, highest score first.
  • constructor(opts: { size?: number; seed?: ScoreEntry[] } = {})
  • place(score: number): number — Where this score would land, or -1 if it would not make the table.
  • qualifies(score: number): boolean — True if score would make the table.
  • add(name: string, score: number): number — Insert and return the row, or -1 if it did not qualify.
  • toJSON(): ScoreEntry[] — The table as plain rows, to save with the save pack (or anywhere): the pack itself keeps nothing.
  • load(data: ScoreEntry[] | null | undefined): this — Replaces the table from saved data. Rows that are not valid are dropped.

Initials

class Initials

The three-letter initials picker: up/down changes a letter, left/right moves.

  • readonly chars: string — The characters a slot cycles through.
  • readonly length: number — The number of letter slots. Default 3.
  • index: number — Which slot the cursor is on.
  • done: boolean — True once the entry has been committed.
  • constructor(opts: { length?: number; chars?: string; initial?: string } = {})
  • get text(): string — The entered letters as one string.
  • up(): void — Steps the letter in the current slot forward through chars.
  • down(): void — Steps the letter in the current slot backward through chars.
  • left(): void — Moves the cursor one slot to the left.
  • right(): void — Past the last slot commits — which is how the cabinets did it.
  • select(): void — The fire button: advance, committing off the end.

Meter

class Meter

One resource. The reason this is a class and not a number is the EDGES: an arcade game wants "the frame the fuel ran out", not "the fuel is out", which is true for every frame after and re-triggers the sound sixty times a second.

  • value: number — The current value, clamped to min..max.
  • max: number — The ceiling. Infinity for an unbounded counter.
  • min: number — The floor. Default 0.
  • rate: number — Signed change per second, applied by update. Live — set it per frame.
  • warn: number — low turns true at or below this. Default: no warning level.
  • onWarn: ((m: Meter) => void) | null — Fired on the frame the value crosses down to warn, and re-armed above it.
  • onEmpty: ((m: Meter) => void) | null — Fired on the frame the value reaches min, and re-armed above it.
  • onFull: ((m: Meter) => void) | null — Fired on the frame the value reaches max, and re-armed below it.
  • constructor(o: MeterOptions = {})
  • get frac(): number — 0..1 against max — what a gauge draws. 0 for an unbounded meter.
  • get low(): boolean — True at or below warn.
  • get empty(): boolean — True at or below min.
  • get full(): boolean — True at or above max.
  • update(dt: number): void — Applies rate over dt seconds. Call once a frame.
  • add(n: number): this — Adds n to the value. A negative n subtracts.
  • set(n: number): this — Sets the value, clamped to min..max, and fires the edge callbacks.
  • spend(n = 1): boolean — Take n if there is n — the ammo/nuke/superzapper test. Returns false and changes nothing when short, so if (nukes.spend(1)) … is the whole verb.
  • reset(n?: number): this — Back to full (or to n), with the edge flags re-armed.

Players

class Players<A>

The player slots of a game, with turn order and teams. Create one with createPlayers.

  • readonly slots: PlayerSlot<A>[] — Every slot, in index order.
  • turn: number — Whose turn it is, in hotseat play.
  • constructor(count: number, opts: { lives?: number; teams?: number[] } = {})
  • get active(): PlayerSlot<A> — The slot whose turn it is.
  • get allOut(): boolean — True when every slot is out of lives.
  • next(): PlayerSlot<A> | null — Next slot with lives left. Returns null when everyone is out.
  • team(n: number): PlayerSlot<A>[] — Returns every slot on team n.
  • get leader(): PlayerSlot<A> — Highest score, for the game-over card.
  • switchNearest(slot: PlayerSlot<A>, actors: A[], to: { x: number; y: number }, at: (a: A) => { x: number; y: number }): A | null — Hand a slot the nearest of actors — the sports "switch to the man closest to the ball", and the reason control ownership is separate from the actor.

PlayerSlot

class PlayerSlot<A>

A player is not an actor. Hotseat play, control switching in a sports game and a ship respawning all depend on the SLOT outliving whatever it is driving.

  • readonly index: number — The position of this slot, from 0.
  • readonly score: Meter — The score meter for this slot.
  • readonly lives: Meter — The lives meter. Default value 3.
  • readonly rounds: Meter — Rounds won — Space Wars plays to ten.
  • team: number — The team number. Default: the slot index.
  • actor: A | null — What this slot is currently driving, or null.
  • constructor(index: number, opts: { lives?: number; team?: number } = {})
  • get out(): boolean — True when this slot has no lives left.
  • take(actor: A | null): A | null — Take control of an actor, releasing whatever it held.

ResourceBase

class ResourceBase<K extends string>

The shared methods of a Resources object. Create one with createResources.

  • get all(): Record<string, Meter> — Every meter by name — for a HUD that iterates rather than names them.
  • get(name: K): Meter — Returns the meter named name.
  • add(name: string, opts: MeterOptions = {}): Meter — Add a meter after construction (a phase-local timer, say).
  • award(rule: AwardRule<K>): this — Adds an award rule. give fires when the watched meter crosses a threshold.
  • update(dt: number): void — Steps every meter and tests the award rules. Call once a frame.
  • _install(defs: Record<string, MeterOptions>): void

Types

AwardRule

interface AwardRule<K extends string>

Give an extra life / ball / nuke at score thresholds.

  • of: K — Which meter to watch.
  • every?: number — Award every N (10000 → at 10000, 20000, 30000…).
  • at?: number[] — …or at exactly these values.
  • give(res: Resources<K>): void — Called at each threshold. Grant the award here. A threshold the meter falls back below fires again when it is crossed again, so restarting a game by setting the score to 0 re-arms every award.

CueDef

interface CueDef

One cue: its subtitle, sound, priority and timing.

  • text?: string — Subtitle. Read cues.text and draw it.
  • audio?: string — Passed to the play callback — a sound key, a sample name, anything.
  • priority?: number — Higher interrupts lower. Equal or lower is dropped while one is holding.
  • cooldown?: number — Seconds this cue cannot repeat. Default 0.
  • hold?: number — Seconds the subtitle stays up. Default 2.

CueOptions

interface CueOptions

Configures createCues.

  • play?(audio: string, key: string): void — Called when a cue with audio fires. Play the sound here.

MeterOptions

interface MeterOptions

Configures a Meter.

  • value?: number — The starting value, clamped to the range. Default 0.
  • max?: number — Ceiling. Omit for an unbounded counter (a score).
  • min?: number — Floor, default 0.
  • rate?: number — Signed change per second — negative drains. Default 0.
  • warn?: number — low turns true at or below this. Default: no warning level.
  • onWarn?(m: Meter): void — Fired on the frame the value crosses DOWN to warn, and re-armed above it.
  • onEmpty?(m: Meter): void — Fired on the frame the value reaches min, and re-armed above it.
  • onFull?(m: Meter): void — Fired on the frame the value reaches max, and re-armed below it.

Resources

type Resources<K extends string> = { readonly [P in K]: Meter } & ResourceBase<K>

The named meters plus the ResourceBase methods. Returned by createResources.

ScoreboardApi

type ScoreboardApi = typeof kit

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

ScoreEntry

interface ScoreEntry

One row of the high-score table: a name and a score.

  • name: string — The player name shown in the row.
  • score: number — The score value of the row.