Phaser AE

Sky circuit

Pro packv3.0.0-12DArcade

Kart racing on circuits in the air: short closed tracks that climb on stilts, bank, dive and pass over and under themselves, raced against a full field. There are no guard rails, so a kart that misses a bend falls to the ground and is put back on the road. Finish high enough to race the next track.

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

Released October 19, 2026

Sky circuit: 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

skyCircuit

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

Install the sky circuit API.

Helpers

CLOUD_CUP_RACERS

const CLOUD_CUP_RACERS: RacerDef[]

Cloud Cup's six racers (original characters).

cloudCupPreset

function cloudCupPreset(o: PresetOptions = {}): SkyCircuitConfig

Cloud Cup, an original configuration of the same rules: one course of three circuits floating 20 to 80 m over a cloud floor, barriers on every edge (nobody falls), six racers, three laps, only a WIN goes on to the next circuit, the player starts last, no catch-up, a floaty low-gravity kart and kickers that send it soaring; prizes for the top three and no extra stage.

cloudKart

function cloudKart(): VehicleSpec

The cloud kart: Power Drift's buggy-kart at well under half its weight in the air, so every crest is a long float.

createSkyRace

function createSkyRace(config: SkyCircuitConfig): SkyRace

Create a sky-circuit race.

DIFFICULTIES

const DIFFICULTIES: readonly Difficulty[]

The three levels in order.

GIRDER_TOWERS

const GIRDER_TOWERS: TowerDef

Red lattice support towers beside the elevated decks (Power Drift's girder pylons).

POWER_DRIFT_RACERS

const POWER_DRIFT_RACERS: RacerDef[]

Power Drift's twelve racers (Sega Retro's list; the looks from the promotional video's portraits). The arcade's drivers make no difference to the kart (Japanese Wikipedia); skill is each one's pace as a RIVAL here, an estimate (no source documents the rivals' driving).

powerDriftCourses

function powerDriftCourses(): CourseDef[]

Power Drift's five courses (A to E) of five tracks (names from Wikipedia and Arcade History).

powerDriftExtra

function powerDriftExtra(course: string, vehicle: VehicleSpec): ExtraDef

Power Drift's extra stage for a course: five wins earn it; the kart morphs into After Burner's F-14 on courses A, C and E and into Hang-On's motorbike on B and D (Wikipedia EN/JA, Sega Retro, the frames). The machine is drawn differently; it drives the track like the kart (the arcade's morph is a picture).

powerDriftPreset

function powerDriftPreset(o: PresetOptions = {}): SkyCircuitConfig

Power Drift: twelve karts, four laps, finish third or better to race the next of the course's five tracks (else the game is over); first in all five earns the extra stage. The player starts fifth on the grid of two abreast (every stage in the longplays). No time limit. Score runs with speed (about 900 a second at 244 km/h), plus a PRIZE BONUS for a win and a TIME BONUS at every finish (A1: 42,640 for a win, 28,920 for second; the formula is an estimate fitted to those). Easy and Normal soften the rivals' pace; Arcade is the estimate of the original.

RIBBON_STEP

const RIBBON_STEP: 4

Metres between the ribbon's vertices.

SKY_STEP

const SKY_STEP: number

The fixed simulation step (seconds).

trackOf

function trackOf(id: string, laps?: number): TrackDef

A track from its file: towers where the look has girders.

TRACKS

const TRACKS: Record<string, RoadDocument>

The road files of this folder, by name.

validate

function validate(c: SkyCircuitConfig): void

Throw with a clear message when a configuration cannot run.

Classes

SkyRace

class SkyRace

