Phaser AE

Track racer

Pro packv3.0.0-32DArcade

The rules of top-down circuit racing on one screen, as a single deterministic session: the track select, the grid, laps and positions, computer drivers, wrenches and upgrades, hazards, crashes, bridges and weapons. Your game sends the steering and the pedal, and draws what the session reports.

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

Released October 14, 2026 · updated October 20, 2026

Track racer: 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

trackRacer

function trackRacer(): Pack<{ trackRacer: typeof api }>

Install the track racer API.

Helpers

BADLANDS_LOOKS

const BADLANDS_LOOKS: CarLook[]

Badlands' cars: blue (left player), red (right player or a drone), green (always a drone).

BADLANDS_TRACKS

const BADLANDS_TRACKS: CircuitDef[]

The eight arenas in the manual's order: City, Desert, Freeway, Volcano, Junkyard, Military Base, Prison Yard, Refinery.

badlandsPreset

function badlandsPreset(o: BadlandsOptions = {}): TrackRacerDef

The Badlands game definition.

badlandsVehicle

function badlandsVehicle(): VehicleSpec

The Badlands car: vehicle-handling's badlandsCar, livelier off the line (the arenas are short: about 0 to 100 pixels a second in a second, as the champions' 7 to 12 second laps need), with a crash speed suited to the arenas' tight walls and short, snappy jumps.

createTrackRacer

function createTrackRacer(def: TrackRacerDef, settings: Partial<RacerSettings> = {}): TrackRacer

Create a session from a definition and settings.

FIGURE_EIGHT

const FIGURE_EIGHT: CircuitDef

A figure-eight with one bridge, to show the layer model: the diagonal from top-left to bottom-right is a deck (level 1) joined to the ground at its two ends (the transition zones); the other diagonal passes under it. Not a Super Sprint track; a demonstration level in the same style.

pointInPolygon

function pointInPolygon(pts: ReadonlyArray<Pt>, x: number, y: number): boolean

Point in polygon (even-odd).

SUPER_SPRINT_LOOKS

const SUPER_SPRINT_LOOKS: CarLook[]

Super Sprint's car slots: blue (left wheel), red (centre), yellow (right), green (always a drone).

SUPER_SPRINT_TRACKS

const SUPER_SPRINT_TRACKS: CircuitDef[]

The eight circuits in track-number order (track 1 first).

superSprintPreset

function superSprintPreset(o: SuperSprintOptions = {}): TrackRacerDef

The Super Sprint game definition.

superSprintVehicle

function superSprintVehicle(): VehicleSpec

The Super Sprint car: vehicle-handling's superSprintCar re-paced to the lap times in the Winner's Circle captures (a 6.8 s average and 5.9 s best lap on track 1, about 1,400 pixels a lap): the arcade cars cross the 512-pixel screen in under two seconds, so top speed, acceleration and grip are raised together; the handling shape (rotate with momentum, understeer, slides) is the pack's. The wheel also turns a car that is standing still (pinned nose-first against a wall it would otherwise never get free), and turns the same way when a wall bounce rolls the car backwards (there is no reverse gear to steer).

Functions

badlandsUpgrades

function badlandsUpgrades(cost = 1): UpgradeDef[]

The shop: the manual's five custom car options.

chooseNext

function chooseNext(c: Circuit, road: number, open: (road: number) => boolean, rand: () => number, skill: DroneSkill): number | null

Choose the next road at the end of road.

droneDrive

function droneDrive(v: DroneView): { steer: number; gas: number; fire: boolean }

One step of a drone's controls.

newDriverMemory

function newDriverMemory(road: number, phase: number): DriverMemory

A fresh memory for a drone on road.

superSprintUpgrades

function superSprintUpgrades(levels = 5): UpgradeDef[]

The upgrade screen: CHOOSE AN ITEM FOR YOUR CAR.

updateRoute

function updateRoute(c: Circuit, mem: DriverMemory, road: number, d: number, ahead: number, open: (road: number) => boolean, rand: () => number, skill: DroneSkill): void

Keep the route starting at the car's road and at least ahead long past it.

Classes

Circuit

class Circuit

