Phaser AE

Road view (top-down)

Free packv3.0.0-22DLook

A top-down road or track drawn from data. You say what the ground is; the view turns it into tiled sprites fixed to the world, so a road scrolls through forks, merges, bridges and rivers with no gaps, popping or interpolation smear.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 19, 2026

Road view (top-down): 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

roadViewTopdown

function roadViewTopdown(): Pack<{ roadViewTopdown: RoadViewTopdownApi }>

Install the top-down road view: draws scrolling roads and static top-down tracks from data.

Helpers

mapping

function mapping(axis: RoadAxis, scale: number, camera: RoadCamera, viewport: { width: number; height: number; x?: number; y?: number }, margin = 0): Mapping

The mapping for an axis. up: u right, v up the screen (a vertical scroller driving up); down: u right, v down the screen (a static top-down map, v = y); right: v to the right, u down; left: v to the left, u down.

paintArt

function paintArt(g: Paint, w: number, h: number, look: ArtLook, seed = 3): void

Paint a built-in roadside object into a w x h canvas (texels), seen from above.

paintSurface

function paintSurface(g: Paint, size: number, look: SurfaceLook, seed = 7): void

Paint one tile of a surface texture, size texels square, that repeats seamlessly.

polygonSource

function polygonSource(shapes: readonly SurfaceShape[], extras: { features?: readonly RoadFeature[]; markings?: readonly RoadMarking[] } = {}): RoadSource

A static source from polygons, painted in order (later shapes over earlier). Each row is the scanline crossing of every shape, so a circuit drawn as a track polygon with kerb and run-off polygons becomes ground the view can draw: the track-racer's static circuits, a car park, an arena floor.

sampleChunk

function sampleChunk(source: RoadSource, chunk: number, rowHeight: number, chunkRows: number, has?: (key: string) => boolean): RowRun[]

Sample rows chunk * chunkRows .. (chunk + 1) * chunkRows - 1 (row r covers r * rowHeight to (r + 1) * rowHeight and is sampled at its middle) and merge identical neighbours into runs.

scatterCell

function scatterCell(rule: SceneryRule, ruleIndex: number, seed: number, i: number, j: number, surfaceAt: (u: number, v: number) => string): SceneryItem | null

The scatter for one grid cell of one rule: a candidate at a hashed position inside the cell, kept only when its footprint (plus clearance) lies wholly on the rule's surfaces. surfaceAt must be the same ground the view draws, so a tree never stands on a road edge.

surfaceIn

function surfaceIn(spans: readonly RoadSpan[], u: number, background = ""): string

The surface showing at u in a cross-section (the last span covering it), or background.

tileUv

function tileUv(axis: RoadAxis, u0: number, u1: number, v0: number, v1: number, tile: number, drift = 0): { repeatX: number; repeatY: number; scrollX: number; scrollY: number }

Texture coordinates that keep a tiled texture fixed to the world, whichever run or sprite a patch of ground lands in: adjacent sprites continue the pattern seamlessly and a sprite that moves never slides its texture.

Functions

hash01

function hash01(a: number, b: number, c: number, d = 0): number

A small integer hash (deterministic across platforms) mapped to 0..1.

hex

function hex(r: number, g: number, b: number): string

#rrggbb from channels (clamped).

mix

function mix(a: string, b: string, t: number): string

Linear blend of two colours, t = 0 gives a.

rgb

function rgb(c: string): [number, number, number]

Parse #rgb / #rrggbb to [r, g, b]. Other strings return grey.

sameSpans

function sameSpans(a: readonly RoadSpan[], b: readonly RoadSpan[]): boolean

True when two cross-sections are the same (same spans, same order).

shade

function shade(c: string, k: number): string

Lighten (k > 0) or darken (k < 0) a colour by a fraction.

Classes

RoadPlanner

class RoadPlanner

