Phaser AE

Scaler runner

Pro packv3.0.0-32DArcade

The rules of a third-person into-the-screen shooter: a hero who flies or runs on his own plane, holds fire for rapid fire and dodges or shoots whatever the world throws at him, through stages of scenery, formations and bosses. It runs on the `scaled-depth` world and decides what its objects mean.

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

Released October 14, 2026 · updated October 19, 2026

Scaler runner: 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

scalerRunner

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

Install the scaler-runner (Space Harrier) rules API.

Helpers

createRunner

function createRunner(config: RunnerConfig): ScalerRunner

Create a runner game from a configuration.

crystalCanyonKinds

function crystalCanyonKinds(): Record<string, RunnerKind>

The Crystal Canyon object table.

crystalCanyonPreset

function crystalCanyonPreset(o: CrystalCanyonOptions = {}): RunnerConfig

The Crystal Canyon configuration.

crystalCanyonStages

function crystalCanyonStages(): RunnerStage[]

The three Crystal Canyon stages.

curve

function curve(fn: (t: number) => { x: number; y: number; z: number }, duration: number, samples = 24, loop = false): DepthPathSpec

Keys sampled from a parametric curve over duration seconds.

harrierGround

function harrierGround(light: string, dark: string, dimLight: string, dimDark: string, haze: string, height = 0): DepthSurface

The Space Harrier ground: column stripes by depth bands in four colours, fading to the haze at the horizon.

keys

function keys(list: [number, number, number, number][], smooth = true): DepthPathSpec

Keys from [t, x, y, z] tuples.

mirrorPath

function mirrorPath(p: DepthPathSpec): DepthPathSpec

The mirrored copy of a path (x negated).

RUNNER_STEP

const RUNNER_STEP: number

Simulation step (seconds).

runnerDifficulty

function runnerDifficulty(d: RunnerDifficulty): RunnerTuning

Difficulty multipliers: Arcade is the original tables.

runnerPaths

function runnerPaths(depthScale = 0.62): Record<string, DepthPathSpec>