A baked circuit. Build it once per track; it is immutable apart from gate and block state, which the race owns and passes in.

  • readonly def: CircuitDef — The definition the circuit was baked from.
  • readonly track: Track — The track network the roads were baked from.
  • readonly roads: Road[] — The baked roads, in the order of def.edges.
  • readonly lapLength: number — Length of the main lap.
  • readonly mainLap: number[] — Road indices of the main lap in order, starting at the line.
  • readonly toFinish: Map<string, number> — Distance from each node to the line along the main lap.
  • readonly levels: number[] — Levels in use, ascending.
  • constructor(def: CircuitDef)
  • remaining(road: number, d: number): number — Distance left to the line from distance d along road road (main-lap metric; a shortcut drops it).
  • project(road: number, x: number, y: number): Projection — The nearest point of one road.
  • pointAt(road: number, d: number, lat = 0): { x: number; y: number; heading: number; hw: number } — Centre-line point, heading and half width at distance d along a road, offset lat to the right.
  • candidates(road: number, x?: number, y?: number): number[] — Roads a car on road at (x, y) is tested against: every road of its own level, plus a road of another level only inside the transition zone where the two meet. A car under a bridge never meets the deck, and a car on the deck never meets the road below, wherever they overlap on screen.
  • inJoint(road: number, to: number, x: number, y: number): boolean — Is (x, y) inside a transition zone of road leading to road to?
  • transitions(): Array<{ x: number; y: number; r: number; from: number; to: number }> — Every transition zone (for debugging overlays): centre, radius and the two levels.
  • decks(): Array<{ level: number; points: Array<{ x: number; y: number }> }> — The outlines of every road on a level above the ground (bridge decks and their ramps), for occlusion.
  • locate(road: number, x: number, y: number): Projection — Track a point that was on road last step: the road it is on now (staying put through junction overlaps, moving to the next road past a node, or to any road of its level when it has left its own: a cut across).
  • inside(x: number, y: number, r: number, among: number[], level?: number): boolean — Is the point inside the drivable union of roads among with a margin r?
  • wallHit(road: number, x: number, y: number, rad: number, gates: ReadonlyMap<string, boolean>, liveBlocks: ReadonlyArray<BlockDef>): WallHit | null — Wall contact for a circle of radius rad at (x, y) on road road: the union edge, closed gates and solid blocks of its level. gates maps gate ids to open state; blocks lists the live blocks.
  • grid(count: number): Array<{ x: number; y: number; heading: number; road: number }> — The default or authored grid slots, front first.
  • line(): { x: number; y: number; heading: number; hw: number } — The start/finish line: centre, heading of travel and half width.
  • turnAhead(route: number[], d: number, dist: number): number — Signed heading change over the next dist units of a route starting at d on road (curvature probe).
  • routePoint(route: number[], d: number, lat: number): { x: number; y: number; heading: number; hw: number; road: number } — A point d along a route of roads (the first road starting at 0), offset lat to the right.
  • ribbon(road: number, caps = true): Array<{ x: number; y: number }> — Drawable outline of one road's ribbon (left side out, right side back), for filling; caps: false squares the ends.
  • walls(): WallLine[] — The union boundary of each level as wall polylines (each point carries the unit normal toward the road).
  • randomSpot(rand: () => number, margin: number): { x: number; y: number; road: number } — A random point on the main lap at least margin from the walls, using rand() in [0, 1).

TrackRacer

class TrackRacer

