The shell every page arcade game repeats, so a game is only its rules and its art. One engine per run over the host page (@fzlt/core/board-page: its words are terrain, its buttons are targets), a fixed overlay with a canvas and a scoreboard in the Board UI kit (title, sound, pause, fold and exit keys, readouts, an optional meter, action keys, a comms toast), keys and pointer (@fzlt/core/input), the touch pad on coarse pointers (@fzlt/core/pad), sound from the event ring (@fzlt/core/sound), sparks and stacking score popups, factory health painted into the buttons, and a controller a hub drives: start, pause, resume, stop, destroy, getSnapshot. While a run is live, a click on the page fires instead of following a link; the scoreboard, fields and [data-virus-ignore] / [data-fizlet-ignore] keep their clicks. Recall (Escape, the exit key, stop) destroys the engine, so every letter and button comes back exactly; a run that ends keeps the scoreboard with the result until Recall. The game supplies create(ctx): its own systems on ctx.engine, reading ctx.ctl (written in the read phase) and drawing in render. The scoreboard and controls are a HUD preset, chosen per game with hud (BOARD-UI.md §19): arcadeHud() is today's card and the default (no hud renders it byte for byte); skirmishHud({...}) is Battlefield Blitz's top bar with a clock, rank and armor bar, a feed, a banner, aim toggles and counted keys, and its flow (countdown, pause / surrender, result) on @fzlt/core/match: then match owns the phase, the shell asks it to pause, resume or end and follows it (HudView.ask, HudRun.go). Each preset is its own export, so a game ships only the one it names.
Every export with its signature, parameters, return type, doc comment and status flag, generated at build time from the published type declarations of v0.21.3 by the TypeScript compiler. For AI agents: the whole API as plain text.
skirmishHud function COMPLETE
The skirmish preset (Battlefield Blitz): hud: skirmishHud({ readouts: [{ label: "Kills", tone: "team" }, { label: "Time", kind: "clock" }, { label: "Armor", kind: "bar", low: 0.3 }] }). hud(out) fills the readouts in order, then any count slots.
export declare function skirmishHud(o: SkirmishOptions): ArcadeHud;
SkirmishOptions interface COMPLETE
Options for the skirmish HUD preset: its readouts, header and nav keys, feed, banner, overlays, result, aim toggles and key counts.
export interface SkirmishOptions {
/** Readouts, in the order `hud(out)` fills them. */
readouts: SkirmishReadout[];
/** Header keys, in order (default full screen, sound, pause, exit; `fold` hides the title and the
* text keys). Full screen shows only where the page may use it. */
header?: ("fullscreen" | "sound" | "pause" | "fold" | "exit")[];
/** Text keys after the actions, as on the card (default none): `next` (Next gate, G), `restart`,
* `auto` (Auto fire: needs `fire`). */
nav?: ("next" | "restart" | "auto")[];
/** A feed of the last `rows` (default 4) events of type `on`, newest first. */
feed?: {
rows?: number;
on: string;
line: (a: number, b: number) => string;
};
/** A centred banner (title, sub) on each event of type `on`. */
banner?: {
on: string;
text: (a: number, b: number) => readonly [string, string?];
};
/** Overlays (default all four; @fzlt/core/match runs the flow either way). `countdown` holds the start
* for 3, 2, 1 on the flow's clock; without `pause` or `result` that phase shows no overlay; without
* `surrender` the pause card has no SURRENDER key. */
overlays?: ("pause" | "surrender" | "countdown" | "result")[];
/** The result card: `stats()` keys to list, and the one whose best is kept on this device. */
result?: {
stats: string[];
best?: string;
};
/** Aim toggles: the intents `aim-auto` and `aim-lock` (`ctx.input.down("aim-lock")`). */
aim?: ("auto" | "lock")[];
/** Counted keys: action id → the `hud(out)` slot of its count, a badge on its keys. */
counts?: Record<string, number>;
}
SkirmishReadout interface COMPLETE
One readout on the skirmish bar: its label, how the value shows and its tone.
export interface SkirmishReadout {
label: string;
/** How the value shows: `num` (default) 12 · `clock` 3:05 from seconds · `rank` #2 · `pct` 75%
* from 0..1 · `bar` a meter from 0..1 (armor, health). */
kind?: "num" | "clock" | "rank" | "pct" | "bar";
tone?: "team" | "enemy";
/** A bar: class `low` at or under this (0..1). */
low?: number;
}
Controls and view, written once per frame in the read phase (a step reads them): mode (0 keys or stick, 1 pointer), pointer x, y and on, move x, y (-1..1), fire, viewport width and height, and reduced motion (1/0). The game writes TARGET_HP: the full health of a target, for its paint.
What create gets: the run's engine and board, the controls, and the shell's presentation.
export interface ArcadeContext {
engine: Engine;
board: BoardPage;
input: Controls;
/** MODE … TARGET_HP (see the constants). */
ctl: Float64Array;
reduced(): boolean;
/** A popup at (x, y): `+value` when `text` is null (stacked with nearby ones), else the text.
* Tone 0 score, 1 accent (upgrade), 2 hurt. Call it from render (react). */
popup(x: number, y: number, value: number, text: string | null, tone?: 0 | 1 | 2): void;
/** n sparks from (x, y) in colour `c` (an index into `colors`); none under reduced motion. */
spark(x: number, y: number, n: number, c: number): void;
/** A line in the comms toast. */
say(text: string, ms?: number): void;
/** onEvent for the hub (a plain object: call it from render, not a step). */
emit(type: string, data?: Record<string, unknown>): void;
/** In-page art: a shadow root in an overlay inside `el` (an absolutely positioned host that
* follows the page's layout, scrolling and transforms natively). A static `el` becomes
* relative. Through the ledger: Recall removes it and puts the style back. Call from create
* or reset (it reads computed style), never from a step. Null for replaced elements. */
attach(el: Element): ShadowRoot | null;
}
ArcadeGame interface COMPLETE
One run of your game, as the shell drives it: reset, draw, the HUD and meter, page scroll, reactions to events, stats, and whether the run is over.
export interface ArcadeGame<S> {
/** A fresh run (the viewport is in ctl[VW], ctl[VH]). */
reset(): void;
/** 0 playing, 1 lost, 2 won: read every render; the run ends the frame it turns non-zero. */
over(): number;
/** The page scrolled by (dx, dy): move page-space things (read phase). */
scroll?(dx: number, dy: number): void;
/** Each event on the ring this frame (render): popups, sparks, the toast, onEvent. */
react?(ev: FizEvent): void;
/** Draw on the canvas (render), after the target beacons, before sparks and popups. */
draw(ctx: CanvasRenderingContext2D, dt: number): void;
/** The readout values, in `readouts` order, then the meter's change key (render, change-only). */
hud(out: Float64Array): void;
/** The meter's left text, right text and fill 0..1, when its change key moved. */
meter?(): readonly [string, string, number];
/** The snapshot's game fields (not a hot path). */
stats(): S;
}
ArcadeDef interface COMPLETE
Your game for arcade(): its id, title, accent, hint, CSS, actions, readouts, sound cues and create(), which makes a run.
export interface ArcadeDef<S> {
id: string;
title: string;
/** The game's accent (hex): title, meter, popups, target paint at full health. */
color: string;
hint: string;
touchHint?: string;
/** The Board UI kit CSS text (the game bakes in the sections it uses). */
css: string;
/** Hold Space / the pointer to fire; a Fire key on the touch pad and an Auto fire key. */
fire?: boolean;
actions?: ArcadeAction[];
/** Touch move control (default "stick"; "dpad" for grid games). */
pad?: "stick" | "dpad";
/** Keys (or the stick) only: the mouse never steers. */
keyboard?: boolean;
/** The card's readouts: label, tone, how the value shows, and whether it hides when the board is
* folded. A preset with its own readouts (skirmish) leaves this out. */
readouts?: [label: string, tone?: "team" | "enemy" | undefined, fmt?: ((v: number) => string) | undefined, fold?: boolean | undefined][];
meter?: {
label: string;
};
/** Spark colours, by index. */
colors: string[];
/** Event type → sound cue. */
cues: Record<string, Cue>;
/** Paint targets with their health (default true). */
paint?: boolean;
/** The scoreboard and controls: a HUD preset (default `arcadeHud()`, today's card). */
hud?: ArcadeHud;
create(ctx: ArcadeContext): ArcadeGame<S>;
}
ArcadeHud interface COMPLETE
A HUD preset: its CSS (after the game's kit CSS) and a mount, once per run.
export interface HudRun {
/** The kit root (`.bui`), the canvas already in it. */
ui: HTMLElement;
def: ArcadeDef<any>;
engine: Engine;
input: Controls;
/** A coarse pointer: build the touch pad. */
coarse: boolean;
/** The game, once `create` returned: read it in `write`, never in `mount`. */
game: ArcadeGame<any>;
/** Sound (a scoreboard key flips `on`). */
sfx: {
on: boolean;
};
/** Aborts at Recall: the overlay's life. */
signal: AbortSignal;
status(): ArcadeStatus;
/** Moves the run, for a HUD that owns its flow (`ask`), once its phase changed: pause, resume,
* the end (won, lost), a rematch (restart) or Recall (exit). */
go(to: ArcadeFlow | "restart" | "exit"): void;
}
ArcadeFlow type COMPLETE
A change in a run's flow the HUD is told about: paused, resumed, won or lost.
A mounted HUD. Keys are button[data-a] (sound, pause, exit, restart, gate, auto, fold, or the HUD's own) and button[data-act] (an action): the shell runs them, then calls key.
export interface HudView {
/** The comms toast. */
msg: HTMLElement;
/** Every render and once at start: change-only writes, nothing allocated in the steady state. */
write(): void;
/** The status changed: the pause key, overlays. */
sync(): void;
/** A scoreboard key ran: repaint its face (`auto`: auto fire is on). */
key(a: string, b: HTMLElement, auto: boolean): void;
/** At start, in place of starting the engine: a countdown that calls `go` when it is done. */
begin?(go: () => void): void;
/** Before the engine goes at the end of a run (its ledger takes back what went through it). */
hold?(): void;
/** The run ended and the page is back: show the result. */
end(won: boolean, notice: string, stats: Record<string, unknown>): void;
/** The HUD owns the phase (skirmish: `@fzlt/core/match`): the shell's pause, resume and end are asked of
* it, and it answers with `HudRun.go` (one owner; the shell keeps the keys and delegates). */
ask?(to: ArcadeFlow): void;
}
ArcadeHudOptions interface COMPLETE
Options for the default HUD: which header keys and which nav keys, in order.
export interface ArcadeHudOptions {
/** Header keys, in order (default all four). */
header?: ("sound" | "pause" | "fold" | "exit")[];
/** The nav row after the action keys (default all three; `auto` needs `fire`). */
nav?: ("next" | "restart" | "auto")[];
}
arcadeHud function COMPLETE
The arcade preset, the default: one card with the title, sound / pause / fold / exit keys, the readouts, one meter, the nav row (actions, Next gate, Restart, Auto fire) and the toast; phones get the pad and the card folded at the top. arcadeHud() renders exactly what leaving hud out renders. Values: hud(out) fills the readouts in order, then the meter's change key.
export declare function arcadeHud(o?: ArcadeHudOptions): ArcadeHud;
arcade function COMPLETE
The shell every page arcade game repeats: one engine per run over the host page (its words are terrain, its buttons targets), a Board UI overlay with a canvas and scoreboard, input, sound and the run's flow. def is only the game's rules and art.