Phaser AE

Platform map

Pro packv3.0.0-22DArcade

The level model for side-view arcade platform games: a grid of floors, jump-through platforms, ladders, bars and hazards, with materials and stepped or sloped floors. It answers the questions movement and enemy AI ask, takes edits while the game runs, and is the shared level the other platform packs build on.

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

Platform map: 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

platformMap

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

Install the renderer-independent platform map API.

Helpers

CELL_BITS

const CELL_BITS: { readonly solid: 1; readonly platform: 2; readonly ladder: 4; readonly bar: 8; readonly hazard: 16; readonly broken: 32; readonly diggable: 64; readonly breakable: 128; readonly dug: 256; readonly through: 512; }

Bit flags stored per cell. Public API uses CellSpec/CellView; these are exported for compact snapshots.

createPlatformMap

function createPlatformMap(def: PlatformMapDef): PlatformMap

Build a platform map from strings or cell specs.

defaultLegend

function defaultLegend(): Record<string, LegendEntry>

The default legend. Characters can be replaced or extended per map.

girderMap

function girderMap(o: GirderMapDef): PlatformMapDef

Turn girder and ladder lines into a platform-map definition. Each tile-wide piece of a sloped girder keeps the standing height at its centre (rounded), so slopes become one-unit stairs like the arcade's; ladders run from the girder cell at their top to the girder cell at their bottom (snapped to the girders actually there), and a broken ladder's upper cells are marked broken so climbers cannot go down it or past its gap.

makeSurface

function makeSurface(fields: Pick<Surface, "id" | "left" | "right" | "top"> & Partial<Surface>): Surface

Fill a surface from the fields that matter, defaulting the rest (static, one-way, normal traction).

Types

BoundPolicy

type BoundPolicy = "solid" | "open"

How the map treats space outside its columns/rows on a side that does not wrap.

CellHit

interface CellHit

A solid cell hit by a rising head, or a horizontal wall.

  • col: number — Column.
  • row: number — Row.
  • at: number — The blocking line: the cell's bottom (ceiling) or the clamped x (walls).

CellSpec

interface CellSpec

What a cell is. Every field is optional; an empty spec is open air.

  • solid?: boolean — Blocks bodies from every side; its top is a standing surface.
  • platform?: boolean — One-way (jump-through) platform: stand on the top, pass from below and the sides.
  • ladder?: boolean — Climbable ladder. A ladder's top cell is standable when ladderTopFloor is on.
  • broken?: boolean — A gap in a ladder: climbers whose profile blocks broken ladders cannot pass it (Donkey Kong).
  • bar?: boolean — Hand-over-hand bar (Lode Runner): bodies that can hang move along it.
  • hazard?: boolean — Touching it is reported as a hazard (spikes, fire, water).
  • diggable?: boolean — Can be dug into a temporary hole with dig (Lode Runner bricks, Space Panic floors).
  • breakable?: boolean — Can be removed with breakCell.
  • through?: boolean — With solid: a block that rising bodies pass through from below (no head bump, no embedding while passing), but that still blocks from the sides and is stood on from above. Bubble Bobble's platforms and inner walls.
  • material?: string — Named material, e.g. 'ice', 'belt', 'brick'. Walkers look up traction by material.
  • tag?: string — Free text owned by the game: 'rivet', 'trap', 'exit'. Not interpreted by the map.
  • lift?: number — Standing surface lowered by this much inside the cell (stepped girders). 0..tile height.
  • rise?: number — Extra lowering at the cell's right edge relative to its left (linear slope); negative rises.

CellView

interface CellView extends Required<Omit<CellSpec, "material" | "tag">>

A read-only view of one cell.

  • col: number — Column (wrapped into range when the map wraps).
  • row: number — Row (wrapped into range when the map wraps).
  • dug: boolean — A dug hole (open) that remembers the cell it was.
  • empty: boolean — Nothing at all: no feature, material or tag.
  • outside: boolean — Outside the map on a non-wrapping side.
  • material: string — Material name or ''.
  • tag: string — Tag or ''.

GirderLine

interface GirderLine

A girder (or conveyor) line: its standing line from (x1, y1) to (x2, y2), world units.

  • x1: number — One end, x.
  • y1: number — Standing line at x1.
  • x2: number — Other end, x.
  • y2: number — Standing line at x2.
  • material?: string — Cell material ('belt' for conveyors).

GirderMapDef

interface GirderMapDef