Turns a camera into the frame's sprites. Rows are sampled once per chunk and cached until the source's revision changes; chunks behind the view are evicted. Keys are anchored to the world (chunk, run, span; scatter cell; feature id; dash index), so a sprite always shows the same piece of ground.

  • readonly axis: RoadAxis — The direction of increasing v on screen. Default up.
  • readonly scale: number — Screen units per world unit. Default 1.
  • readonly rowHeight: number — The row height in world units along v. Default 2.
  • readonly chunkRows: number — Rows per cached chunk. Default 32.
  • sampled: number — Rows sampled since creation (for tests and the stats readout).
  • constructor(readonly o: PlannerOptions)
  • invalidate(): void — Drop every cached row and scatter cell (the source changed rows already drawn).
  • runs(chunk: number): RowRun[] — The cached runs of one chunk.
  • surfaceAt(u: number, v: number): string — The surface the view draws at a world point (sampled at the row's middle, like the ground itself).
  • plan(camera: RoadCamera, time = 0): DrawItem[] — The sprites for one frame. time drives flowing textures (seconds).

Constants

frac

const frac = (n: number): number=> …

Positive fractional part, so a texture offset is always 0..1 whichever side of zero the world is.

Types

ArtLook

interface ArtLook

How a feature or scenery kind is drawn: a built-in painting or an atlas frame of the game's own.

  • paint?: ArtName — A built-in painting.
  • frame?: number — Or an atlas frame added by the game.
  • colour?: string — Main colour for built-in paintings (canopy, roof, rock).
  • colour2?: string — Second colour (trunk, walls, shadow).
  • tint?: string — Multiplicative tint at draw time. Default white.

ArtName

type ArtName = | "tree" | "bush" | "pine" | "snow-pine" | "rock" | "cactus" | "house" | "barn" | "boathouse" | "bridge-rail" | "post" | "sign" | "tuft" | "reeds" | "dune" | "buoy" | "lamp"

Built-in art the view can paint for scenery and features.

DrawItem

interface DrawItem

One sprite the view wants this frame: pure data, produced by planFrame.

  • key: string — Stable key (same object, same key, every frame).
  • look: string — What to draw: surface:<id>, art:<kind>, or solid.
  • x: number — Screen centre x.
  • y: number — Screen centre y.
  • w: number — Screen width.
  • h: number — Screen height.
  • rot: number — Rotation, radians.
  • repeatX: number — Texture repeats across the quad (1 = once).
  • repeatY: number — Texture repeats down the quad.
  • scrollX: number — Texture offset in tile fractions.
  • scrollY: number — Texture offset in tile fractions.
  • tint: string — Tint (before the light).
  • z: number — Draw order.

Mapping

interface Mapping

World-to-screen mapping for one frame.

  • toScreen(u: number, v: number): { x: number; y: number } — Screen position of a world point.
  • toWorld(x: number, y: number): { u: number; v: number } — World point under a screen position.
  • rect(u0: number, u1: number, v0: number, v1: number): { x: number; y: number; w: number; h: number } — Screen rectangle (centre and size) of a world box.
  • readonly bounds: { u0: number; u1: number; v0: number; v1: number } — The world box the viewport shows.

Paint

type Paint = Pick<OffscreenCanvasRenderingContext2D, "fillStyle" | "fillRect" | "beginPath" | "arc" | "ellipse" | "fill" | "moveTo" | "lineTo" | "closePath" | "strokeStyle" | "lineWidth" | "stroke" | "globalAlpha">

The drawing calls the paintings use (an OffscreenCanvas or a canvas 2D context).

PlannerOptions

interface PlannerOptions

The planner's options (the view's options minus the kernel-only ones).

  • source: RoadSource — The road to draw.
  • surfaces: Readonly<Record<string, SurfaceLook>> — Surface looks by id. Unknown ids draw magenta so a missing entry is obvious.
  • background?: string — The surface id under everything, where no span covers. Default none (transparent).
  • axis?: RoadAxis — Direction of increasing v on screen. Default up.
  • scale?: number — Screen units per world unit. Default 1.
  • viewport: { width: number; height: number; x?: number; y?: number } — The screen area to cover: width and height in screen units, and optionally its top-left x and y.
  • rowHeight?: number — The row height in world units along v. Default 2; must be above 0.
  • chunkRows?: number — Rows per cached chunk. Default 32; must be a whole number of 1 or more.
  • scenery?: readonly SceneryRule[] — Roadside scatter rules, applied in order (an earlier rule's object blocks a later one's cell).
  • seed?: number — Scatter seed. Default 1.
  • z?: number — Base draw order; ground at base, markings +1, features +2, scenery +4. Default -100.
  • margin?: number — Extra margin drawn beyond the viewport, in world units. Default 8.

RoadAxis

type RoadAxis = "up" | "down" | "left" | "right"

Which way the road's v axis runs on screen.

RoadCamera

interface RoadCamera

Where the camera looks: the world point (u, v) drawn at screen (x, y).

  • u: number — Across, world units.
  • v: number — Along, world units.
  • x?: number — Screen x of that point. Default: viewport centre.
  • y?: number — Screen y of that point. Default: viewport centre.

