Speed limit = road geometry
A circuit where nothing punishes speed is a screen saver. The demo needed corners to have consequences: arrive too fast and you leave the road and the run ends.
The rule is the real one
Every sample on the centerline carries a radius. The speed limit at that sample is the textbook cornering bound:
const vMax = Math.sqrt(GRIP * radius); // a = v²/r, capped by grip
That's not a game-feel invention — v²/r is the centripetal demand, μ·g
is what the tyre-road pair can supply. GRIP = 12 is the chosen coefficient;
at the hairpin (r ≈ 10 m) the limit lands near 11 m/s, sweepers sit near
19–20 m/s, and straights (r → ∞) are unbounded. A straight is a corner with
infinite radius, which is why it needs no speed limit at all.
Exceed the local limit by 10% while on the asphalt and the run ends: car
tips, speed zeroes, off the road — press R to restart.
Why this beats scripted slow-zones
The alternative was marking each corner "slow zone, max X" — hand-authored rules that drift the moment the track changes. Deriving the limit from geometry means the rule survives any layout: drop in a new waypoint set and every braking zone computes itself. The road is the rulebook.
It also composes with the other honest physics. On grass the drag constant triples — leave the circuit and the run bleeds speed, no crash flag needed. Two independent models (grip and drag) produce what feels like one judgment.
The numbers, as a spec sheet
57% of lap r > 40 m flat out
hairpin r ≈ 10 m ~11 m/s entry — a real braking zone
sweepers r ≈ 30 m ~19 m/s — lift, don't brake
Those percentages come from counting samples, not from vibes — the same histogram that caught the r=1.6 spike in the previous post.