Phaser AE

Road data

Free packv3.0.0-42DLogicUpdated

The road file every road game in the engine reads: a versioned JSON graph of bends, hills, forks and merges, with regions, seeded scenery, surfaces, terrain features and event markers. Author a road as data, or let players draw one, and the rules, the handling and the renderer all read the same geometry.

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

Released October 14, 2026 · updated October 26, 2026

Road data: 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.

What it does

A road is a graph of sections. Each section is a list of pieces, written in a compact notation such as S600 R500/800 L400/350^20, or a body generated from a seed within ranges of length, curvature, climb and width. Pieces carry curvature, climb, bank, a centre shift, half-width, median, shoulder and a lane count, with bends and bank eased in and out so they arrive smoothly. Sections name their successors from left to right, and cycles are allowed, so a circuit loops and a stage map branches.

Branching is built in. A fork widens the road into strips, one per successor, joined by connectors that bend apart; the side the vehicle is on chooses, or choose decides ahead for AI and scripts. Merges are the mirror, and a median opens an island that rejoins. Regions name a look, a surface and the ground beside the road, and carry scenery pools that are generated from the road's seed rather than stored object by object, measured outward from the tarmac edge so a wide canopy never overhangs the road. Hand-placed landmarks sit beside them.

A path lays the sections out as driven and samples any distance: plan position and heading, curvature, grade, height, bank, the cross-section's strips, the region and the surfaces. advance reports what the vehicle has passed: a new section, a fork with the shift that rebases the vehicle onto its branch, a merge, and entering, passing and leaving each marker, by lane. Markers and features carry free data and no behaviour of their own, so a checkpoint, a weapons van or a ramp means whatever your rules say.

The file is a tool format too. validateRoad checks the schema, references, ids, geometry and lanes and reports each problem with a JSON pointer, saveRoad writes canonical JSON that round-trips, and planLayout lays the whole graph flat for a map or an editor.

Features

  • Compact piece notation — straights, bends, climbs, width, shift, median and lanes in one line, read and written by parsePieces and formatPieces.
  • Generated sections — seeded bodies from ranges of length, piece kinds and weights, optionally different on each visit.
  • Forks, merges and islands — branches chosen by the vehicle's side, mirrored merges, and medians that rejoin.
  • Regions and scenery pools — looks, surfaces and deterministic roadside scenery from the seed, plus landmarks.
  • Surfaces and features — oil, puddles, water and dirt; ramps with launch angles, ridable walls, tunnels, and bridges with missing spans.
  • Markers — checkpoints, laps, grids, vans, bosses and music cues, each with free data, reported per lane.
  • Two frames — a curved frame for pseudo-3D roads and a straight frame for top-down scrollers, driven forward or in reverse.
  • Roads in true 3D — ribbonLeg builds the road in 3D, close makes a lap meet itself, and a ground plane lets an elevated circuit pass over and under itself.
  • Endless roads and saves — trim for roads with no end, and snapshot and restore as plain JSON.
  • A published, checked format — format road-data, version 1, with its JSON Schema, a sample road file, precise messages for every issue and saved files that round-trip.

Good for

  • Pseudo-3D arcade racers with a branching stage map, in the style of Out Run, or a single circuit, as in Pole Position.
  • Motorbike racers with banked bends and long hills, in the style of Hang-On.
  • Top-down road and river combat on a forking road that becomes water through a boathouse, in the style of Spy Hunter.
  • Police chases with a boss marker and shortcut forks, in the style of Chase H.Q.
  • Rally stages with gravel surfaces and jumps and no traffic.
  • Elevated kart circuits that cross over and under themselves, in the style of Power Drift.
  • Delivery games on a bike, with drop points placed along the road.
  • Road editors and player-drawn tracks, checked and saved in the same format the game plays.

Make it yours

The pack has no options: everything is in the file. At the top level are the seed, the defaults, named entries, scenery kinds, regions, and a progression that scales curvature, width and climb by distance driven. Each section has pieces or generate, its successors, forks and merges, region spans, surfaces, features, markers, objects and free data. A path chooses its frame, reverse driving and the conditions that switch on items marked when.

Two different uses: a pseudo-3D stage race whose sections fork into a branching map, a region per stage with its own palette and roadside pools, and checkpoint markers that add time; and a top-down road-and-river combat game in the straight frame, its road authored with shift, oil and water surfaces, a bridge with a missing span and a weapons-van marker.

A quick look

A road file loaded, sampled and driven, with forks and checkpoints handled:

await game.assets.load([{ kind: 'json', key: 'road', url: 'roads/my.road.json' }]);
const road = game.roadData.loadRoad(game.assets.get('road'));   // throws, listing every issue
const path = road.path({ frame: 'curved' });
const here = path.sample(z);              // curvature, grade, height, bank, lanes, region, surfaces
for (const e of path.advance(z, x)) {
    if (e.type === 'fork') x += e.dx;     // rebase the vehicle onto the chosen branch
    if (e.type === 'pass' && e.marker.type === 'checkpoint') addTime();
}

Works with

Good to know

  • Pseudo-3D roads need not agree in plan: sections can cross on a map, and a merge whose branches do not meet in plan is reported as a gap.
  • The straight frame turns curvature into sideways drift; it is meant for roads authored with shift.
  • Reverse driving needs a single predecessor and refuses sections with forks or merges.
  • Markers and features define no behaviour: your rules decide what a checkpoint or a ramp does.
  • Scenery pools know nothing of another stretch of the same lap passing nearby; drop placements that meet an elevated deck with ribbonIndex.

Changes

3.0.0-4 (2026-10-26)

  • Fixed: on Windows, scripts/roads-module.mjs wrote backslashes into the header of the module it generates, so its --check called a current module stale.

More Logic packs

Show all →