A session of a track-racer game. Create with createTrackRacer.

  • readonly def: TrackRacerDef — The game definition the session was made from.
  • readonly settings: RacerSettings — The settings in force: difficulty, seed and any drone or hazard override.
  • readonly circuits: Circuit[] — Baked circuits, by index in def.circuits.
  • state!: RacerState — Everything the presenter reads; plain JSON, replaced by restore.
  • constructor(def: TrackRacerDef, settings: Partial<RacerSettings> = {})
  • reset(): void — Back to attract mode with a demo race on the first circuit.
  • get circuit(): Circuit — The circuit being raced.
  • vehicle(car: number): Readonly<VehicleState> — A car's full vehicle state.
  • spec(car: number): VehicleSpec — A car's scaled vehicle spec (after upgrades).
  • operator(): { drones: OperatorLevel; hazards: OperatorLevel } — The operator settings in force.
  • standings(): RacerCar[] — Cars by race position, leader first.
  • gateOpen(id: string): boolean — Gate open?
  • roadOpen(road: number): boolean — Is a road passable now: its gate (if any) open and no destructible block still standing across it?
  • liveBlocks(): BlockDef[] — Blocks currently solid (authored blocks still standing plus temporary debris).
  • patches(): PatchDef[] — Surface patches currently on the track (authored, spills and patch hazards), topmost last.
  • droneSkill(): DroneSkill — The effective skill of a drone this heat.
  • drain(): RacerEvent[] — Events since the last drain.
  • join(seat: number): void — Start a game for seat (from attract), or join one in progress at the next race.
  • newGame(seats: number[]): void — Begin a new game for the given seats.
  • step(input: RacerInput | ReadonlyArray<RacerInput> = {}): void — Advance one fixed step with each seat's controls (a single input drives seat 0).
  • upgradeCost(seat: number, id: string): number | null — Cost of the next level of an upgrade, or null when it is maxed.
  • buy(seat: number, id: string): boolean — Buy the next level of an upgrade for a seat (the shop's confirm).
  • qualified(seat: number): boolean — Did this seat qualify in the last race?
  • snapshot(): RacerSnapshot — Plain JSON of the whole session.
  • restore(snap: RacerSnapshot): void — Restore a snapshot taken from a session with the same definition.

Constants

BADLANDS_DIFFICULTY

const BADLANDS_DIFFICULTY: Record<RacerDifficulty, { drones: OperatorLevel; hazards: OperatorLevel; assist: RacerDifficulty; catchUp: number }>

Easy / Normal / Arcade for Badlands. Arcade is the manual's recommended Moderate.

BADLANDS_DRONES

const BADLANDS_DRONES: Record<OperatorLevel, DroneSkill>

Drone skill per the operator's Difficulty Level (Easy / Moderate / Hard / Hardest), estimates.

BADLANDS_SURFACES

const BADLANDS_SURFACES: Record<string, SurfaceSpec>

Lava (the Volcano): slows hard and spins a car that drives in fast.

SUPER_SPRINT_DIFFICULTY

const SUPER_SPRINT_DIFFICULTY: Record<RacerDifficulty, { drones: OperatorLevel; hazards: OperatorLevel; assist: RacerDifficulty; catchUp: number }>

What Easy, Normal and Arcade mean for Super Sprint. Arcade is the operator's recommended Medium / Medium.

SUPER_SPRINT_DRONES

const SUPER_SPRINT_DRONES: Record<OperatorLevel, DroneSkill>

Drone skill per operator Drone Difficulty switch (estimates; the manual names the four settings only).

Types

AreaDef

interface AreaDef

Extra drivable surface: a polygon joined to the roads of its level (a wide apron, a sweep round an island).

  • points: ReadonlyArray<Pt> — Outline, any winding.
  • level?: number — Level (default 0).
  • surface?: string — Surface id (default tarmac).

BadlandsOptions

interface BadlandsOptions

Options for badlandsPreset.

  • laps?: number — Laps per race (manual: four).
  • cars?: number — Cars per race (three).
  • seats?: number — Player seats (two wheels).
  • circuits?: CircuitDef[] — Circuits and their order (default the eight arenas in the manual's order).
  • order?: number[] — The circuit cycle, as indices into circuits; the race goes through them in turn.
  • missiles?: number — Missiles each player starts a game with (estimate).
  • wrenchesPerItem?: number — Wrenches per shop item (estimate; the manual gives no prices).
  • dronesFire?: boolean — Drones fire their cannons.
  • vehicle?: VehicleSpec — The car, in metres.

BlockDef

interface BlockDef

A solid obstacle: a convex or concave polygon on one level.

  • id?: string — Optional id (a destructible block is addressed by it).
  • points: ReadonlyArray<Pt> — Outline, any winding.
  • level?: number — Level (default 0).
  • hp?: number — Hit points if weapons can destroy it (Badlands walls opened by missiles); omit for indestructible.
  • score?: number — Score when destroyed.

CarLook

interface CarLook

A car slot: its colour and the name the HUD shows.

  • id: string — Stable id (blue, red, yellow, green).
  • color: string — CSS colour for the presenter.
  • name: string — HUD name for a human driver ("BLUE CAR").

CarVehicle

type CarVehicle = Readonly<VehicleState>

Read-only handle on a car's vehicle state, for presenters that want more than the copied fields.

CircuitDef

interface CircuitDef

A circuit definition: plain data, any number of roads, levels and gates.

  • id: string — Unique id.
  • name: string — Display name.
  • size: { w: number; h: number } — World size (one screen).
  • width: number — Default full road width.
  • nodes: Record<string, Pt> — Named node positions.
  • edges: CircuitEdgeDef[] — Roads, directed in the racing direction.
  • finish: string — The node the start/finish line sits on.
  • grid?: ReadonlyArray<{ x: number; y: number; heading: number }> — Explicit grid slots (front first); default: two by two behind the line.
  • gridSpacing?: readonly [number, number] — Spacing of the default grid: [along, across] in world units.
  • checkpoints?: number — Scoring checkpoints per lap (default 14, the Sprint games' count).
  • areas?: AreaDef[] — Extra drivable aprons.
  • blocks?: BlockDef[] — Solid obstacles.
  • gates?: GateDef[] — Gates.
  • patches?: PatchDef[] — Fixed surface patches.
  • par?: { average: number; best: number } — Par lap times in seconds for the lap bonuses: 100 points per second under par (Winner's Circle).
  • difficulty?: string — Difficulty label shown on the select screen (EASY, MEDIUM, HARD, VERY HARD).
  • startWrenches?: number — Bonus wrenches for starting a game here.
  • spots?: ReadonlyArray<Pt> — Candidate hazard and pickup positions; default: anywhere along the main lap.
  • props?: PropDef[] — Shootable targets, turrets and wrecks (armed games).
  • timed?: TimedEvent[] — Scenery events on timers.
  • data?: Record<string, unknown> — Free data for the presenter (theme, colours, scenery).

CircuitEdgeDef

interface CircuitEdgeDef

One road of a circuit, directed in the racing direction.

  • from: string — Node the road leaves.
  • to: string — Node the road arrives at.
  • via?: PathSegmentData[] — Curve geometry in the path notation, continuing from from; omit for a straight road.
  • width?: number | readonly [number, number] — Full road width, constant or [at from, at to] (default: the circuit's width).
  • level?: number — Level: 0 is the ground, higher is a bridge over it (default 0).
  • kind?: "road" | "ramp" | "gap" — road (default); ramp launches a car that crosses it fast enough; gap is open air: a car on it that is not airborne falls.
  • surface?: string — Surface id under the whole road (default tarmac).
  • shortcut?: boolean — A shortcut: not part of the main lap; progress is measured along the main lap it rejoins.
  • gate?: string — Gate id that closes this road's mouth (the AI only takes the road while it is open).
  • blockedBy?: string — Id of a destructible block that stands across this road until it is destroyed (a wall to blow open).
  • weight?: number — Preference at a branch for computer drivers (default 1).

Debris

interface Debris

A temporary solid block from an effect (a rock fall, a fallen power line).

  • id: number — Unique among the session's wrenches, hazards, shots and spills.
  • block: BlockDef — The block the effect laid.
  • t: number — Seconds since it appeared.
  • life: number — Seconds it lasts.

DriverMemory

interface DriverMemory

A drone's memory between steps (plain JSON, part of the session snapshot).

  • route: number[] — Roads ahead, the first being the one it is on.
  • lane: number — Current lane, fraction of the half width (+ right).
  • passCar: number — Car being passed and the side chosen, until passT runs out.
  • passSide: number — The side chosen to pass on: -1 left, 1 right.
  • passT: number — Seconds of passing left before it settles back into its lane.
  • avoidId: number — Hazard being avoided and the side chosen.
  • avoidSide: number — The side chosen to steer round it on: -1 left, 1 right, 0 to drive straight on.
  • mark: number — Progress mark and time without progress; recovery time left.
  • stuck: number — Seconds without progress; past 1.2 it starts a recovery.
  • recover: number — Seconds of recovery manoeuvre left.
  • phase: number — Phase of its line wobble.
  • lifting: boolean — Gas latch (hysteresis).
  • bias: number — This driver's own preferred lane offset (so a field spreads out).

DroneSkill

interface DroneSkill

How well a computer driver drives. Every field is a plain number so tables can be authored and scaled.

  • pace: number — Fraction of the car's top speed it allows itself (0..1+).
  • corner: number — Fraction of the grip-limited corner speed it carries into a bend (0.6 timid .. 1.1 over-driving).
  • look: number — Look-ahead, seconds of travel (plus a fixed distance).
  • gain: number — Steering gain.
  • wobble: number — Weaving error of its line, fraction of the half width.
  • overtake: number — Tendency to pull out and pass a car ahead, 0..1.
  • shortcut: number — Chance of taking an open shortcut, 0..1.
  • avoid: number — Chance of steering round a hazard it sees, 0..1.
  • fire: number — Chance per second of firing the cannon at a car in its sights (armed games).

DroneView

interface DroneView

What a drone sees when it decides.

  • circuit: Circuit — The circuit being raced.
  • car: RacerCar — The drone's car.
  • state: Readonly<VehicleState> — Its vehicle's state.
  • spec: VehicleSpec — The drone's own (scaled) spec.
  • skill: DroneSkill — Its skill this heat.
  • mem: DriverMemory — Its memory between steps; the decision updates it in place.
  • cars: ReadonlyArray<RacerCar> — Every car in the race, its own included.
  • hazards: ReadonlyArray<Hazard> — The hazards on the track.
  • open: (road: number) => boolean — Is a road passable now (its gate open, no wall standing across it)?
  • rand: () => number — The session's seeded random source: a number from 0 up to but not including 1.
  • dt: number — Seconds of this step.
  • pace: number — Throttle-cap multiplier from the rubber band (1 = none).
  • armed: boolean — The drone's car carries a cannon.

GateDef

interface GateDef

A gate across a road: a wall segment that opens and closes on its own timer.

  • id: string — Id, referenced by CircuitEdgeDef.gate.
  • a: Pt — End points of the closed gate.
  • b: Pt — The other end of the closed gate.
  • level?: number — Level (default 0).
  • open?: boolean — Starts open (default false).
  • openTime: readonly [number, number] — Seconds open, [min, max] chosen at random each time.
  • closedTime: readonly [number, number] — Seconds closed, [min, max].

Hazard

interface Hazard

A hazard on the track.

  • id: number — Unique among the session's wrenches, hazards, shots and spills; events refer to it.
  • kind: string — The id of its HazardKind.
  • x: number — World x of its centre.
  • y: number — World y of its centre.
  • r: number — Radius, world units.
  • level: number — The level it is on.
  • vx: number — Velocity x, world units a second (roamers; 0 for the others).
  • vy: number — Velocity y, world units a second (roamers; 0 for the others).
  • t: number — Seconds since it appeared.
  • life: number — Seconds it lasts before it leaves; Infinity for a patch laid for the whole race.
  • tx: number — World x of the spot a roamer is heading for.
  • ty: number — World y of the spot a roamer is heading for.

HazardKind

interface HazardKind

A hazard kind laid on the track at the start of a race (oil, water) or roaming (tornado).

  • id: string — Id (oil, water, tornado, mud, mine ...).
  • kind: "patch" | "roamer" | "mine" — patch: lies where it is put; roamer: wanders over the track; mine: explodes under a car.
  • radius: number — Radius in world units.
  • surface?: string — Surface a car on it drives on (patches).
  • spins?: boolean — Spins a car that touches it (tornado, mud).
  • speed?: number — Roamer speed, world units per second.
  • life?: number — Roamer life before it leaves, seconds.
  • weight?: number — Relative chance when a hazard is placed.
  • from?: Partial<Record<OperatorLevel, number>> — First heat (0-based count of heats raced) it may appear in, per hazard setting.

OperatorLevel

type OperatorLevel = "easy" | "medium" | "medium-hard" | "hard"

The operator's four-step settings (Super Sprint's drone and track-hazard switches).

PatchDef

interface PatchDef

A fixed patch of another surface (a puddle painted on the track, a sand trap).

  • at: Pt — Centre.
  • rx: number — Radii (an ellipse); ry defaults to rx.
  • ry?: number — Vertical radius, world units. Default rx.
  • surface: string — Surface id.
  • level?: number — Level (default 0).

Projection

interface Projection

The nearest point of a road to a query point.

  • road: number — Index of the road in circuit.roads.
  • d: number — Distance along the road.
  • dist: number — Distance from the centre line.
  • lat: number — Signed lateral offset, positive to the right of the direction of travel.
  • hw: number — Half width there.
  • x: number — The centre-line point.
  • y: number — World y of the centre-line point.
  • heading: number — Road heading there.

PropDef

interface PropDef

A shootable or timed piece of scenery that changes the track (Badlands' animated backgrounds).

  • id: string — Id.
  • at: Pt — Position.
  • r: number — Hit radius.
  • hp?: number — Hits to destroy (cannon 1, missile missileDamage); omit for an indestructible turret.
  • score?: number — Score when destroyed.
  • missiles?: number — Missiles awarded to the destroyer.
  • effect?: PropEffect — What happens when it is destroyed.
  • fire?: { every: number; speed: number; range: number; life?: number } — A turret: fires at the nearest car in range.
  • respawn?: number — Respawn after this many seconds (targets that come back each lap).
  • touch?: "explode" | "solid" — Explodes when a car drives into it (Junkyard wrecks); the car spins out.
  • level?: number — Level (default 0).
  • look?: string — Free data for the presenter (sprite, colour).

PropEffect

interface PropEffect

A change to the track: a spill, a fallen obstacle, a wall knocked down, a blast.

  • patch?: PatchDef & { life?: number } — A surface patch appears for life seconds (water from a tower, an oil spill, lava).
  • block?: BlockDef & { life?: number } — A solid block appears for life seconds (rock fall, a fallen power line).
  • remove?: string — A block is removed (a wall blown open to make a shortcut).
  • blast?: { at?: Pt; radius: number } — A blast that spins every car within radius.

PropState

interface PropState

A prop's live state.

  • id: string — The id of the PropDef it belongs to.
  • hp: number — Hit points left; Infinity for a prop that cannot be destroyed.
  • alive: boolean — False once destroyed, until it respawns (if it does).
  • t: number — Seconds since it was destroyed, counting towards its respawn.
  • cool: number — Seconds until a turret fires again.

Pt

type Pt = readonly [number, number]

[x, y] in world units.

RacerCar

interface RacerCar

A car in the race.

  • id: number — Slot index.
  • look: string — Look id and colour.
  • color: string — CSS colour of the car, from its look.
  • seat: number | null — Human seat, or null for a drone.
  • x: number — Pose and handling (copied from the vehicle every step).
  • y: number — World y, in world units.
  • heading: number — Direction it faces, radians; 0 faces along +x.
  • speed: number — Speed, world units a second.
  • skid: number — Skid intensity, 0 (gripping) to 1 (full skid).
  • spinning: boolean — True during a spin-out.
  • airborne: boolean — True while it is in a jump.
  • z: number — Height above the track in a jump, world units; 0 on the ground.
  • road: number — Road, distance along it, signed lateral offset, level.
  • d: number — Distance travelled along that road, world units.
  • lat: number — Signed lateral offset from the road's centre line, world units; positive to the right of the direction of travel.
  • level: number — The level it is on: 0 the ground, higher a bridge deck.
  • r: number — Distance left to the line on this lap, race progress (laps x lap length + distance done).
  • progress: number — Race progress, world units: laps completed times the lap length plus the distance done on this lap; negative on the grid.
  • lap: number — Lap number in progress (0 on the grid behind the line), next checkpoint, best checkpoint count reached.
  • next: number — The checkpoint it must pass next on this lap, counting from 0.
  • reached: number — The highest checkpoint reached, counted across laps: (lap - 1) x checkpoints + checkpoint.
  • lapStart: number — Race time this lap began, lap times, best lap.
  • laps: number[] — Times of its completed laps, seconds, in order.
  • best: number | null — Its fastest lap, seconds; null before it has completed one.
  • finished: boolean — Finished, finishing place (1-based) and time.
  • place: number — Finishing place, 1 for first; 0 until it has finished.
  • finishTime: number — Race time at which it finished, seconds; 0 until it has.
  • position: number — Live race position (1-based).
  • score: number — Totals across the game.
  • wrenches: number — Wrenches it holds, to spend in the shop.
  • upgrades: Record<string, number> — Levels bought, by upgrade id.
  • status: "grid" | "racing" | "exploding" | "airlift" | "finished" — grid, racing, exploding, airlift, finished.
  • t: number — Seconds in the current status.
  • missiles: number — Armament.
  • shields: number — Shield charges held.
  • shieldTime: number — Seconds of protection left from a shield charge that has been used.
  • cooldown: number — Seconds until the cannon can fire again.
  • heli: { x: number; y: number; fromX: number; fromY: number; toX: number; toY: number; heading: number; phase: "in" | "hover" | "out"; t: number; carried: boolean } | null — The rescue helicopter while it is out for this car.

RacerDifficulty

type RacerDifficulty = "easy" | "normal" | "arcade"

The three difficulty presets every rules pack exposes. Arcade is the original operator defaults.

RacerEvent

type RacerEvent = | { type: "phase"; phase: RacerPhase } | { type: "green" } | { type: "white-flag" } | { type: "checkpoint"; car: number; points: number } | { type: "lap"; car: number; lap: number; time: number } | { type: "finish"; car: number; place: number } | { type: "wrench-spawn"; id: number; x: number; y: number } | { type: "wrench"; car: number; id: number } | { type: "wrench-gone"; id: number } | { type: "skid"; car: number } | { type: "spin"; car: number } | { type: "bump"; a: number; b: number; speed: number } | { type: "wall"; car: number; kind: string; speed: number } | { type: "crash"; car: number; cause: "wall" | "fall" | "missile" | "blast"; x: number; y: number } | { type: "helicopter"; car: number; phase: "in" | "drop" | "out" } | { type: "respawn"; car: number } | { type: "jump"; car: number } | { type: "land"; car: number } | { type: "gate"; id: string; open: boolean } | { type: "hazard"; id: number; kind: string; x: number; y: number } | { type: "hazard-hit"; car: number; kind: string } | { type: "fire"; car: number; kind: "shell" | "missile" | "turret" } | { type: "hit"; car: number; by: number; kind: "shell" | "missile" | "turret"; x: number; y: number } | { type: "shield"; car: number } | { type: "shot-hit"; kind: "shell" | "missile" | "turret"; x: number; y: number; what: "wall" | "block" | "prop" } | { type: "prop"; id: string; x: number; y: number; by: number } | { type: "effect"; x: number; y: number; what: "patch" | "block" | "remove" | "blast" } | { type: "event-warning"; index: number } | { type: "upgrade"; seat: number; item: string; level: number } | { type: "join"; seat: number } | { type: "continue"; seat: number } | { type: "out"; seat: number } | { type: "results" } | { type: "game-over" }

Events for sound and effects, drained by the presenter.

RacerInput

interface RacerInput

One seat's controls for one step. Steering is -1..1 (left, right); gas 0..1 or a boolean.

  • steer?: number — Steering from -1 (full left) to 1 (full right); values outside are clamped. Default 0.
  • gas?: number | boolean — The gas pedal: true, or a number above 0.5, is on; anything else is off. The car is either off the gas or on it.
  • fire?: boolean — Fires the cannon while held (armed games). Default false.
  • missile?: boolean — Fires a missile on each press (armed games). Default false.
  • start?: boolean — The START button: on a press it starts a game from attract or game over, joins a seat, or keeps a beaten player in. Default false.

RacerPhase

type RacerPhase = "attract" | "select" | "grid" | "race" | "results" | "shop" | "continue" | "over"

Session phase.

RacerSettings

interface RacerSettings

Settings chosen for a game (the operator switches and the gallery panel).

  • difficulty: RacerDifficulty — Easy, normal or arcade: picks the drone and hazard settings, the handling assist and the rubber band from TrackRacerDef.difficulties. The session defaults to normal.
  • drones?: OperatorLevel — Override the drone setting the difficulty implies.
  • hazards?: OperatorLevel — Override the hazard setting the difficulty implies.
  • seed: number — Seed.

RacerSnapshot

interface RacerSnapshot

A plain-JSON snapshot of a session. rng is where the session's seeded stream stands (a kernel Rng snapshot).

  • state: RacerState — The session state.
  • vehicles: VehicleSnapshot[] — Each car's vehicle, by car id.
  • rng: RngState — The state of the session's random stream.

RacerState

interface RacerState

The whole session state (plain JSON).

  • phase: RacerPhase — Where the session is: attract, circuit select, grid, race, results, shop, continue or game over.
  • t: number — Seconds in this phase.
  • raceTime: number — Seconds since the green flag.
  • slot: number — Index into order of the circuit being raced, the circuit index, heats raced so far (the level).
  • circuit: number — Index into TrackRacerDef.circuits of the circuit being raced.
  • heat: number — Heats raced so far in this game; raises the drones' skill and the hazards.
  • seats: boolean[] — Human seats in the game, and seats waiting for the next race (PLEASE STAND BY).
  • waiting: boolean[] — Per seat: true while it waits out the race in progress for the next one (PLEASE STAND BY).
  • cursor: number — Select screen cursor (index into order).
  • cars: RacerCar[] — Cars of the current race.
  • keep: Array<{ score: number; wrenches: number; upgrades: Record<string, number>; missiles: number; shields: number } | null> — Totals kept for seats between races (score, wrenches, upgrades, missiles, shields).
  • droneScores: number[] — Drone scores carried between races by slot (the drones keep scores too).
  • wrenches: Wrench[] — Wrenches lying on the track.
  • hazards: Hazard[] — Hazards on the track.
  • shots: Shot[] — Shells, missiles and turret shots in flight.
  • gates: Array<{ id: string; open: boolean; t: number }> — Each gate of the circuit: its id, whether it is open, and the seconds until it changes.
  • blocks: Array<{ id: string; hp: number }> — Live block ids (destructible blocks not yet destroyed), with hit points.
  • props: PropState[] — The live state of each prop of the circuit.
  • spills: Spill[] — Temporary surface patches laid by effects.
  • debris: Debris[] — Temporary blocks laid by effects.
  • nextWrench: number — Timers: next wrench, next roamer, timed events.
  • nextRoam: number — Seconds until the next roaming hazard may appear.
  • nextEvent: number[] — Seconds until each of the circuit's timed events happens, by index in timed.
  • results: Array<{ car: number; place: number; placeBonus: number; average: number | null; averageBonus: number; best: number | null; bestBonus: number }> — Winner's circle order (car ids, first first) and bonus lines.
  • shop: Array<{ cursor: number; done: boolean } | null> — Shop cursors per seat (index into upgrades), done flags.
  • continueFor: Array<number | null> — Continue countdown per seat (null when not offered).
  • leaderLap: number — Leading lap, for the white flag.
  • message: string — A banner the presenter shows ("GO", "WHITE FLAG", "PLEASE STAND BY" ...).
  • nextId: number — Next id for wrenches, hazards, shots and spills.
  • bestEver: number | null — Best lap ever (this session).
  • drivers: Array<DriverMemory | null> — Drone memories by car id (null for human cars).
  • held: Array<{ steer: number; gas: boolean; fire: boolean; missile: boolean; start: boolean }> — Last step's controls per seat, for presses.
  • touching: number[] — Per car: the hazard id it is touching (to report a hit once).
  • finishClock: number | null — Time since the first car finished (null before).
  • demo: boolean — The attract race is running (title screen demo).

Road

interface Road

A road after baking: samples every few units with distance, half width and heading.

  • index: number — Index in circuit.roads.
  • def: CircuitEdgeDef — The edge definition it was baked from.
  • from: string — Node the road leaves.
  • to: string — Node the road arrives at.
  • level: number — Level: 0 the ground, higher a bridge deck.
  • kind: "road" | "ramp" | "gap" — road, a ramp that launches a fast car, or a gap of open air.
  • surface: string — Surface id under the whole road.
  • shortcut: boolean — True for a shortcut: not part of the main lap.
  • gate: string | null — Id of the gate that closes its mouth, or null.
  • blockedBy: string | null — Destructible block across this road, or null.
  • weight: number — Preference at a branch for computer drivers.
  • length: number — Length of the road along its curve, world units.
  • xs: Float64Array — World x of each sample along the centre line.
  • ys: Float64Array — World y of each sample along the centre line.
  • ds: Float64Array — Cumulative distance at each sample.
  • hw: Float64Array — Half width at each sample.
  • next: number[] — Indices of roads leaving to.
  • prev: number[] — Indices of roads arriving at from.
  • touching: number[] — Roads sharing a node with this one.
  • joints: Array<{ road: number; x: number; y: number; r: number }> — Transition zones: roads of ANOTHER level sharing a node with this one, and the zone round that node.
  • rEnd: number — Distance from the end of this road to the line along the main lap.

Shot

interface Shot

A cannon shell or missile (or a turret shot: owner -1).

  • id: number — Unique among the session's wrenches, hazards, shots and spills.
  • kind: "shell" | "missile" | "turret" — A cannon shell, a missile or a turret shot.
  • owner: number — The id of the car that fired it; -1 for a turret.
  • x: number — World x of the shot.
  • y: number — World y of the shot.
  • vx: number — Velocity x, world units a second.
  • vy: number — Velocity y, world units a second.
  • t: number — Seconds it has flown.
  • level: number — The level it flies on; it hits only cars and walls of that level.
  • road: number — Index of the road the firer was on (a turret: the target car's road).

Spill

interface Spill

A temporary patch or block from an effect.

  • id: number — Unique among the session's wrenches, hazards, shots and spills.
  • patch: PatchDef — The surface patch the effect laid.
  • t: number — Seconds since it appeared.
  • life: number — Seconds it lasts.

SuperSprintOptions

interface SuperSprintOptions

Options for superSprintPreset. Everything defaults to the arcade operator's recommended settings.

  • laps?: number — Laps per race (manual: four).
  • cars?: number — Cars per race (four); drones fill the seats players leave.
  • seats?: number — Player seats (three wheels).
  • wrenchesPerBonus?: number — Wrenches per car bonus (operator switch: 2, 3, 4 or 5; shipped at 3).
  • upgradeLevels?: number — Levels of each car bonus (five).
  • circuits?: CircuitDef[] — Circuits (default the eight) and their cycle (default 1, 3, 7, 6, 2, 5, 8, 4 as indices).
  • order?: number[] — The circuit cycle, as indices into circuits; the race goes through them in turn.
  • select?: boolean — First player chooses the starting track (true).
  • vehicle?: VehicleSpec — The car, in metres.
  • qualify?: "beat-drones" | "win" — Who goes on: every player must beat every drone (the arcade), or only win.

TimedEvent

interface TimedEvent

A scenery event on a timer (the mountain rumbles, lava spills).

  • every: readonly [number, number] — Seconds between events, [min, max].
  • after?: number — First event no earlier than this many seconds into the race.
  • effect: PropEffect — The effect.
  • warn?: number — Seconds of warning before it happens (an event-warning is emitted).

TrackRacerDef

interface TrackRacerDef

The whole game: cars, circuits, laps, scoring, wrenches, upgrades, drones, hazards and timings.

  • name: string — Game name.
  • seats: number — Human seats (Super Sprint 3, Badlands 2).
  • cars: number — Cars in every race; drones fill the seats humans leave.
  • looks: CarLook[] — Car slots in order; human seat n drives slot n, drones the rest.
  • circuits: CircuitDef[] — Circuits.
  • order: number[] — The circuit cycle (indices into circuits); after the last it starts again at a higher level.
  • select: boolean — The first player picks the starting circuit from order.
  • laps: number — Laps per race.
  • unitsPerMetre: number — World units per metre for the vehicle spec.
  • vehicle: VehicleSpec — The car, in metres (a vehicle-handling spec). Drones drive the same car.
  • surfaces?: Record<string, SurfaceSpec> — Extra surfaces (lava ...), metres.
  • qualify: "beat-drones" | "win" — Who goes on: beat-drones (every human must finish ahead of every drone) or win.
  • continues: boolean — Can a beaten human continue (the add-a-coin)?
  • timing: { select: number; ready: number; finishGrace: number; results: number; shop: number; continue: number } — Timings in seconds.
  • scoring: { /** Points per checkpoint passed in order. */ checkpoint: number; /** Place bonuses, first first. */ place: number[]; /** Points per second under the circuit's par for the average and best lap. */ perSecond: number; /** Default par lap times when a circuit has none. */ par: { average: number; best: number }; } — Scoring.
  • wrenches: { /** Wrenches per upgrade. */ cost: number; /** Seconds between appearances \[min, max]`. / interval: readonly [number, number]; /* Seconds one stays on the track. / life: number; /* Most on the track at once. / max: number; /* Pick-up radius. / radius: number; /* Bonus wrenches for the winner of a race (Badlands: one). / firstPlace: number; /* Drones can pick wrenches up (they take them away). */ dronesCollect: boolean; }` — Wrenches.
  • upgrades: UpgradeDef[] — Upgrade menu.
  • drones: { /** Skill per operator setting. */ skill: Record<OperatorLevel, DroneSkill>; /** Added to \pace` and `corner` per heat raced (drones of increasing skill). / perHeat: number; /* Cap on the per-heat increase. / maxHeatBonus: number; /* Rubber band: pace change per lap of distance from the leading human (0 = none). */ catchUp: number; }` — Drones.
  • hazards: { kinds: HazardKind[]; /** Patches at the start of a race per setting: \base + per heat`, capped. / count: Record<OperatorLevel, { base: number; perHeat: number; max: number }>; /* Roamers: seconds between appearances. */ roamEvery: readonly [number, number]; }` — Hazards.
  • crash: { /** Seconds of explosion before the helicopter sets off. */ explode: number; /** Helicopter speed, world units per second. */ heliSpeed: number; /** Seconds the helicopter hovers to lower the car. */ hover: number; /** Wall impacts at or above the vehicle's \impact.crash` speed explode the car. */ walls: boolean; }` — Crashes and the rescue helicopter.
  • jump: { minSpeed: number; up: number } — Jumps: a ramp launches a car moving at least minSpeed (world units/s) upward at up world units/s (a fixed hop: the faster the car, the further it flies).
  • weapons?: WeaponsDef — Armament (Badlands).
  • difficulties: Record<RacerDifficulty, { drones: OperatorLevel; hazards: OperatorLevel; assist: RacerDifficulty; catchUp: number }> — What Easy, Normal and Arcade mean: operator settings, handling assist, rubber band.
  • dt: number — Seconds per simulation step.

UpgradeDef

interface UpgradeDef

One item on the between-races upgrade screen.

  • id: string — Id (traction, speed, turbo, score, missiles, shields ...).
  • label: string — Screen label.
  • levels?: number — Maximum level; omit for unlimited (Increase Score, missiles).
  • cost?: number — Wrenches per purchase (default wrenches.cost).
  • effect: UpgradeEffect — Effect per level.

UpgradeEffect

interface UpgradeEffect

What one level of an upgrade does. Multipliers compound per level: grip: 0.1 is +10% a level.

  • grip?: number — Cornering grip (Super Traction, Badlands Tires).
  • accel?: number — Acceleration (Turbo Acceleration, Badlands Turbo).
  • top?: number — Top speed (Higher Top Speed, Badlands Speed).
  • score?: number — Points added at once (Increase Score).
  • missiles?: number — Missiles added at once.
  • shields?: number — Shield charges added at once.

WallHit

interface WallHit

A wall push: unit normal toward the drivable area and penetration depth.

  • normal: { x: number; y: number } — Unit vector pointing from the wall toward the drivable area.
  • depth: number — How far the car's circle sinks into the wall, world units.
  • what: "edge" | "block" | "gate" — What was hit.
  • id?: string — Block or gate id when it was one.

WallLine

interface WallLine

A drawable wall polyline on one level, with the side the road is on.

  • level: number — The level the wall is on.
  • points: Array<{ x: number; y: number; nx: number; ny: number }> — The vertices in order, each with the unit normal (nx, ny) that points toward the drivable area.

WeaponsDef

interface WeaponsDef

Badlands-style armament. Omit weapons for an unarmed racer.

  • cannon: { interval: number; speed: number; life: number; radius: number; slow: number } — Front cannon: held to fire. Hits slow the target down.
  • missile: { speed: number; life: number; radius: number; damage: number; start: number } — Missiles: one per press, destroy a car or damage a block.
  • shield: { time: number; start: number } — A shield charge absorbs one shot, missile or crash, then protects for time seconds.
  • dronesFire: boolean — Computer drivers fire their cannons.

Wrench

interface Wrench

A wrench lying on the track.

  • id: number — Unique among the session's wrenches, hazards, shots and spills; events refer to it.
  • x: number — World x of the wrench.
  • y: number — World y of the wrench.
  • level: number — The level it lies on.
  • t: number — Seconds it has lain there; it goes at wrenches.life.