Phaser AE

Three-lane 3D runner

Pro packv3.0.0-33DArcade

An endless lane runner seen from behind: swipe between lanes, jump, roll, run the roofs of trains and collect coins and power-ups. The rules, the track and the power-ups are all here; the pack draws nothing, so you present it in 3D, or in 2D seen from behind.

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

Three-lane 3D 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

laneRunner3d

function laneRunner3d(): Pack<{ laneRunner: LaneRunnerApi }>

The lane-runner-3d pack: game.laneRunner, Subway Surfers' rules on runner-core. No options.

Helpers

createLaneAutopilot

function createLaneAutopilot(game: LaneRunner, options: LaneAutopilotOptions = {}): LaneAutopilot

A lane run's autopilot. It never touches game but to read it; give the moves it returns to game.input.

createLaneRunner

function createLaneRunner(options: LaneRunnerOptions = {}): LaneRunner

Start a lane run. Throws, with every fault, on options validateLaneRunner (or runner-core) refuses.

laneBodyDefaults

const laneBodyDefaults: LaneBody

The body defaults.

laneRunnerPowerUps

const laneRunnerPowerUps: Record<string, RunnerPowerUp>

The built-in power-ups: Subway Surfers' set.

subwayChunks

function subwayChunks(lanes = 3, options: { trainHeight?: number } = {}): RunnerChunk<LaneItem>[]

Subway Surfers' track as chunks for any number of lanes: barriers to jump and roll, walls of trains with one lane open, ramps up onto roofs and runs along them, trains that come at the runner, coin runs and power-ups, harder ones further along. Every chunk starts with clear track, so the runner can reach any lane between chunks.

validateLaneRunner

function validateLaneRunner(options: LaneRunnerOptions = {}): string[]

Check a lane run's options and chunks. Empty when it can be run.

Types

LaneAutopilot

interface LaneAutopilot

A player that picks the next move.

  • next(): LaneMove[] — The moves to make now (swipes, then a jump or a roll), empty to keep running. Call it before each step; it decides once every every.

LaneAutopilotOptions

interface LaneAutopilotOptions

How the autopilot looks ahead.

  • horizon?: number — Seconds a plan must live through. Default 1.5.
  • beyond?: number — Seconds it looks on past that, running straight, to choose between plans that live. Default 1.
  • times?: number[] — When a plan's jump or roll can come: now, or this many seconds later. Default [0, 0.2, 0.4].
  • every?: number — Seconds of run between decisions; next() returns no moves in between. Default 0.1.

LaneBarrier

interface LaneBarrier

A barrier across a lane: the runner's body must pass above top or below bottom.

  • kind: 'barrier' — Marks this item as a barrier.
  • lane: number — Its lane.
  • at: number — Where it stands, from the chunk's start.
  • bottom: number — The height of its underside: a runner whose head is below it rolls under.
  • top: number — The height of its top: a runner whose feet are above it jumps over.
  • look?: string — What it is, for the drawing: 'hurdle', 'overhead', 'gate' or a game's own.
  • hit?: 'crash' | 'stumble' — What touching it does. Default 'crash'.

LaneBarrierView

interface LaneBarrierView

A barrier, as drawn.

  • id: string — Unique id in the run: <chunk index>:<item index>.
  • lane: number — Its lane, from 0 on the left.
  • x: number — The x of its lane's middle, world units.
  • z: number — Where it stands along the run, world units.
  • bottom: number — The height of its underside, world units.
  • top: number — The height of its top, world units.
  • look: string — What it is, for the drawing: 'hurdle', 'overhead', 'gate', a game's own, or 'barrier' when the chunk says none.

LaneBody

interface LaneBody

