Dressing a roadside
The racetrack demo is handed a road and works out what should stand beside it, and where. This is the ruler it measures in, the rules it has to satisfy, and the loop it runs until they all come out true at once.
The idea
A roadside is not scenery. It is instructions.
On a fast road, the trees, signs, barriers and grandstands beside the track are not decoration. They tell you a corner is coming, how hard it is, and where to brake. Take them away and the same road becomes unreadable at speed.
So the demo is given two things. A spec — rules with numbers in them: how much stuff, how far off the road, what must stay visible from the cockpit, what has to stand before a corner. And a vocabulary — the objects it may use, described as sizes and habits.
Then it dresses a road it has never seen, and every rule has to come out true.
Step one
Measure everything in road-widths.
A rule that says “a barrier 4 metres from the edge” is a rule about one road. Said in half-widths — call it W, half the distance across the tarmac — it is a rule about every road. That single choice is what makes the whole spec portable: point it at a wider track, or a longer one, and nothing has to be restated.
How the graph is built
The graph makes the road and the ruler. The rules are code that reads them.
pcg-ts is a node graph: little boxes that each do one thing to a pile of points, wired nose to tail. This demo's graph is about twenty nodes, and it has exactly two jobs — turn the given centreline into a road surface, and write the s / t / h / W coordinate system onto every point of it so that anything downstream can speak in road-widths.
Worth saying plainly: the graph does not place the scenery. The placement rules are ordinary TypeScript that cooks the graph, reads the lap back, and works out a list of things to stand beside it. The graph's own roadside row — two evenly spaced lines of points — is a deliberate placeholder, kept only so the seam between “where the road is” and “what stands beside it” is already cut.
What the demo reads
A vocabulary of boxes, and the habits of each box.
The demo reads one data file, demos/racetrack/vocabulary.json. Think of it as a parts list: each object described as a handful of axis-aligned boxes, plus how that kind of object behaves — how far off the road it stands, how high, how often, which side, whether it prefers corners or straights.
Size
Will it fit?
- Across, along and tall, in W
- Enough to test it against the driving corridor, the overhead ceiling and whatever stands next to it
Habits
Where does it belong?
- Typical lateral and height, with a spread
- Which side it favours, how clumped it is, how much it prefers a bend to a straight
Each entry also carries a set of poses, and they are the reason the dressing does not look stamped. The vocabulary carries no rotation, so the demo chooses one — but every pose keeps its own boxes, and 362 poses give 361 distinct sets. The variety is in the shapes.
The important distinction
Some rules you can roll dice for. Some you have to go back and fix.
A rule like “35% of things sit in the mid band” is a distribution. You can satisfy it while placing, by picking each object with the right odds. A rule like “the driver must be able to see the next stretch of road” is a threshold. No amount of careful dice-rolling guarantees it, because it is a promise about the result, not a shape of the results.
Thresholds get a repair pass: place first, then measure, then move or drop the offenders. Getting this backwards is what makes a rule set look self-contradictory when it is only being applied in the wrong order.
How much stuff there is, overall.
0.95 per W of lap · 0.71–1.54How it splits across the bands, from verge to horizon.
15 / 8 / 29 / 35 / 13 / 0.5 %No long empty stretches. Most of the lap has something within reach.
85% within 2W · gaps ≤ 25WNothing may stand in the volume the car drives through.
|t| < 1W, h < 1.2W kept clearFrom the cockpit, the road ahead must stay visible. Blockers move out or go.
next 12W, eye at h = 0.3WEvery corner gets something tall before its entry, so you can see it coming.
one marker per cornerEvenly spaced marks through the braking zone — a ruler you read at speed.
on one line, outside the cornerEvery tenth of the lap gets a landmark you recognise, so you know where you are.
≥ 1 repeated asset per tenthNo false edges: a line of objects must never read as a road edge that isn't there.
checked on |t|, then broken upSome of the lap runs under cover — tunnels and bridges, placed as runs, not as single objects.
10–25% of the lap enclosedThe pipeline
The order is the design.
Nine stages, and they only work in this sequence. The cull sits sixth rather than fourth — everything the legibility rules add has to face the visibility test too, and a corner marker is exactly the tall thing near the racing line that the test exists to catch.
Every repair reports how many times it had to fire. A repair that never runs and a generator that never needed one produce identical green tests — so the count is part of the output, not just the pass.
Why it runs more than once
Fixing one rule breaks another. So it goes round until nothing moves.
Two failures worth remembering, both found by looking at the screen rather than at the tests. A repair that keeps handing work to the next repair never settles — trace what each round actually moved before blaming the rules. And a repair that cannot recognise its own no-op will fire forever on a rounding error: a height of 1.2 that comes back as 1.1999999999999997 looks like a thing that still needs fixing.
What you see when you run it
Two layers, and the whole argument is between them.
Both layers are on screen at once, drawn by the same renderer at the same half alpha. The lighter grey drops the vocabulary's own poses straight onto the lap, placed by nothing but their three numbers. The darker one is what the rules built from nothing. One value against another is the whole difference between them.
Below full alpha a flat fill writes no depth, so a box in front does not hide the box behind it — they add. Brightness is how much structure stands along the line of sight. That is also why nothing here is coloured by what it is: accumulated alpha and hue are the same channel, and six colours summing through each other make a muddy one that means neither. The look spends the channel on density instead. The overhead pass, which is a line drawing rather than an accumulation, is free to spend its own on the thing the chase view cannot say — which of the two populations a box belongs to.
They stand side by side on purpose. Statistics can say the generated set matches the vocabulary on every single figure, and it can still look wrong at eye level. The only test that catches that is a person looking at it.
There is also a density slider, because “how much stuff” is taste as much as measurement — the rule gives a range, not a number.
Next
Where to go from here
The demo itself is at demos/racetrack, and demos/road dresses a road with none of these placement rules in it. They are two independent demos that share a spline and a naming scheme, not one page with the rules switched off, so read road as the other answer rather than as a diff. Architecture is how the library underneath is put together, and the corpus gallery is every graph in the repository, cooked and shot.