RoadFeature

interface RoadFeature

A placed piece of the road's own furniture: a boathouse, a bridge rail, a sign, a gantry.

  • id: string | number — Stable id: the same feature keeps the same sprite while it is on screen.
  • kind: string — A key in the view's art table (or one of the built-in art names).
  • u: number — Centre across, world units.
  • v: number — Centre along, world units.
  • width: number — Size across, world units.
  • length: number — Size along, world units.
  • rot?: number — Rotation in radians, clockwise on screen. Default 0.
  • tint?: string — Multiplicative tint. Default white.
  • z?: number — Draw order relative to the view's base z. Default 2 (over the ground, under the scenery's tall items).

RoadMarking

interface RoadMarking

A painted line along the road: a lane divider, an edge line, a dashed centre line.

  • id: string | number — Stable id. Dashes are keyed by this id plus their world dash index, so they never swap sprites.
  • u: number — Centre of the line across, world units.
  • from: number — Start along, world units.
  • to: number — End along, world units (> from).
  • width?: number — Line width, world units. Default 1.
  • dash?: number — Dash length; omitted or 0 draws a solid line.
  • gap?: number — Gap between dashes. Default equal to dash.
  • colour?: string — Line colour. Default white.

RoadSource

interface RoadSource

What the view draws from. Any object with spansAt is a source: a scrolling road generated as it is driven (road-combat's course), a static circuit rasterised from polygons (polygonSource), or a hand-written function.

  • spansAt(v: number): readonly RoadSpan[] — The cross-section at v: spans in paint order. Must be a pure function of v for a given revision.
  • featuresIn?(v0: number, v1: number): readonly RoadFeature[] — Furniture whose extent overlaps v0..v1 (v0 < v1). Optional.
  • markingsIn?(v0: number, v1: number): readonly RoadMarking[] — Markings overlapping v0..v1. Optional.
  • lookAt?(v: number): string | null — The look (palette, region) at v, optional. When the view's surfaces has a key <surface>@<look>, it is used for that surface there; otherwise the plain <surface> key. One road can then change colours by region.
  • readonly revision?: number — Bump when spans that were already sampled change (a road generated further ahead does not count: rows the view has not reached are sampled fresh). The view drops its cached rows when this changes.

RoadSpan

interface RoadSpan

One stretch of surface across the road at one position along it. from < to, world units across.

  • from: number — Left edge in the across axis u, world units.
  • to: number — Right edge in the across axis u, world units.
  • surface: string — A surface id, looked up in the view's surfaces table.

RoadView

interface RoadView

A road being drawn. Call draw(camera) once per fixed step, from the game's step.

  • draw(camera: RoadCamera, o?: { snap?: boolean; time?: number }): void — Plan and draw the frame. snap cuts interpolation (a respawn, a camera jump); time drives flowing water.
  • setLight(amount: number, colour?: string): void — Day and night: blend every road sprite toward colour by amount (0 = day, 1 = full night tint).
  • invalidate(): void — Forget cached rows (the source changed ground already drawn).
  • toScreen(u: number, v: number): { x: number; y: number } — Screen position of a world point for the last drawn camera (to place the game's own sprites).
  • toWorld(x: number, y: number): { u: number; v: number } — World point under a screen position for the last drawn camera.
  • readonly bounds: { u0: number; u1: number; v0: number; v1: number } — The world box the last frame covered.
  • readonly stats: { shown: number; pooled: number; rowsSampled: number } — Sprites shown last frame and the pool size.
  • readonly planner: RoadPlanner — The planner (pure): for tests and for games that want the plan without the sprites.
  • dispose(): void — Hide and release every sprite.

RoadViewOptions

interface RoadViewOptions

Options for create().

  • source: RoadSource — The road to draw.
  • surfaces: Readonly<Record<string, SurfaceLook>> — Surface looks by id. Unknown ids draw magenta so a missing entry is obvious.
  • background?: string — The surface under everything, where no span covers. Default: none (transparent).
  • axis?: RoadAxis — Direction of increasing v on screen. up for a vertical scroller driving up the screen. Default up.
  • scale?: number — Screen units per world unit. Default 1.
  • viewport: { width: number; height: number; x?: number; y?: number } — Viewport in screen units (the area to cover).
  • rowHeight?: number — Rows sampled per world unit along v: the row height in world units. Default 2.
  • chunkRows?: number — Rows per cached chunk. Default 32.
  • scenery?: readonly SceneryRule[] — Roadside scatter rules, applied in order (an earlier rule's object blocks a later one's cell).
  • art?: Readonly<Record<string, ArtLook>> — Art for features (and named overrides of built-ins), by kind.
  • seed?: number — Scatter seed. Default 1.
  • layer?: string — Render layer. Default hud (the world-screen layer games draw in).
  • z?: number — Base draw order; ground at base, markings +1, features +2, scenery +4. Default -100.
  • margin?: number — Extra margin drawn beyond the viewport, world units. Default 8.
  • resolution?: number — Texels painted per world unit for the built-in textures and art. Default 1 (one texel per unit: crisp pixel art when units are pixels). A road in metres wants about 5.

RoadViewTopdownApi

interface RoadViewTopdownApi

The composed game.roadViewTopdown API.

  • create(options: RoadViewOptions): RoadView — Draw a road from a source: retained, world-anchored sprites (see the pack page).
  • polygonSource: typeof polygonSource — A static source from filled polygons (a top-down circuit, an arena floor).
  • RoadPlanner: typeof RoadPlanner — The pure planner, for headless use.
  • mapping: typeof mapping — World-to-screen mapping for an axis, scale and camera.
  • sampleChunk: typeof sampleChunk — Sample one chunk of rows into runs of identical cross-sections.
  • scatterCell: typeof scatterCell — The deterministic scatter for one grid cell of a scenery rule.
  • surfaceIn: typeof surfaceIn — The surface showing at a point of a cross-section.
  • tileUv: typeof tileUv — World-anchored texture repeat and scroll for a rectangle of ground.
  • paintSurface: typeof paintSurface — Paint a tiling surface texture into a canvas.
  • paintArt: typeof paintArt — Paint a built-in roadside object into a canvas.
  • mix: typeof mix — Blend two colours.
  • shade: typeof shade — Lighten or darken a colour.

RowRun

interface RowRun

A merged run of identical rows inside one chunk.

  • v0: number — First along-position covered.
  • v1: number — Last along-position covered (exclusive).
  • spans: readonly RoadSpan[] — The cross-section shared by every row of the run.

SceneryItem

interface SceneryItem

One roadside object chosen by the scatter.

  • key: string — Stable key: rule, cell across, cell along.
  • rule: number — Index of the rule.
  • kind: number — Index of the kind within the rule.
  • u: number — Centre across.
  • v: number — Centre along.
  • width: number — Size across.
  • length: number — Size along.

SceneryKind

interface SceneryKind

One kind of roadside object for the scatter.

  • art: ArtLook — Built-in art or a frame.
  • width: number — Size across, world units.
  • length: number — Size along, world units.
  • weight?: number — Relative chance among the rule's kinds. Default 1.

SceneryRule

interface SceneryRule

Roadside scatter: objects placed deterministically on a world grid (so they never move or pop as the road scrolls), kept only where their whole footprint, plus clearance, lies on one of the on surfaces.

  • on: string | readonly string[] — Surfaces the objects may stand on.
  • kinds: readonly SceneryKind[] — Kinds to choose from.
  • spacing?: number — Grid cell size in world units (one candidate per cell). Default 24.
  • chance?: number — Chance that a cell holds an object, 0..1. Default 0.6.
  • clearance?: number — Extra margin that must also be on an allowed surface, world units. Default 2.
  • z?: number — Draw order relative to the base z. Default 4.

SurfaceLook

interface SurfaceLook

How one surface looks.

  • colour: string — Base colour (CSS).
  • pattern?: SurfacePattern — Painted pattern. Default flat.
  • detail?: readonly string[] — Detail colours for the pattern (speckles, ripples, blades). Default: shades of the base colour.
  • density?: number — Detail density 0..1. Default 0.35.
  • tile?: number — World units per texture tile. Default 32.
  • flow?: number — Texture drift along the road, world units per second (a river's current). Default 0.
  • frame?: number — Use this atlas frame (already added by the game) instead of painting one.

SurfacePattern

type SurfacePattern = | "flat" | "grain" | "speckle" | "grass" | "water" | "ice" | "snow" | "sand" | "gravel" | "planks" | "stripes" | "checker"

Built-in surface patterns the view can paint without any art from the game.

SurfaceShape

interface SurfaceShape

A filled polygon for polygonSource.

  • surface: string — Surface id.
  • points: ReadonlyArray<{ u: number; v: number }> — Outline, at least three points, in (u, v) world units. Any winding; self-crossings fill by even-odd.