Phaser AE

Endless runner core

Pro packv3.0.0-1Arcade

The run every endless runner shares, as plain state: chunks of world laid ahead by seed, a speed that climbs to a cap, stumbles that bring the chaser, crashes that end it, a score for distance and coins, and timed power-ups. Your game moves the runner; this pack owns the distance and everything that follows from it.

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

Released October 19, 2026 · updated October 20, 2026

Endless runner core: 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

runnerCore

function runnerCore(): Pack<{ runner: RunnerCoreApi }>

The runner-core pack: game.runner, the endless run a runner's motor stands on. No options.

Helpers

createRunner

function createRunner<I = unknown>(options: RunnerOptions<I>): Runner<I>

Start a run. Throws, with every fault, on options validateRunner refuses.

runnerDefaults

const runnerDefaults: { seed: number; ahead: number; behind: number; gap: number; noRepeat: number; hits: "stumble"; grace: number; speed: { start: number; max: number; accel: number; by: "time"; }; stumble: { window: number; limit: number; slow: number; slowFor: number; }; score: { perUnit: number; perCoin: number; multiplier: number; }; }

The defaults a run's options fall back on.

validateRunner

function validateRunner<I>(options: RunnerOptions<I>): string[]

Check a run's options. Empty when it can be run; otherwise one line per fault.

Types

Runner

interface Runner<I = unknown>

A run.

  • readonly state: RunnerState — Where it stands. Read it; change it through the methods.
  • readonly options: Readonly<Required<Omit<RunnerOptions<I>, 'milestone'>>> & { milestone: number | undefined } — The options it was made with, defaults filled in.
  • readonly chunks: ReadonlyArray<RunnerPlaced<I>> — The chunks laid from behind the runner to ahead of it, in order.
  • step(dt: number): void — Advance the run by dt seconds: the speed, the distance, the timers and the chunks ahead.
  • hit(kind: 'stumble' | 'crash', cause?: string): RunnerHitResult — A hit: 'stumble' for a scrape, 'crash' for a head-on. Says what it did.
  • end(cause: string): void — End the run now, whatever is running: a fall off the world that no shield or grace can stop.
  • coin(count?: number): number — Coins picked up. Returns the total.
  • power(name: string): boolean — Start a power-up by name. Returns false when stack: 'ignore' refused it.
  • remaining(name: string): number — Seconds left of a power-up, 0 when it is not running.
  • has(name: string): boolean — True while a power-up runs.
  • powerUp(name: string): RunnerPowerUp | undefined — The definition of a power-up.
  • endPower(name: string): void — End a power-up now.
  • setMultiplier(n: number): void — The run's own multiplier, before power-ups (Subway Surfers raises it with missions).
  • revive(): void — Carry on after the end ("save me"), with the grace time and a clear chaser.
  • protect(seconds: number): void — Make the runner unhittable for at least seconds more: a landing after a jetpack, a respawn.
  • chunkAt(distance: number): RunnerPlaced<I> | null — The chunk under a distance along the run, or null in a gap or beyond what is laid.
  • drain(): RunnerEvent<I>[] — The events since the last call. At most 512 are kept.
  • snapshot(): RunnerSave — The run as plain JSON.
  • restore(save: RunnerSave): void — Pick up a saved run. Throws on a save that does not fit these options.

RunnerChunk

interface RunnerChunk<I = unknown>

One stretch of authored content: an obstacle pattern, a coin run, a breather. The core strings chunks end to end as the runner goes, chosen by seed, weight and the rules below; what is in a chunk is the motor's business.

  • id: string — Its name, unique among the chunks.
  • length: number — How long it is along the run, in the motor's units (metres, say).
  • items?: I[] — The motor's content, placed relative to the chunk's start. The core never reads it.
  • weight?: number — How likely it is against the other candidates. Default 1; 0 keeps it for opening only.
  • from?: number — The first distance it may start at. Default 0.
  • until?: number — The last distance it may start at. Default no limit.
  • tags?: string[] — Names the rules below can use for a group of chunks ('trains', 'breather').
  • follows?: string[] — May come only straight after a chunk with one of these ids or tags.
  • never?: string[] — Never comes straight after a chunk with one of these ids or tags.

RunnerCoreApi

interface RunnerCoreApi

What the runner-core pack adds to a game: game.runner.

  • create: typeof createRunner — Start a run.
  • validate: typeof validateRunner — Check a run's options; empty when it can be run.
  • defaults: typeof runnerDefaults — The defaults a run's options fall back on.

RunnerEvent

type RunnerEvent<I = unknown> = | { kind: 'chunk'; placed: RunnerPlaced<I> } | { kind: 'drop'; placed: RunnerPlaced<I> } | { kind: 'coin'; count: number; total: number } | { kind: 'stumble'; count: number; cause: string } | { kind: 'shielded'; power: string; cause: string } | { kind: 'over'; cause: string } | { kind: 'power'; power: string; seconds: number } | { kind: 'power-end'; power: string } | { kind: 'milestone'; distance: number } | { kind: 'revive' }

Something that happened, for sound, effects and the HUD. Read them with drain().

