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
parsePiecesandformatPieces. - 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 —
ribbonLegbuilds the road in 3D,closemakes a lap meet itself, and a ground plane lets an elevated circuit pass over and under itself. - Endless roads and saves —
trimfor roads with no end, andsnapshotandrestoreas 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
pseudo-road— projects any stretch of a road file; it stays the projector, this pack the geometry.road-view-pseudo3dandroad-view-topdown— draw the road with its splits, lanes and looks.vehicle-handling— reads the whole cross-section at a distance as the road a vehicle drives on.scaled-depth— takes the scenery stream as scaled roadside sprites.road-racer,road-combatandroad-pursuit— rules that read markers and section data and own no geometry.sky-circuit— elevated circuits built on the 3D ribbon.track— for planar node networks and single-screen circuits instead.
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.mjswrote backslashes into the header of the module it generates, so its--checkcalled a current module stale.