A level authored as girder and ladder lines (Donkey Kong's own layout format).

  • width: number — Width, world units (a multiple of the tile).
  • height: number — Height, world units.
  • tile?: number — Tile size. Default 8.
  • girders: GirderLine[] — Girders: one-way platforms stepped one unit at a time along their slope.
  • ladders: LadderLine[] — Ladders.
  • solids?: { x: number; y: number; w: number; h: number }[] — Solid blocks, world units.
  • tags?: { x: number; y: number; tag: string }[] — Tag the girder cell under a point (rivets). y is the girder's standing line.
  • bounds?: PlatformMapDef["bounds"] — Map bounds. Default: solid sides, open top and bottom.
  • brokenReach?: number — How far below the girder above a broken ladder's climb stops, world units (the ROM stops Jumpman 11 px short: $1D80). Default 11.

LadderLine

interface LadderLine

A ladder from a girder's standing line down to the girder below.

  • x: number — Any x inside the ladder's column.
  • top: number — Standing line at its top.
  • bottom: number — Standing line at its bottom.
  • broken?: boolean — Broken: climbers whose profile blocks broken ladders cannot pass the gap (Jumpman), rollers can (barrels).

LadderRun

interface LadderRun

A vertical run of ladder cells.

  • col: number — Column.
  • top: number — Top row of the run.
  • bottom: number — Bottom row of the run.
  • x: number — Centre x of the column.
  • topY: number — World y of the run's top edge.
  • bottomY: number — World y of the run's bottom edge.
  • broken: number[] — Rows inside the run that are broken.

LegendEntry

interface LegendEntry extends CellSpec

A legend entry: a cell spec plus an optional named marker recorded at that cell.

  • marker?: string — Record an authored point with this name (start, enemy, item, exit, pipe ...).

MapChange

interface MapChange

One recorded edit.

  • revision: number — Map revision after this change.
  • col: number — Column.
  • row: number — Row.
  • before: CellView — The cell before.
  • after: CellView — The cell after.
  • cause: string — Why: 'set', 'dig', 'refill', 'break', 'material' or a caller-supplied cause.

Marker

interface Marker

A named point recorded from the legend.

  • name: string — Marker name.
  • col: number — Column.
  • row: number — Row.
  • x: number — Cell centre x.
  • y: number — Cell centre y.
  • floor: number — Bottom of the cell: where a body standing in the cell has its feet.

PlatformMap

interface PlatformMap

The level model. Create with createPlatformMap.

  • readonly cols: number — Columns.
  • readonly rows: number — Rows.
  • readonly tileW: number — Tile width, world units.
  • readonly tileH: number — Tile height, world units.
  • readonly x0: number — Left edge, world x.
  • readonly y0: number — Top edge, world y.
  • readonly width: number — Width in world units.
  • readonly height: number — Height in world units.
  • readonly wrapX: boolean — Columns wrap.
  • readonly wrapY: boolean — Rows wrap.
  • readonly bounds: Readonly<Record<"left" | "right" | "top" | "bottom", BoundPolicy>> — Bound policies.
  • readonly ladderTopFloor: boolean — Whether ladder tops are floors.
  • readonly revision: number — Increments on every edit.
  • col(x: number): number — Column containing world x (not wrapped).
  • row(y: number): number — Row containing world y (not wrapped).
  • centre(col: number, row: number): { x: number; y: number } — Cell centre in world units.
  • left(col: number): number — Cell's left edge, world x.
  • top(row: number): number — Cell's top edge, world y.
  • wrapWorldX(x: number): number — Wrap a world x into the map when wrapX; otherwise unchanged.
  • wrapWorldY(y: number): number — Wrap a world y into the map when wrapY; otherwise unchanged.
  • dx(a: number, b: number): number — Shortest signed x distance from a to b (across the wrap when wrapX).
  • dy(a: number, b: number): number — Shortest signed y distance from a to b (across the wrap when wrapY).
  • cell(col: number, row: number): CellView — The cell at a column/row (wrapped; outside cells follow bounds).
  • cellAt(x: number, y: number): CellView — The cell containing a world point.
  • cells(): CellView[] — Every non-empty cell, row by row (for renderers).
  • markers(name?: string): Marker[] — Markers, optionally by name, in row-major order.
  • isSolid(col: number, row: number): boolean — Solid at a column/row (outside follows bounds).
  • solidAt(x: number, y: number): boolean — Solid at a world point (respects lift: the part above a lowered surface is open).
  • surfaceTop(col: number, row: number, x?: number): number | null — The standing height inside a cell at world x, or null.
  • supportBelow(x: number, fromY: number, toY: number, o?: { span?: number; oneWay?: boolean; ignoreRow?: number | null }): Support | null — The highest standing surface crossed between two heights under a foot span centred on x. oneWay: false ignores platforms and ladder tops (dropping through); ignoreRow skips one row of one-way cells (the platform a body just dropped through).
  • ceilingAbove(x: number, fromY: number, toY: number, span?: number, o?: { through?: boolean }): CellHit | null — The first solid cell bottom a head rising from fromY to toY (smaller) meets, centre column preferred. through blocks are passed unless o.through (flyers that bounce off them).
  • sweepX(x: number, toX: number, halfWidth: number, top: number, bottom: number): { x: number; hit: CellHit | null } — Sweep a box horizontally; returns the reachable centre x and the wall hit, if any.
  • ladderAt(x: number, y: number): LadderRun | null — The ladder run containing a world point, or null.
  • ladderRun(col: number, row: number): LadderRun | null — The ladder run containing a cell, or null.
  • ladderRuns(): LadderRun[] — Every ladder run, left to right then top to bottom.
  • barAt(x: number, y: number): CellView | null — The bar cell at a world point, or null.
  • hazardIn(left: number, top: number, right: number, bottom: number): CellView | null — The first hazard cell overlapping a rectangle, or null.
  • solidIn(left: number, top: number, right: number, bottom: number): CellView | null — The first solid cell overlapping a rectangle, or null (through blocks are never reported: bodies pass them).
  • standable(col: number, row: number, o?: { ladders?: boolean; bars?: boolean }): boolean — A body can rest in this cell: open and supported below, or on a ladder/bar when allowed.
  • lineOfWalk(row: number, c0: number, c1: number): boolean — Every cell from c0 to c1 on a row is open and standable (a body can walk the stretch).
  • bodyCell(x: number, feetY: number): { col: number; row: number } — The cell a body standing with its feet at (x, feetY) occupies (the row its feet rest in).
  • route(from: { col: number; row: number }, to: { col: number; row: number } | ((col: number, row: number) => boolean), o?: RouteOptions): RouteStep[] | null — Deterministic breadth-first route between cells, or null if unreachable.
  • set(col: number, row: number, spec: CellSpec, cause?: string): void — Replace a cell.
  • patch(col: number, row: number, partial: CellSpec, cause?: string): void — Change some fields of a cell.
  • canDig(col: number, row: number): boolean — Whether dig would succeed: diggable, not already dug, inside the map.
  • dig(col: number, row: number, cause?: string): boolean — Dig a diggable cell into an open hole that remembers it. Returns false if it cannot be dug.
  • refill(col: number, row: number, cause?: string): boolean — Restore a dug hole. Returns false if the cell is not dug.
  • breakCell(col: number, row: number, cause?: string): boolean — Remove a breakable cell. Returns false if it is not breakable.
  • setMaterial(col: number, row: number, material: string, cause?: string): void — Change a cell's material ('' clears).
  • original(col: number, row: number): CellView | null — What a dug cell was, or null.
  • changesSince(revision: number): MapChange[] — Changes after a revision (oldest first). Throws if the history no longer reaches that far.
  • drain(): MapChange[] — Changes since the previous drain.
  • onChange(fn: (c: MapChange) => void): () => void — Call a function on every change. Returns an unsubscribe function. Listeners are not snapshotted.
  • toRows(legend?: Readonly<Record<string, LegendEntry>>): string[] — Rows as strings through a legend (best match; '?' when nothing matches).
  • snapshot(): PlatformMapSnapshot — Serialisable state.
  • restore(s: PlatformMapSnapshot): void — Restore a snapshot from a map of the same size.

PlatformMapDef

interface PlatformMapDef

A map definition. Give rows (strings through a legend) or cells (specs).

  • rows?: readonly string[] — One string per row, top first; each character is looked up in legend. Short rows are padded with open air.
  • cells?: readonly (readonly CellSpec[])[] — Explicit cell specs, cells[row][col] (alternative to rows).
  • legend?: Readonly<Record<string, LegendEntry>> — Character legend for rows. Defaults to defaultLegend().
  • tile?: number | { w: number; h: number } — Tile size in world units: one number for square tiles. Default 8.
  • origin?: { x: number; y: number } — World position of the map's top-left corner. Default (0, 0).
  • wrapX?: boolean — Columns wrap: leaving the right edge re-enters on the left (Mario Bros).
  • wrapY?: boolean — Rows wrap: falling out of the bottom re-enters at the top (Bubble Bobble).
  • bounds?: Partial<Record<"left" | "right" | "top" | "bottom", BoundPolicy>> — Treatment of each non-wrapping side. Default: left/right solid, top/bottom open.
  • ladderTopFloor?: boolean — A ladder's top cell (no ladder above it) is a standable floor. Default true.
  • historyLimit?: number — How many changes changesSince can still report. Default 4096.

PlatformMapSnapshot

interface PlatformMapSnapshot

Serialisable state of a map.

  • bits: number[] — Cell flags.
  • material: number[] — Material table indices.
  • tag: number[] — Tag table indices.
  • lift: number[] — Surface lifts.
  • rise: number[] — Surface rises.
  • materials: string[] — Material and tag string tables.
  • tags: string[] — Tag strings.
  • originals: [number, Stored][] — Dug cells' originals.
  • revision: number — Revision.
  • history: MapChange[] — Change history.
  • drained: number — Drain cursor.

RiderMap

type RiderMap = ReadonlyMap<string, readonly string[]>

Surface ids to the walker body ids standing on them after the last walker step.

RouteMove

type RouteMove = "start" | "walk" | "climb-up" | "climb-down" | "hang" | "fall" | "drop" | "jump-up"

How a route moves between two cells.

RouteOptions

interface RouteOptions

What a router may use. Defaults suit a walker that climbs, hangs and drops any height.

  • climb?: boolean — Use ladders. Default true.
  • hang?: boolean — Use hang bars. Default true.
  • fall?: boolean — Walk off edges and drop from bars. Default true.
  • maxFall?: number — Longest drop allowed, in rows. Default unlimited.
  • jumpUp?: number — Jump up through one-way platforms this many rows above. Default 0 (never).
  • ladderExit?: "ends" | "anywhere" — Step off a ladder sideways anywhere ('anywhere', Lode Runner) or only where it is standable ('ends'). Default 'anywhere'.
  • brokenLadders?: boolean — Pass broken ladder cells. Default false.
  • catchLadders?: boolean — Falling bodies catch ladders they fall into. Default true.
  • limit?: number — Cap on explored cells. Default every cell.

RouteStep

interface RouteStep

One step of a route: the cell entered and how.

  • col: number — Column entered.
  • row: number — Row entered (the row the body occupies: its feet rest on the row's bottom).
  • move: RouteMove — How it is entered.

Support

interface Support

A standing surface found by supportBelow.

  • y: number — Standing line, world y.
  • col: number — Column of the supporting cell.
  • row: number — Row of the supporting cell.
  • kind: "solid" | "platform" | "ladder-top" | "bound" — What supports: a solid cell, a one-way platform, a ladder's top, or the closed map bottom.
  • material: string — Material of the supporting cell or ''.
  • oneWay: boolean — True for one-way support (platform or ladder top).

Surface

interface Surface

One standable surface: a horizontal top edge in world units (y down), plus how it moves and treats riders.

  • id: string — Stable id, unique across every provider in a walker world. Riders are keyed by it.
  • left: number — Left edge, world units.
  • right: number — Right edge, world units.
  • top: number — The standing line (top edge), world units, y down.
  • height: number — Thickness below top; a solid surface stops rising heads at top + height.
  • dx: number — Horizontal displacement over the provider's last step; riders are carried by it.
  • dy: number — Vertical displacement over the provider's last step; riders follow top exactly.
  • vx: number — Horizontal velocity, units per second (informational; e.g. momentum on leaving).
  • vy: number — Vertical velocity, units per second (informational).
  • solid: boolean — false: one-way (stand on top, pass from below). true: also blocks a rising head (a bump).
  • belt: number — Conveyor speed applied to riders, units per second, positive to the right. 0 for none.
  • traction: number — Ground traction for riders: 1 normal, below 1 slippery (acceleration and braking scale by it).
  • bounce: number — Launch speed (units per second, upward) given to a body that lands on it. 0 for none.
  • bounceNeedsJump: boolean — When bounce is set, launch only while the rider holds jump (Bubble Bobble bubbles).
  • hazard: boolean — Touching it is reported as a hazard event by the walker.
  • active: boolean — Inactive surfaces are ignored (vanished timed platforms, fallen crumblers).
  • teleported: boolean — Moved discontinuously during the last step (looping elevator): riders are released, not dragged.
  • kind: string — Free classification for renderers and rules: 'path', 'elevator', 'belt', 'bubble', 'guard' ...
  • owner: string — The provider (or game object) that owns it.
  • riderTag?: string — Only bodies carrying this tag stand on it or bump it (e.g. 'player': bubbles that players ride while enemies pass through them). Omitted or '' means every body. Optional, so existing providers are unchanged.

SurfaceProvider

interface SurfaceProvider

A source of standable surfaces. The walker world calls step (if present) first in every fixed step with the riders recorded at the end of the previous step, then reads surfaces() for that step's movement.

  • readonly id: string — Provider id (for diagnostics and removal).
  • step?(dt: number, riders: RiderMap): void — Advance one fixed step. Optional: a provider whose surfaces are moved by someone else omits it.
  • surfaces(): readonly Surface[] — The surfaces for the current step, in a stable order.