A sky-circuit race. Create with createSkyRace(config).

  • readonly config: SkyCircuitConfig — The configuration the race was built from.
  • state: SkyState — Everything the presenter reads; updated by each step.
  • track!: Track — The track being raced; replaced when the next track or the extra stage loads.
  • field!: DepthField — The depth field of the current track: its scenery, towers, karts and shadows; replaced with the track.
  • constructor(config: SkyCircuitConfig)
  • get courseDef(): CourseDef — The course being raced.
  • get karts(): readonly KartState[] — Every kart's rules state (the player's included), in field order (racer order).
  • get player(): KartState — The player's kart.
  • get vehicle(): Vehicle — The player's vehicle (gear, revs, speed for the HUD).
  • get speedDisplay(): number — Speed in the vehicle's display units (km/h).
  • get lapProgress(): number — The player's share of the lap, 0..1 (for the course map).
  • lapProgressOf(id: string): number — Every kart's share of the lap, by racer id (rival dots on the map).
  • autoInput(): SkyInput — The rivals' driver at the wheel of the player's kart: the input it would give now (pace, the line, braking for bends, passing; it shifts the box itself). An attract-mode demo, or a hands-off test drive. Feed it to tick.
  • start(): void — Start (from attract): the signals, then the race.
  • tick(dt: number, input: SkyInput = {}): void — Advance by dt seconds of wall time (fixed steps inside, at most 0.25 s per call).
  • get droppedEvents(): number — Events discarded from the drain queue because it was full (eventLimit), since the race was created.
  • drain(): SkyEvent[] — Events since the last drain, oldest first; at most eventLimit (1024 by default): if it was not drained in time the oldest were discarded.
  • scene(): SkyScene — What to draw now (road-view-pseudo3d's ribbon scene, plus the focus its chase camera follows).
  • step(input: SkyInput = {}): void — One fixed step.
  • snapshot(): SkySnapshot — A plain copy of the whole race.
  • restore(s: SkySnapshot): void — Restore a snapshot taken from a race with the same config.

Track

class Track

A circuit ready to race.

  • readonly def: TrackDef — The definition the track was built from.
  • readonly road: RoadData — The loaded road-data document.
  • readonly path: RoadPath — The lap driven as a path (curved frame), laid lazily as karts drive further.
  • readonly lap: number — Lap length (metres).
  • readonly ribbon: RibbonLeg — The lap laid in world space (closed).
  • readonly index: RibbonIndex — Spatial index of the deck, so scenery stays off every stretch of a track that crosses itself.
  • constructor(def: TrackDef)
  • ensure(z: number): void — Lay the road far enough to sample z (laps are laid lazily as the karts drive them).
  • env(z: number, bounds: number, keepOn = false): RoadEnv — vehicle-handling's environment at course distance z. The deck edges include the shoulders (the kerbs). Over the ground a kart may run bounds metres off the road; beside a drop nothing holds it (it falls); keepOn (the rivals) holds it on the deck instead.
  • scenery(): ScenePlacement[] — The lap's scenery (road-data pools and landmarks over one lap), with anything that would stand on another stretch of the deck dropped: a track passing over or beside itself must not grow trees through its bridges.
  • towers(): { x: number; y: number; z: number; w: number; h: number; kind: string }[] — The support towers of the lap (def.towers): one object per storey, standing on the ground beside the deck.
  • inLap(z: number): number — Course distance within the lap (0..lap).

Types

ByDifficulty

type ByDifficulty<T> = T | { easy: T; normal: T; arcade: T }

A value, or one value per difficulty.

ContactDef

interface ContactDef

Kart-to-kart contact.

  • rearLoss: number — Speed lost (fraction) by the kart running into the back of another; the one hit gains half of it.
  • side: number — Lateral knock (m/s) when two karts touch side by side, and how long it holds (s).
  • sideTime: number — Seconds the sideways knock holds.
  • spinAbove?: number | null — A side hit above this closing speed (m/s) spins the kart that was hit (null: never).

CourseDef

interface CourseDef

A course: tracks raced in order.

  • id: string — Stable id of the course; SkyCircuitConfig.course names one.
  • name: string — Name shown on the course select screen.
  • tracks: TrackDef[] — The tracks raced, in order.

Difficulty

type Difficulty = "easy" | "normal" | "arcade"

Difficulty levels: arcade is the original tables; normal (the gallery default) and easy are kinder.

EffectsDef

interface EffectsDef

The objects the race spawns itself. Only the kart shadow is spawned today; any other key is refused by validate.

  • shadow?: { w?: number; h?: number } — The shadow under each kart (kind shadow): width, and height of the object (metres). Default the kart's w x 1.05 and 0.3.

ExtraDef

interface ExtraDef

The reward stage for winning every race of a course.

  • when: "all-wins" | "complete" — When it is given: all-wins (first in every race of the course) or complete (every race qualified).
  • track: TrackDef — The track raced, and its laps.
  • laps: number — Laps of the extra stage.
  • vehicle: VehicleSpec — The vehicle driven on it (a different machine).
  • kind: string — The player's sprite kind on it.
  • finish: number[] — Points per position at its finish (it does not end the game whatever the result).

FallDef

interface FallDef

Falling off the deck.

  • time: number — Seconds from leaving the deck until the kart stands on the road again.
  • restartSpeed: number — Speed it is put back at (fraction of its top speed).
  • restart?: "centre" | "lane" — Where it is put back, at the course distance it left: centre of the road, or lane, the side of the deck it left from, one kart width in from the edge (the lateral place it was in if that was already further in). Default centre.

GridDef

interface GridDef

The grid: rows of karts behind the start line.

  • columns: number — Karts per row.
  • spacing: number — Metres between rows.
  • from: number — Metres from the start line to the first row.
  • player: ByDifficulty<number> — The player's slot (0 = pole, field - 1 = last), by difficulty.

KartState

interface KartState

A kart in the race (rules state; its body is field object obj).

  • id: string — Racer id.
  • obj: string — Field object id.
  • z: number — Course distance (z) and lateral position.
  • x: number — Lateral position, metres right of the road's reference line.
  • y: number — Height above the road (a jump, or below the deck when it has fallen).
  • speed: number — Forward speed, m/s.
  • lap: number — Laps completed.
  • place: number — Place, 1 = leading.
  • player: boolean — The player's kart.
  • finished: boolean — Finished the race (and its finishing place and time).
  • finishPlace: number | null — The place it finished in, 1 = first; null until it has finished.
  • finishTime: number | null — Race time, seconds, at which it finished; null until it has.
  • turn: number — Steering -1..1 (for turn frames).
  • airborne: boolean — True while the kart is in the air (a jump, or a fall off the deck).

PresetOptions

interface PresetOptions

Options for the presets.

  • course?: string — The course to race, an id from the preset's courses: a to e for Power Drift (default a); Cloud Cup has one course and ignores it.
  • difficulty?: Difficulty — The difficulty level. Default normal.
  • seed?: number — The config's seed. Default the preset's own (1988 for Power Drift, 7 for Cloud Cup).
  • player?: string — The player's racer.

RacerDef

interface RacerDef

One racer of the field: who they are, what they drive and how well.

  • id: string — Stable id (the sprite kind is kart-<id> unless kind is given).
  • name: string — Name shown on the select screen and the results.
  • kind?: string — Sprite kind of the kart. Default kart-<id>.
  • colours?: Record<string, string> — Colours for the presenter's art (body, driver, helmet...): free data.
  • skill: number — How quick this racer is as a rival: 0..1 of the rival speed range (RivalDef.speed). The arcade's rivals differ in pace; a strong rival leads, a weak one tails off.
  • data?: Record<string, string | number | boolean> — Free data (nationality, the kart's look, a catch phrase).

ResultRow

interface ResultRow

One race's result row.

  • id: string — The racer's id.
  • name: string — The racer's name.
  • place: number — Finishing place, 1 = first.
  • time: number | null — Race time in seconds, or null if the racer had not finished when the table was made.
  • player: boolean — True for the player's row.

RivalDef

interface RivalDef

The rivals' racing.

  • speed: ByDifficulty<[number, number]> — Cruising speed range as a fraction of the kart's top speed: a racer of skill s cruises at lerp(min, max, s).
  • corner: number — Share of the kart's own corner speed (vehicle-handling's cornerSpeed) the rivals take bends at.
  • look: number — Seconds of road they read ahead for corners and karts in the way.
  • accel?: number — The most acceleration (m/s^2) a rival's driver asks of the throttle: the pedal is eased so the kart's drive, at its current speed and gear, does not exceed it. Omit for the kart's own full drive (the presets do). The kart's engine sets what is possible; this only holds a driver back, which is also how a slow-starting field is made. Not used with a throttle.mode of target, where the pedal sets a speed rather than a force.
  • brake?: number — The hardest braking (m/s^2) a rival's driver asks for: the brake pedal is limited so the kart's deceleration does not exceed it. Omit for the kart's own full brake (the presets do). Held back like this a driver overshoots bends unless corner is lowered or look raised.
  • catchUp?: ByDifficulty<{ gap: number; ahead: number; behind: number } | null> — Catch-up: a rival more than gap metres ahead of the player eases off to ahead of its pace, one more than gap behind pushes to behind (the arcade keeps the pack round the player). Null: none.
  • line: number — Fraction of the half-width a rival's racing line may use (1 = kerb to kerb).
  • weave: number — Lateral speed limit of a rival's line change (m/s).
  • gap: number — Rivals keep this far (metres) from a kart ahead in their line before moving out to pass.

ScoringDef

interface ScoringDef

Scoring.

  • distance: number — Points per metre driven on the road while racing (the arcade's score runs with distance).
  • finish: number[] — PRIZE BONUS for finishing a race, by position (1st first); missing places score 0.
  • time?: { pace: number; points: number } | null — TIME BONUS at the finish: points per second the race time is under par, par being pace seconds per kilometre of the race (the arcade adds one to every finisher; 0 when slower).
  • pass?: number — Points per rival passed, awarded while racing for every place the player gains beyond the best place held so far this race (the grid place at the start). Places lost and won back score nothing, so only the net gain over a race is paid, never twice. Default 0.

SkyBookkeeping

interface SkyBookkeeping

A race's private bookkeeping, as a snapshot holds it: where in the cup it is, lap and scoring progress, and timers. Read it only to store it; restore puts it back.

  • course: number — Course index in config.courses.
  • track: number — Track index in the course, or -1 on the extra stage.
  • extra: boolean — The race is the course's extra stage.
  • lapStart: number — The player's racing distance at the last lap line.
  • lapsDone: number — Laps the player has finished (for lap events).
  • scored: number — The player's scored distance so far (for distance points).
  • wins: number — Wins on this course (for the extra stage).
  • fallTime: number — Seconds of the current fall.
  • overSaid: boolean — The game-over event has been sent.
  • order: string[] — Finishing order so far (racer ids).
  • heading: number — Integrated road heading under the player.
  • best: number — The best (lowest) place the player has held in this race, from the grid: places beyond it earn scoring.pass.

SkyCircuitConfig

interface SkyCircuitConfig

One whole configuration: courses of tracks and the race rules.

  • name: string — Shown in a panel.
  • difficulty: Difficulty — The difficulty level: picks the per-difficulty values of the rivals' pace, catch-up and grid slot, and the assist on the player's kart.
  • seed: number — Seed for each rival's lane bias: the same seed gives the same field.
  • courses: CourseDef[] — The courses the player chooses from.
  • course: string — The course raced (an id from courses).
  • startTrack?: number — The track of the course to start on (0-based; practice from a later track). Default 0.
  • laps: number — Laps per race (a track's own laps wins).
  • racers: RacerDef[] — The field, the player's racer included.
  • player: string — The player's racer (an id from racers).
  • qualify: number — Finish in this position or better to race the next track; otherwise the game is over.
  • vehicle: VehicleSpec — The kart (vehicle-handling spec, metres): the player's and every rival's.
  • assist?: boolean — Apply vehicle-handling's assist for the difficulty to the player's kart. Default true.
  • surfaceSpecs?: Record<string, SurfaceSpec> — Extra vehicle surfaces the tracks name.
  • kart: { w: number; h: number; d: number; hit?: number } — Kart body (metres): drawn width w, collision width hit, height and length.
  • bounds: number — How far beyond the deck edges a kart may drive where the road is on the ground (metres).
  • countdown: number — Seconds of start signals (the lights) before GO.
  • signals: number — Lights shown during the countdown.
  • grid: GridDef — Where the karts start.
  • rivals: RivalDef — How the rivals race.
  • contact: ContactDef — What happens when karts touch.
  • fall: FallDef — What happens when the player's kart leaves the deck.
  • scoring: ScoringDef — How points are earned.
  • finishTime: number — Seconds of the finish (the result banner) before the next race or the end.
  • extra?: ExtraDef | null — The reward stage, or null.
  • effects?: EffectsDef — Sizes of the objects the race spawns itself, apart from the scenery and the karts. Default none: the defaults below.
  • eventLimit?: number — Most events the race keeps for drain(). A game that never drains would otherwise grow the queue forever; when it is full the oldest events are discarded (droppedEvents counts them). Whole number >= 0. Default 1024.

SkyEvent

type SkyEvent = | { type: "start"; track: number; name: string; extra: boolean } | { type: "signal"; n: number; of: number } | { type: "go" } | { type: "lap"; lap: number; time: number } | { type: "final-lap" } | { type: "jump"; id: string } | { type: "land"; id: string; impact: number } | { type: "bump"; a: string; b: string; side: boolean; speed: number } | { type: "spin"; id: string } /** The player's kart hit a solid roadside thing (its kind) at this speed (m/s). */ | { type: "hit"; kind: string; speed: number } | { type: "edge"; side: number } | { type: "fell"; drop: number } | { type: "back-on" } | { type: "place"; place: number; was: number } | { type: "finish"; place: number; time: number; qualified: boolean; prize: number; timeBonus: number } | { type: "results"; rows: ResultRow[] } | { type: "next"; track: number } | { type: "extra" } | { type: "game-over" } | { type: "complete" } | { type: "vehicle"; event: string }

What happened (drain each step).

SkyInput

interface SkyInput

Input for one step.

  • throttle?: number — Throttle pedal, 0 (off) to 1 (full); values outside are clamped. Default 0.
  • brake?: number — Brake pedal, 0 (off) to 1 (full); values outside are clamped. Default 0.
  • steer?: number — Steering, -1 (full left) to 1 (full right); values outside are clamped. Default 0.
  • gear?: number | "up" | "down" — The shifter: 0 LOW, 1 HIGH (a lever position), or 'up' / 'down'.

SkyPhase

type SkyPhase = "attract" | "countdown" | "racing" | "falling" | "finish" | "results" | "over" | "complete"

Race phases.

SkyScene

interface SkyScene

What scene() returns: the data road-view-pseudo3d's ribbon view draws (structural, no view import).

  • ribbon: RibbonLeg — The track laid in 3D (road-data's RibbonLeg).
  • field: DepthField — Every object: scenery, karts, shadows (course coordinates).
  • focus: { z: number; x: number; y: number; turn: number; yaw: number } — The kart the camera follows: course z, lateral x, height above the road, steering, yaw on the road.
  • style: string | null — The look of the road under the focus (a region look key), or null.
  • heading: number — Integrated heading of the road under the focus (for backdrop scroll).

SkySnapshot

interface SkySnapshot

A complete saved race: plain data.

  • state: SkyState — The state the presenter reads.
  • internal: SkyBookkeeping — The race's private bookkeeping: the track and course being raced, lap line, wins, fall timer and the like.
  • karts: { st: KartState; vehicle: VehicleSnapshot; pace: number; bias: number; line: number; pose: number; obj: string; shadow: string }[] — Every kart: its rules state, its vehicle's snapshot and its driver's pace, lane bias, line and pose.
  • field: DepthFieldSnapshot — The depth field holding the scenery, karts and shadows.
  • events: SkyEvent[] — Events raised and not yet drained.
  • acc: number — Seconds of time carried towards the next fixed step.

SkyState

interface SkyState

Everything the presenter reads.

  • phase: SkyPhase — Where the game is: attract before start, then countdown, racing, falling (off the deck, waiting to be put back), finish (the result banner), results (the table), and at the end over (did not qualify) or complete.
  • phaseTime: number — Seconds spent in the current phase.
  • course: string — The course and the track being raced (1-based within the course), and whether this is the extra stage.
  • track: number — The track being raced, 1-based within the course; one more than tracks on the extra stage.
  • tracks: number — Tracks in the course, not counting the extra stage.
  • extra: boolean — True while the extra stage is being raced.
  • trackName: string — The name of the track being raced.
  • lap: number — Lap being driven (1-based, capped at laps) and the race length.
  • laps: number — Laps in this race: the track's own laps, else the config's (the extra stage's own for the extra stage).
  • lapTime: number — Time on this lap, the last lap and this race.
  • lastLap: number | null — Seconds the last finished lap took; null before the first.
  • raceTime: number — Seconds since the green light, across all laps; counts while racing or falling.
  • place: number — The player's place now (1 = leading).
  • places: number[] — Places of every race finished on this course (1-based).
  • score: number — The player's points: distance driven, rivals passed, and the prize and time bonuses.
  • topScore: number — The highest score reached so far.
  • signal: number — Lights lit in the countdown (0..signals) and GO.
  • results: ResultRow[] | null — The last race's result table (null before one ends).
  • bonus: { prize: number; time: number } | null — The PRIZE and TIME bonuses of the last finish (null before one).
  • qualified: boolean | null — The player qualified for the next race (set at the finish).
  • falls: number — Times the player has fallen off, and jumps flown.
  • jumps: number — Jumps flown by the player's kart.
  • sinceLap: number — Seconds since the last lap ended (for the LAP TIME banner).

TowerDef

interface TowerDef

Support towers stood beside an elevated deck (Power Drift's red lattice towers): every spacing metres where the deck is at least min above the ground, one each side, gap outside the deck edge, stacked from the ground in storeys of storey metres up to above metres over the deck.

  • kind: string — Field object kind of each tower storey (what the presenter's sprite table is keyed by).
  • spacing: number — Course metres between one pair of towers and the next.
  • storey: number — Height of one storey, metres.
  • w: number — Width and depth of a tower, metres.
  • gap: number — Metres between the outer edge of the deck's shoulder and the tower.
  • min: number — The least height of the deck above the ground, metres, at which towers are stood.
  • above: number — Metres the stack rises above the deck.

TrackDef

interface TrackDef

One track: a closed lap in a road-data file (a close, level section whose successor is itself).

  • id: string — Stable id of the track (the preset tracks use their road file's id, such as a1).
  • name: string — Name shown for the track in panels and banners (SkyState.trackName).
  • road: RoadDocument — The circuit. Its start section is the lap; defaults.ground sets the ground plane the deck stands over; scenery kinds with on: "ground" stand on it; wall features (upright) are barriers the kart cannot fall past; ramp features launch it; the regions' look keys pick the presenter's style.
  • laps?: number — Laps (overrides the config's).
  • towers?: TowerDef | null — Support towers beside elevated decks, or none.
  • data?: Record<string, string | number | boolean> — Free data for the presenter (the look, the music).