The path library shared by the Space Harrier preset and other configurations. depthScale scales every depth in front of the camera (the arcade's formations work 10 to 60 units out; behind-the-camera depths are kept).

spaceHarrierBosses

function spaceHarrierBosses(): Record<string, RunnerBoss>

The seven bosses that come back in the stage 18 rush, by name.

spaceHarrierKinds

function spaceHarrierKinds(): Record<string, RunnerKind>

The Space Harrier object table. Land objects 5,000, sky creatures and formation targets 10,000.

spaceHarrierPreset

function spaceHarrierPreset(o: SpaceHarrierOptions = {}): RunnerConfig

The Space Harrier preset (arcade order, Normal by default).

spaceHarrierProjectiles

function spaceHarrierProjectiles(): Record<string, RunnerProjectile>

Enemy projectiles.

spaceHarrierStages

function spaceHarrierStages(): RunnerStage[]

The 18 arcade stages.

validate

function validate(c: RunnerConfig): void

Check a configuration and throw a clear error for anything unsupported.

windowOpen

function windowOpen(w: RunnerWindow | undefined, age: number): boolean

Is a periodic window open at age?

Classes

ScalerRunner

class ScalerRunner

A Super Scaler runner game (see the pack docs).

  • readonly config: RunnerConfig — The configuration the game was created with.
  • readonly camera: DepthCamera — The view: its pose is set every step; draw with it.
  • readonly field: DepthField — Every object in the world: the runner, his shots, scenery, enemies, enemy shots and boss parts.
  • state: RunnerState — The whole game state (plain data); replaced on a restore, so read it afresh each frame.
  • constructor(config: RunnerConfig)
  • get stageDef(): RunnerStage — The stage being played.
  • get playerObject(): DepthObject — The runner's field object.
  • kind(name: string): RunnerKind | undefined — A kind's definition (undefined for the runner, shots and projectiles).
  • get timeLeft(): number | null — Seconds shown by TIME (the longest formation or boss timer running), or null.
  • start(stage = this.config.startStage ?? 0): void — Start a new game (from startStage unless given).
  • tick(seconds: number, input: RunnerInput = {}): void — Advance by seconds in whole 1/60 s steps (at most 10 per call).
  • drain(): RunnerEvent[] — Notifications since the last drain.
  • screenToTarget(sx: number, sy: number): { x: number; y: number } — The runner target (world x, feet y) under a screen point on his plane, for pointer control.
  • snapshot(): RunnerSnapshot — Plain copy of the game.
  • restore(s: RunnerSnapshot): void — Restore a snapshot taken from a game with the same configuration.
  • step(input: RunnerInput = {}): void — One fixed step.
  • addScore(points: number): void — Add points (extra lives and the TOP score follow).

Types

BossPart

type BossPart = SerpentPart | CorePart | SquadPart

A boss part.

CorePart

interface CorePart

A core (face, mothership, sorcerer) with an optional orbiting shield.

  • type: "core" — Always "core": what tells this part from the other kinds of boss part.
  • kind: string — The id of the core's kind in RunnerConfig.kinds.
  • path: string — Registered path for the core (camera frame).
  • hits: number | null — Core hits; null for an invulnerable core (Stanley).
  • shield?: { kind: string; count: number; radius: number; /** Radians per second (positive clockwise on screen). */ spin: number; /** The core cannot be hurt while any shield unit lives (Barbarian, Wi Wi Jumbo). */ first?: boolean; /** When the core dies, surviving units fly at the runner (the Ida boss). */ release?: boolean; } — Orbiting shield units.
  • launches?: RunnerWave[] — Launches waves while it is present (seconds after the core appears as at).
  • throws?: { projectile: string; every: number } — Throws ground obstacles at the runner (Wi Wi Jumbo): a projectile every every seconds.

CrystalCanyonOptions

interface CrystalCanyonOptions

Options for crystalCanyonPreset.

  • difficulty?: RunnerDifficulty — "easy", "normal" or "arcade". Default "normal".
  • lives?: number — Lives at the start, before the difficulty's extra lives are added. Default 2.
  • hits?: number — Hits per life (default 3).
  • startStage?: number — First stage, 1-based. Default 1.
  • seed?: number — Seed for the game's random stream. Default 77.

RunnerBoss

interface RunnerBoss

A boss encounter.

  • name: string — The boss's name, shown on screen and reported in events.
  • parts: BossPart[] — The serpents, cores and squadrons that make up the encounter.
  • points: number — Points for defeating it (heads and cores; squad units score their kind's points).
  • timer?: number | null — Seconds before it leaves undefeated (TIME on screen); null: it stays until defeated.

RunnerBossState

interface RunnerBossState

A boss in progress.

  • name: string — The boss's name.
  • index: number — Its place in the stage's bosses, from 0.
  • time: number — Seconds since the boss appeared.
  • timer: number | null — Seconds left before it leaves undefeated; null if it stays until defeated.
  • parts: string[] — Field ids of every live part.
  • tracks: { t: number; x: number; y: number; z: number }[][] — Serpent head tracks (camera frame), one per serpent part.
  • launched: number[] — Launches made so far by each core part, indexing its launches list.
  • defeated: boolean — Whether it has been defeated.
  • left: boolean — Whether it has left undefeated.

RunnerConfig

interface RunnerConfig

A full game.

  • seed: number — Seed for the game's random stream (scenery, spawn jitter, volleys); the same seed and inputs replay the same game.
  • lives: number — Lives at the start, before tuning.lives is added.
  • view: RunnerView — The camera and projection.
  • player: RunnerPlayer — How the runner moves.
  • gun: RunnerGun — The runner's cannon.
  • kinds: Record<string, RunnerKind> — Every kind of obstacle, enemy and boss part, by id.
  • projectiles: Record<string, RunnerProjectile> — Every enemy projectile, by id.
  • paths: Record<string, DepthPathSpec> — Paths by id, camera frame (x right, y up, z depth). Every path also gets a mirrored id~m.
  • stages: RunnerStage[] — The stages in play order.
  • startStage?: number — Index of the first stage (default 0).
  • ending: "end" | "loop" — After the last stage: end (game complete) or loop back to the first.
  • scoring: RunnerScoring — Scoring and extra lives.
  • timing: RunnerTiming — How long each state lasts.
  • tuning: RunnerTuning — The difficulty multipliers (see runnerDifficulty).

RunnerDifficulty

type RunnerDifficulty = "easy" | "normal" | "arcade"

Easy / Normal / Arcade (Arcade = the original tables).

RunnerEvent

type RunnerEvent = | { kind: "stage-start"; stage: number; number: number; name: string; bonus: boolean } | { kind: "ready" } | { kind: "boss-start"; name: string } | { kind: "boss-damage"; id: string; stage: number } | { kind: "boss-defeated"; name: string; points: number; x: number; y: number; z: number } | { kind: "boss-left"; name: string } | { kind: "shot"; id: string } | { kind: "hit"; id: string; target: string; x: number; y: number; z: number } | { kind: "deflect"; id: string; target: string; x: number; y: number; z: number } | { kind: "destroyed"; id: string; target: string; points: number; x: number; y: number; z: number; by: "shot" | "smash" | "boss" | "trip" } | { kind: "enemy-fire"; id: string; projectile: string; x: number; y: number; z: number } | { kind: "player-hit"; cause: string; fatal: boolean } | { kind: "trip"; cause: string } | { kind: "respawn" } | { kind: "extra-life"; lives: number } | { kind: "wave"; index: number; enemy: string } | { kind: "stage-clear"; stage: number; name: string } | { kind: "bonus-tally"; smashed: number; points: number } | { kind: "game-over"; score: number } | { kind: "complete"; score: number }

Notifications for presentation.

RunnerFire

interface RunnerFire

An enemy's firing schedule.

  • projectile: string — Projectile id.
  • every: number — Seconds between volleys.
  • first?: number — Seconds after spawning before the first volley (default every).
  • jitter?: number — Random extra delay up to this many seconds per volley (default 0).
  • burst?: number — Projectiles per volley (default 1) and the delay between them (default 0.12 s).
  • burstGap?: number — Seconds between the projectiles of a burst. Default 0.12.
  • spread?: number — Lateral spread between projectiles of a volley, world units at the target (default 0).
  • minDepth?: number — Only fire while at least this deep / at most this deep (defaults 12 and the view's far).
  • maxDepth?: number — Only fire while at most this deep (world units ahead of the camera). Default the view's far.
  • whenOpen?: boolean — Only fire while the vulnerability window is open.

RunnerGun

interface RunnerGun

The runner's cannon.

  • every: number — Seconds between shots while fire is held (continuous rapid fire).
  • speed: number — Shot speed relative to the runner (units per second).
  • range: number — Distance a shot travels before it fades.
  • max: number — Most shots in flight.
  • size: number — Shot box size (world units).

RunnerInput

interface RunnerInput

Player input for one step.

  • x?: number — Stick, -1 .. 1 (x right, y UP).
  • y?: number — Stick vertical, -1 (down) .. 1 (up).
  • target?: { x: number; y: number } | null — Absolute target for the runner (world units), from a pointer or an analog stick; overrides x/y.
  • fire?: boolean — Fire held.

RunnerKind

interface RunnerKind

Anything in the world that is not the runner: obstacles, enemies, boss parts, bonus scenery.

  • w: number — Box size (world units).
  • h: number — Box height (world units).
  • d?: number — Box depth (world units). Default the smaller of w and h.
  • anchor?: "base" | "centre" — base: positioned by its bottom (things on the ground); centre (default).
  • hp: number | null — Shots needed; null: indestructible (shots are blocked by it).
  • points: number — Points when destroyed.
  • lethal: boolean — Touching it costs a life.
  • trip?: boolean — Touching it while on the ground trips the runner (bushes); flying over it is safe.
  • deflects?: boolean — Shots bounce off it without damage even though it has hp (a dragon's body).
  • smash?: boolean — Smashed by the bonus mount for the stage's per-object bonus.
  • window?: RunnerWindow — Only damaged while open.
  • fire?: RunnerFire — Fires at the runner.
  • family?: string — Counts as a sky creature (10,000) or ground object for presentation; free text.

RunnerPlayer

interface RunnerPlayer

How the runner moves.

  • w: number — Box width, height and depth (world units); the anchor is his feet.
  • h: number — Box height (world units).
  • d: number — Box depth (world units).
  • x: [number, number] — Lateral range [min, max] of his centre.
  • y: [number, number] — Height range [min, max] of his feet; min is the ground when run is true.
  • speedX: number — Digital-stick speeds (units per second).
  • speedY: number — Vertical speed of the digital stick, in units per second.
  • approach: number — Speed toward an absolute target (analog stick or pointer), units per second.
  • run: boolean — He can run on the ground at y[0] (bushes trip him there).
  • trip: number — Seconds a trip stalls him.
  • hits: number — Hits per life: 1 is the arcade's one-touch death; more is an energy bar.
  • invulnerable: number — Seconds of invulnerability after a respawn or a non-fatal hit.

RunnerPlayerState

interface RunnerPlayerState

The runner.

  • x: number — Centre x and feet height (world units).
  • y: number — Height of his feet (world units).
  • vx: number — Lateral velocity (units per second, positive right).
  • vy: number — Vertical velocity (units per second, positive up).
  • lean: number — -1 .. 1: leaning left/right with his lateral speed (sprite pose).
  • grounded: boolean — On the ground (running).
  • tripped: number — Seconds left of a trip.
  • invulnerable: number — Seconds left of invulnerability.
  • hits: number — Hits left in this life.
  • alive: boolean — Whether he is alive; false from the fatal hit to the respawn.
  • dying: number — Seconds into the death fall.

RunnerProjectile

interface RunnerProjectile

An enemy projectile.

  • w: number — Box width (world units).
  • h: number — Box height (world units).
  • speed: number — Speed (units per second, relative to the camera).
  • homing?: number — Turn rate toward the runner in rad/s (0: straight at where he was).
  • commit?: number — Homing stops this far in front of the runner's plane, so a late sidestep dodges it (default 8).
  • shootable?: boolean — Can be shot down (points when it is).
  • points?: number — Points for shooting it down; only used when shootable.
  • life?: number — Seconds before it fizzles (default 6).

RunnerScoring

interface RunnerScoring

Scoring and lives.

  • tick: number — Points added every tickEvery seconds of play (the score creeps up as the runner keeps moving).
  • tickEvery: number — Seconds between those score ticks.
  • extraLives: number[] — Scores that award an extra life, in order.
  • extraEvery: number | null — After the list, another life every this many points (null: none).
  • topScore: number — The TOP score shown before anyone beats it.

RunnerSnapshot

interface RunnerSnapshot

A snapshot of a running game.

  • state: RunnerState — The game state, a deep copy.
  • field: DepthFieldSnapshot — Every object in the world, as the depth field saves it.

RunnerStage

interface RunnerStage

One stage.

  • name: string — The stage's name, reported in events.
  • length: number — Distance before the boss (or the end of a bonus stage).
  • speed: number — Scroll speed (units per second); speeds overrides it from given distances.
  • speeds?: { at: number; speed: number }[] — From each at distance into the stage the scroll speed becomes speed (units per second), in order of distance.
  • ground: DepthSurface — The ground pattern (and an optional ceiling).
  • ceiling?: DepthSurface — A roof pattern over the stage, for a canyon or tunnel. Default none.
  • look?: Record<string, string> — Presentation colours and names (sky, backdrop ...): free strings the presenter reads.
  • scenery?: Omit<SceneryOptions, "seed" | "name"> & { seed?: number } — Streamed scenery (z measured from the stage start).
  • waves: RunnerWave[] — Formations in order of distance.
  • bosses?: RunnerBoss[] — Bosses in order (several: a boss rush). None: the stage ends at its length.
  • bonus?: { perSmash: number; mount: { w: number; h: number; d: number; y: number; bob: number; bobRate: number } } — A bonus ride: invincible, lateral control only, smash smash kinds for perSmash each at the end.

RunnerState

interface RunnerState

The whole game state (plain data).

  • status: RunnerStatus — Where the game is: ready, playing, boss, clear and so on.
  • statusTime: number — Seconds spent in the current status.
  • stage: number — Index of the current stage in RunnerConfig.stages, from 0.
  • stageNumber: number — Stage number shown (1-based, counting loops).
  • loop: number — How many times the game has looped back to the first stage; 0 on the first pass.
  • distance: number — Distance travelled in this stage.
  • camZ: number — Camera z (absolute).
  • speed: number — Current scroll speed (units per second).
  • time: number — Seconds of simulated time since the game began.
  • score: number — Points scored.
  • topScore: number — The best score so far, starting at scoring.topScore.
  • lives: number — Lives left.
  • nextExtra: number — The score that earns the next extra life; Infinity when no more are due.
  • extraIndex: number — How many entries of scoring.extraLives have been passed.
  • tickClock: number — Seconds banked towards the next creeping score tick.
  • fireClock: number — Seconds until the cannon can fire again.
  • player: RunnerPlayerState — The runner.
  • timers: { id: string; left: number; members: string[] }[] — Formation timers on screen: TIME shows the largest.
  • wavesFired: number — Waves already triggered in this stage.
  • pending: { at: number; wave: number; member: number }[] — Pending member spawns.
  • boss: RunnerBossState | null — The boss in progress, or null when none is.
  • bossesDone: number — Bosses finished in this stage.
  • stageZ: number — Camera z where this stage started (stage distances are measured from it).
  • acc: number — Fixed-step accumulator (seconds).
  • smashed: number — Bonus objects smashed this stage.
  • stats: { shots: number; kills: number; deaths: number } — Totals for presentation.
  • rng: number[] — The seeded random stream's state: four words, from the kernel's rng.snapshot(). It advances in place and is saved with the state.

RunnerStatus

type RunnerStatus = "attract" | "ready" | "playing" | "dying" | "boss" | "clear" | "tally" | "over" | "complete"

Game status.

RunnerTiming

interface RunnerTiming

State timings in seconds.

  • ready: number — Seconds of the READY pause before play (or a boss) begins.
  • dying: number — Seconds of the death fall before the runner respawns (or the game ends).
  • clear: number — Seconds the stage-clear screen shows before the next stage.
  • tally: number — Seconds the bonus tally of a bonus stage shows before the next stage.
  • bossIntro: number — Seconds of travel left to the boss when the stage resumes after a boss in a boss rush, so the next one arrives after this long.

RunnerTuning

interface RunnerTuning

Difficulty multipliers (see runnerDifficulty).

  • fireEvery: number — Enemy fire interval multiplier (bigger: less fire).
  • projectileSpeed: number — Enemy projectile speed multiplier.
  • speed: number — Scroll speed multiplier.
  • hitbox: number — Runner hit box multiplier against enemies, shots and obstacles.
  • timers: number — Boss/formation timer multiplier.
  • lives: number — Extra lives at the start.

RunnerView

interface RunnerView

The camera and projection: how the Super Scaler view follows the runner.

  • width: number — Screen width and height in pixels (the arcade's 320 x 224).
  • height: number — Screen height in pixels.
  • focal: number — Pixels per world unit at depth 1.
  • horizon: number — Screen y of the horizon when the runner is on the ground.
  • pitchPerHeight: number — Horizon shift in px per unit of runner height (negative: the horizon rises as he climbs).
  • playerDepth: number — Depth of the runner's plane in front of the camera.
  • eyeHeight: number — Camera height when the runner is on the ground.
  • followX: number — Share of the runner's x the camera follows (0 fixed, 1 locked behind him).
  • followY: number — Share of the runner's height the camera follows.
  • far: number — Farthest depth objects are drawn and enter.
  • behind: number — Units behind the camera before objects are dropped.

RunnerWave

interface RunnerWave

A formation: count members of kind that follow path (camera frame), one every every seconds.

  • at: number — Stage distance at which it starts.
  • kind: string — The id of the member's kind in RunnerConfig.kinds.
  • count: number — How many members the formation has.
  • every?: number — Seconds between members (default 0.25).
  • path: string — Registered path id (see RunnerConfig.paths); ~m suffix is the mirrored path.
  • mirror?: boolean | "alternate" — Mirror every member, or alternate plain/mirrored.
  • offsets?: { x?: number; y?: number; z?: number }[] — Per-member origin offsets, cycled.
  • pathEnd?: "remove" | "continue" | "hold" — What a member does at the path end (default remove: it flew away).
  • timer?: number — Seconds before surviving members leave; shown as TIME while they are on screen.

RunnerWindow

interface RunnerWindow

Periodic vulnerability: open for open seconds, closed for closed, starting offset seconds into the cycle.

  • open: number — Seconds the window is open (vulnerable) in each cycle.
  • closed: number — Seconds the window is closed in each cycle.
  • offset?: number — Seconds into the cycle at age 0, to put enemies out of step. Default 0.
  • from?: number — Closed until this age (seconds): a Tomos arrives shut. Default 0.

SerpentPart

interface SerpentPart

A dragon: a head that takes hits and a body of segments trailing its path.

  • type: "serpent" — Always "serpent": what tells this part from the other kinds of boss part.
  • head: string — The id of the head's kind in RunnerConfig.kinds.
  • body: string — The id of the body segments' kind in RunnerConfig.kinds.
  • segments: number — Body segments.
  • spacing: number — Seconds of the head's track between segments.
  • path: string — Registered looping path for the head (camera frame).
  • phase?: number — Start time on the path.
  • hits: number — Head hits to destroy.
  • stages?: number — Colour stages the head goes through as it is hit (presentation reads data.stage).
  • delay?: number — Start delay (seconds).

SpaceHarrierOptions

interface SpaceHarrierOptions

Options for spaceHarrierPreset.

  • difficulty?: RunnerDifficulty — "easy", "normal" or "arcade" (the original tables). Default "normal".
  • lives?: number — Lives DIP: 2, 3 (default), 4 or 5.
  • extraLife?: number — Bonus life DIP: 5,000,000 (default) or 7,000,000.
  • startStage?: number — First stage, 1-based (default 1).
  • stages?: number[] — Keep only these stage numbers (1-based), in order (default all 18).
  • seed?: number — Seed for the game's random stream. Default 1985.

SquadPart

interface SquadPart

A squadron of count units on a path, each every seconds apart in phase.

  • type: "squad" — Always "squad": what tells this part from the other kinds of boss part.
  • kind: string — The id of the units' kind in RunnerConfig.kinds.
  • count: number — How many units the squadron has.
  • path: string — The id of the registered path the units follow (camera frame); ~m after it is the mirrored path.
  • every: number — Seconds of path phase between units (all appear together).
  • offsets?: { x?: number; y?: number; z?: number }[] — Per-unit origin offsets in world units, cycled through the units.
  • mirror?: boolean | "alternate" — Mirror every unit, or alternate plain and mirrored.
  • pathEnd?: "remove" | "hold" — At the end of a non-looping path: remove (default: flew off or passed), hold.