RunnerHitResult

type RunnerHitResult = 'ignored' | 'shielded' | 'stumbled' | 'over'

What a hit did.

RunnerOptions

interface RunnerOptions<I = unknown>

Everything a run is made of.

  • chunks: RunnerChunk<I>[] — The chunks to string together. At least one must fit at every distance.
  • opening?: string[] — Chunk ids played first, in order, before the seeded choice: a safe runway, a tutorial.
  • seed?: number — The seed for the choice of chunks. Default 1.
  • ahead?: number — How far ahead chunks are laid. Default 150.
  • behind?: number — How far behind a chunk is kept before it is dropped. Default 30.
  • gap?: number — Empty run between chunks. Default 0.
  • noRepeat?: number — A chunk is not chosen again within this many chunks. Default 1: never twice running.
  • speed?: RunnerSpeed — How the speed rises. Default from 10 to 30 units a second, gaining 0.15 a second.
  • hits?: 'stumble' | 'kill' — 'stumble': a soft hit stumbles and a crash ends the run (Subway Surfers, Temple Run). 'kill': every hit ends it (Jetpack Joyride). Default 'stumble'.
  • stumble?: RunnerStumble — What a stumble does.
  • grace?: number — Seconds a runner cannot be hit after a shield takes a hit, or after a revive. Default 1.5.
  • powerUps?: Record<string, RunnerPowerUp> — The power-ups a pickup can start, by name.
  • score?: RunnerScore — How score is counted.
  • milestone?: number — Raise an event every this many units run. Default none.

RunnerPlaced

interface RunnerPlaced<I = unknown>

A chunk laid on the run.

  • readonly index: number — Its place in the run: 0 for the first chunk laid.
  • readonly chunk: RunnerChunk<I> — The chunk.
  • readonly start: number — Where it starts along the run.
  • readonly end: number — Where it ends.

RunnerPowerUp

interface RunnerPowerUp

A timed power-up a pickup starts.

  • duration: number — Seconds it lasts. 0 lasts until it is used: a shield that waits for its hit.
  • stack?: 'refresh' | 'extend' | 'ignore' — A second pickup while it runs: start the time again ('refresh', the default), add to it, or do nothing.
  • shield?: boolean — Takes one hit of any kind, then ends, and the runner has its grace time.
  • invulnerable?: boolean — Hits do nothing while it runs.
  • multiplier?: number — Score multiplier while it runs (they multiply together).
  • speed?: number — Speed factor while it runs.
  • data?: unknown — Anything else the motor reads: a magnet's reach, a jetpack's height.

RunnerSave

interface RunnerSave

A saved run, as plain JSON: restore on a run made with the same options picks it up exactly.

  • state: RunnerState — The state. Infinity is saved as -1.
  • placed: [number, string, number][] — The chunks laid that are still kept, as [index, id, start].
  • next: [number, number] — The next chunk's index and start.
  • recent: string[] — The ids of the last chunks chosen, for noRepeat.
  • sinceStumble: number — Seconds since the last stumble.
  • rng: RngState — The chunk choice's random stream.
  • milestone: number — The next milestone.

RunnerScore

interface RunnerScore

How score is counted. The score is kept as it goes, so a multiplier counts only while it is on.

  • perUnit?: number — Points per unit run. Default 1.
  • perCoin?: number — Points per coin. Default 0: coins are their own count.
  • multiplier?: number — The multiplier a run starts with. Default 1.

RunnerSpeed

interface RunnerSpeed

How the speed rises.

  • start: number — The speed a run starts at, in units per second.
  • max: number — The most it reaches.
  • accel: number — What it gains: units per second, each second (by: 'time') or each unit run (by: 'distance').
  • by?: 'time' | 'distance' — What the speed rises with. Default 'time'.

RunnerState

interface RunnerState

Where a run stands.

  • time: number — Seconds run.
  • distance: number — Units run: the runner's place along the run.
  • speed: number — The speed now, with stumbles and power-ups applied.
  • rampSpeed: number — The speed the ramp has reached, before them.
  • score: number — The score.
  • coins: number — Coins collected.
  • multiplier: number — The multiplier now, with power-ups applied.
  • baseMultiplier: number — The multiplier before power-ups: the run's own, which setMultiplier sets.
  • alive: boolean — False once the run is over.
  • cause: string | null — Why it ended, or null.
  • stumbles: number — Stumbles in the current window.
  • chaser: number — How close the chaser is, 1 on the runner's heels to 0 gone.
  • grace: number — Seconds left of the grace time.
  • powers: Record<string, number> — The power-ups running, by name, with the seconds left (a waiting shield has Infinity).
  • revives: number — Revives used.

RunnerStumble

interface RunnerStumble

What a stumble does, in a run that has them.

  • window?: number — Seconds the chaser stays on the runner's heels after a stumble. Default 4.
  • limit?: number — Stumbles inside one window that end the run: the chaser catches up. Default 2.
  • slow?: number — The speed, as a fraction, just after a stumble. Default 0.6.
  • slowFor?: number — Seconds the speed takes to come back. Default 0.8.