The runner's body and moves.

  • height: number — Standing height. Default 1.8.
  • rollHeight: number — Height while rolling. Default 0.7.
  • halfWidth: number — Half its width. Default 0.3.
  • halfDepth: number — Half its depth along the run. Default 0.3.
  • jumpHeight: number — How high a jump goes. Default 1.4.
  • jumpTime: number — How long a jump is in the air, on the flat. Default 0.65 s.
  • rollTime: number — How long a roll lasts. Default 0.55 s.
  • changeTime: number — Seconds to move one lane across. Default 0.18.
  • fastFall: number — The speed a roll in the air drives the runner down at. Default 30.
  • stepUp: number — The height of an edge the runner steps up onto rather than hits (a ramp's start). Default 0.4.

LaneCoins

interface LaneCoins

A run of coins down a lane, straight or arched over something.

  • kind: 'coins' — Marks this item as a run of coins.
  • lane: number — Its lane.
  • at: number — Where the first coin is, from the chunk's start.
  • count?: number — How many. Default 1.
  • every?: number — The spacing. Default 3.
  • y?: number — The height of the first and last coin. Default 1.
  • arc?: number — How high the middle coins rise above that, in an arch. Default 0: straight.

LaneEvent

type LaneEvent = | RunnerEvent<LaneItem> | { kind: 'jump' } | { kind: 'roll' } | { kind: 'land'; on: string | null } | { kind: 'bump'; side: -1 | 1 } | { kind: 'scrape'; id: string } | { kind: 'crash'; id: string; what: string } | { kind: 'pickup'; id: string; x: number; y: number; z: number; power?: string } | { kind: 'board' }

Something that happened in a lane run: runner-core's events, and the motor's own.

LaneItem

type LaneItem = LaneTrain | LaneBarrier | LaneCoins | LanePickup

What a lane chunk holds.

LaneMove

type LaneMove = 'left' | 'right' | 'jump' | 'roll' | 'board'

A move.

LanePickup

interface LanePickup

A power-up to pick up, by its name in the run's powerUps.

  • kind: 'power' — Marks this item as a power-up pickup.
  • lane: number — Its lane.
  • at: number — Where it is, from the chunk's start.
  • power: string — The power-up it starts.
  • y?: number — Its height. Default 1.

LaneRunner

interface LaneRunner

A lane run.

  • readonly run: Runner<LaneItem> — runner-core's run under it: score, coins, speed, power-ups, stumbles.
  • readonly state: LaneRunnerState — Where the runner is.
  • readonly options: Readonly<{ lanes: number; laneWidth: number; trainHeight: number; trainWidth: number; body: LaneBody; boardPower: string; coinRadius: number }> — The options, defaults filled in.
  • laneX(lane: number): number — The x of a lane's middle.
  • input(move: LaneMove): void — A move, applied on the next step: swipes queue and turn back, jumps and rolls wait for the ground.
  • step(dt: number): void — Advance dt seconds, in fixed steps of 1/120 s.
  • view(): LaneView — What is where.
  • drain(): LaneEvent[] — Events since the last call.
  • snapshot(): LaneRunnerSave — The run as plain JSON.
  • restore(save: LaneRunnerSave): void — Pick up a saved run made with the same options.
  • clone(): LaneRunner — A separate run made with the same options, at the same point: to try moves on without touching this one.

LaneRunnerApi

interface LaneRunnerApi

What the lane-runner-3d pack adds to a game: game.laneRunner.

  • create: typeof createLaneRunner — Start a lane run.
  • validate: typeof validateLaneRunner — Check a lane run's options; empty when it can be run.
  • chunks: typeof subwayChunks — The built-in track as chunks, for any number of lanes.
  • powerUps: typeof laneRunnerPowerUps — The built-in power-ups.
  • body: typeof laneBodyDefaults — The runner's body defaults.
  • autopilot: typeof createLaneAutopilot — A player that looks ahead, for an attract mode or a test.

LaneRunnerOptions

interface LaneRunnerOptions

A lane run's options: the track, the body, and runner-core's run.

  • lanes?: number — How many lanes. Default 3.
  • laneWidth?: number — Their width. Default 2.6.
  • startLane?: number — The lane the runner starts in. Default the middle.
  • trainHeight?: number — A train's roof height, when a train does not say. Default 3.4.
  • trainWidth?: number — A train's width, as a fraction of the lane. Default 0.82.
  • body?: Partial<LaneBody> — The runner's body and moves.
  • chunks?: RunnerChunk<LaneItem>[] — The chunks the run is built from. Default subwayChunks(lanes).
  • opening?: string[] — Chunk ids played first. Default ['runway'] with the built-in chunks, none otherwise.
  • boards?: number — Hoverboards in hand at the start. Default 3.
  • boardPower?: string — The power-up a hoverboard starts. Default 'hoverboard'.
  • powerUps?: Record<string, RunnerPowerUp> — The power-ups, by name. What each does comes from its definition: shield (a hoverboard), multiplier (a doubler), and the motor's data: reach pulls in coins that far ahead from every lane (a magnet), fly lifts the runner to that height over everything (a jetpack), jump is the jump height while it runs (super sneakers). Default laneRunnerPowerUps.
  • coinRadius?: number — How far from a coin's centre the runner picks it up. Default 0.9.
  • run?: Omit<RunnerOptions<LaneItem>, 'chunks' | 'opening' | 'powerUps' | 'hits'> — runner-core's options for the run: seed, speed, stumbles, grace, score, milestone, ahead and behind.

LaneRunnerSave

interface LaneRunnerSave

A lane run saved as plain JSON.

  • run: RunnerSave — runner-core's saved run: score, speed, power-ups and the chunks laid.
  • state: LaneRunnerState — The runner's state.
  • moving: [string, number][] — The fronts of the moving trains, as [train id, front z in world units].
  • taken: string[] — Ids of the coins and pickups already collected.
  • struck: string[] — Ids of the barriers and trains already struck, so each counts once.
  • sky: [number, number][] — The jetpack trail's coins still ahead, as [lane, z in world units].

LaneRunnerState

interface LaneRunnerState

Where the runner is and what it is doing.

  • x: number — Across, in world units (0 is the middle of the track).
  • y: number — Height of its feet.
  • z: number — Along the run: runner-core's distance.
  • vy: number — Vertical speed.
  • lane: number — The lane it is in or heading for.
  • from: number — The lane it left, while it changes.
  • grounded: boolean — On something (the ground, a ramp or a roof).
  • on: string | null — What it stands on: a train's id, or null for the ground or the air.
  • rolling: number — Seconds of roll left.
  • rollOnLand: boolean — It will roll when it lands (a roll pressed in the air).
  • flying: boolean — Up on a jetpack.
  • boards: number — Hoverboards in hand.
  • jumps: number — Jumps, rolls and lane changes made.
  • rolls: number — Rolls made.
  • changes: number — Lane changes made.
  • flights: number — Jetpack flights taken.

LaneThingView

interface LaneThingView

A coin or a pickup, as drawn.

  • id: string — Unique id in the run: <chunk index>:<item index>[:<coin index>], or sky:<flight>:<z> for a jetpack coin.
  • x: number — Across, world units (0 is the middle of the track).
  • y: number — Height above the track, world units.
  • z: number — Along the run, world units.
  • power?: string — A pickup's power-up name; absent on a coin.

LaneTrain

interface LaneTrain

A train: a block a lane wide with a roof to run on, standing or coming at the runner, with a ramp up to it.

  • kind: 'train' — Marks this item as a train.
  • lane: number — Its lane, from 0 on the left.
  • at: number — Where its front is, from the chunk's start. For a moving train, where its front meets the runner.
  • length: number — How long it is.
  • height?: number — Its roof's height. Default the run's trainHeight.
  • speed?: number — Speed towards the runner, units a second. Default 0: standing.
  • ramp?: number — A ramp this long in front of it, up to the roof. Default none.

LaneTrainView

interface LaneTrainView

A train, as drawn.

  • id: string — Unique id in the run: <chunk index>:<item index>.
  • lane: number — Its lane, from 0 on the left.
  • x: number — The x of its lane's middle, world units.
  • front: number — Where its front is along the run, world units; a moving train's front closes on the runner.
  • length: number — How long it is, world units.
  • height: number — Its roof's height, world units.
  • ramp: number — The length of the ramp in front of it up to the roof; 0 for none.
  • moving: boolean — Whether it comes towards the runner rather than standing.

LaneView

interface LaneView

What is where, for a presenter.

  • runner: LaneRunnerState & { height: number; board: boolean; speed: number } — The runner.
  • trains: LaneTrainView[] — Trains from behind to ahead.
  • barriers: LaneBarrierView[] — Barriers.
  • coins: LaneThingView[] — Coins not yet taken, the jetpack's trail among them.
  • pickups: LaneThingView[] — Power-ups not yet taken.
  • chaser: number — How close the inspector is, 1 on the runner's heels to 0 gone.