A self-driving car that runs a real autonomy stack in your browser β
and a neural driver you train, evolve, and race against it. Live. No backend.
Perception β Planning β Control Β· a classical stack Β Β β’Β Β Trained live Β· imitation + neuroevolution Β Β β’Β Β 0 collisions Β· 9 scenarios
How it works Β Β·Β Watch it drive Β Β·Β The neural drivers Β Β·Β Proof Β Β·Β Run it
Blueline is a working implementation of the modular autonomy pipeline from real self-driving
research β visualised like a Tesla β plus a second, learned stack you train yourself.
Two entire approaches, directly comparable, at 60Β FPS in a browser tab.
~6,400 lines of TypeScript. No game engine. No ML library. No server.
|
Every named algorithm, wired end to end β Frenet lattice planning, IDM/MOBIL traffic, Kalman tracking, Stanley control. Verified headless in Node. |
A pure-TS MLP (backprop + Adam) trained by behavioural cloning + DAgger, and a policy found by neuroevolution β no teacher, no framework. |
Live safety / comfort / efficiency scoring, and a head-to-head benchmark of all three drivers on one identical seeded course. |
Every arrow is a real module. The ego never reads ground truth β it plans off noisy
sensor detections fused by a Kalman tracker, exactly like the real thing.
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#1b1d22','primaryTextColor':'#e8eaed','primaryBorderColor':'#1fd18b','lineColor':'#5fe6ad','fontSize':'14px'}}}%%
flowchart LR
subgraph WORLD["π World"]
ENV["Traffic Β· IDM + MOBIL<br/>Pedestrians Β· Lights Β· Weather"]
end
subgraph PERCEPTION["π Perception"]
SENSE["Sensor<br/>noise Β· occlusion Β· dropout"] --> TRACK["Kalman Tracker<br/>const-velocity + association"]
TRACK --> GRID["Occupancy Grid"]
end
subgraph DECIDE["π§ Decision"]
PRED["Prediction"] --> FSM["Behaviour FSM<br/>cruise Β· follow Β· overtake<br/>yield Β· stop Β· emergency"]
FSM --> PLAN["Frenet Lattice Planner<br/>quintic + quartic Β· cost-ranked"]
end
subgraph ACT["πΉ Control"]
STANLEY["Stanley + PID"] --> BICYCLE["Kinematic Bicycle"]
end
ENV --> SENSE
TRACK --> PRED
GRID --> PLAN
PLAN --> STANLEY
BICYCLE -->|"ego pose"| ENV
LEARN["π Learned driver<br/>MLP Β· imitation / evolved"] -.->|"can replace<br/>Decision + Control"| STANLEY
PLAN -.->|"demonstrations"| LEARN
classDef s fill:#16181d,stroke:#2b2e34,color:#e8eaed;
classDef learn fill:#1a2b22,stroke:#1fd18b,color:#5fe6ad;
class ENV,SENSE,TRACK,GRID,PRED,FSM,PLAN,STANLEY,BICYCLE s;
class LEARN learn;
The stack, module by module β with source links
| Layer | Technique | Source |
|---|---|---|
| World model | Arc-length Frenet frame (station s, lateral d) + curvature over a closed centreline |
world/ReferencePath.ts |
| Perception β sensing | Range + line-of-sight sensor with Gaussian noise, occlusion & random dropout | perception/Sensor.ts |
| Perception β tracking | Kalman-filter multi-object tracker (const-velocity), NN data association, track lifecycle, object classes | perception/Tracker.ts |
| Occupancy | Ego-centred occupancy grid | perception/OccupancyGrid.ts |
| Prediction | Constant-velocity roll of each track's estimated velocity (lateral term for pedestrians) | planner/FrenetPlanner.ts |
| Behaviour | Finite-state machine: cruise / follow / overtake / emergency / yield / stop | behavior/BehaviorPlanner.ts |
| Planning | Frenet lattice: quintic + quartic polynomial trajectories, cost-based selection, collision checking | planner/FrenetPlanner.ts Β· core/poly.ts |
| Control | Stanley lateral tracker + PID longitudinal | control/ |
| Vehicle | Kinematic bicycle model | vehicle/Vehicle.ts |
| Traffic | IDM car-following + MOBIL lane changes | traffic/ |
| Urban | Traffic lights with stop-line planning | world/TrafficLight.ts |
The entire simulation core imports zero Three.js and runs headless in Node β which is exactly why every claim below is a passing test, not a screenshot.
Every box in that diagram is a real equation the car solves ~60 times a second.
Here is the whole stack, straight from the source β nothing hand-waved.
The world is re-expressed along the road: station
so the car knows precisely how hard to slow for a bend, capping speed at the lateral-acceleration limit
β world/ReferencePath.ts Β· sim/Simulation.ts
Each frame samples a lattice of manoeuvres. Lateral motion is a quintic (six boundary conditions β position, velocity and acceleration pinned at both ends); longitudinal is a quartic (terminal position left free):
Every candidate is scored, and the car draws the arg-min:
colliding candidates take a +10βΆ penalty, dynamically-infeasible ones +10β΅ β planner/FrenetPlanner.ts Β· core/poly.ts
Ambient cars follow the Intelligent Driver Model β a desired gap, and an acceleration that closes it:
and change lanes by MOBIL only when the net incentive beats a threshold β never braking the new follower past
β traffic/IDM.ts Β· traffic/MOBIL.ts
Steering drives both the heading error
Speed is a PID on the target-speed error; the vehicle itself is a kinematic bicycle β nonholonomic, "can't slide sideways":
β control/Stanley.ts Β· control/PID.ts Β· vehicle/Vehicle.ts
Each tracked object is a constant-velocity state
the ego plans off the estimate, never ground truth β perception/Tracker.ts
The neural driver is a tanh MLP with a linear head, trained to minimise MSE against the expert's action:
Gradients flow back through the tanh, and Adam takes the step:
Ξ²β=0.9, Ξ²β=0.999, Ξ΅=10β»βΈ, Xavier init, tanhβ² = 1βaΒ² β learn/NN.ts
With no teacher, genomes are scored by the worst of two rollouts (so the winner must generalise, not memorise), then bred by tournament selection + uniform crossover + annealed Gaussian mutation:
β learn/Evolution.ts
And all three drivers are ranked on one honest scale:
β sim/Metrics.ts
Geometry to gradients β that's the whole car, ~6,400 lines of it.
A pure-TypeScript MLP with backprop + Adam (learn/NN.ts) learns to drive from a
16-feature view of the road. Two ways to teach it β watch the loss curve fall in real time.
|
The classical stack drives; the network records
β |
A genetic algorithm evolves the weights, scored purely by driving-rollout fitness (distance minus penalties for leaving the road or hitting traffic). Selection + crossover + mutation discover a driver from random weights.
β |
A safety shield (AEB + lane-keeping + virtual guardrail) guards the learned drivers in play β
but switches off during evolution, so fitness judges the raw policy, not the shield.
Everything is verified headless and deterministically in Node (seeded RNG) β no browser,
no hand-waving. Reproduce every number yourself (see Run it).
In-browser head-to-head β all three drivers over one identical seeded course. Classical wins on balance;
the evolved policy is fastest but rougher. All three: zero collisions.
smoke.ts β the classical stack across 9 scenarios
[highway] 0.74 km CRUISE crashes 0 ped-hits 0
[highway@fast]1.09 km CRUISE crashes 0 ped-hits 0
[stalled] 0.51 km CRUISE Β· OVERTAKE crashes 0 ped-hits 0
[cutin] 0.47 km CRUISE Β· OVERTAKE crashes 0 ped-hits 0
[trucks] 0.58 km CRUISE Β· OVERTAKE Β· FOLLOW crashes 0 ped-hits 0
[crossing] 0.53 km CRUISE Β· YIELD Β· OVERTAKE Β· STOP crashes 0 ped-hits 0
[occluded] 0.58 km CRUISE Β· OVERTAKE Β· YIELD Β· STOP crashes 0 ped-hits 0
[jaywalker] 0.53 km CRUISE Β· YIELD Β· STOP crashes 0 ped-hits 0
[rush] 0.62 km CRUISE Β· YIELD Β· STOP crashes 0 ped-hits 0
SMOKE PASS β all scenarios: on-road, zero collisions, pedestrians safe.
|
|
|
Plus lights-test.ts (stops on red, goes on green) and weather-test.ts (rain/fog degrade the sensor). All green.
Highway Β· Trucks (overtake a convoy) Β· Stalled car Β· Cut-in Β· Dense traffic Β·
Crossing pedestrian Β· Occluded ped (steps out from behind a stalled car, seen late) Β·
Jaywalker (emergency stop) Β· π¦ Lights Β· π Rush hour
Deep-link any of them: ?scenario=occluded&cam=top&weather=rain
npm install
npm run dev # β http://localhost:5173
npm run build # typecheck + production bundle β dist/ (drop on any static host)Headless verification β reproduce every number above, no browser needed
npx tsx scripts/smoke.ts # 9 scenarios: on-road, zero vehicle & pedestrian collisions
npx tsx scripts/learn-test.ts # the neural net learns to drive (imitation + DAgger)
npx tsx scripts/evo-test.ts # neuroevolution discovers a driver from random weights
npx tsx scripts/lights-test.ts # ego obeys the traffic light
npx tsx scripts/weather-test.ts # rain/fog degrade perceptionEach asserts real, measurable behaviour β stays on road, zero collisions, loss falls, fitness climbs, red light obeyed.
Keyboard Β Β·Β 1β9 scenarios Β Β·Β T train Β Β·Β E evolve Β Β·Β C/N/V switch driver Β Β·Β Space pause Β Β·Β R reset Β Β·Β B benchmark
TypeScript Β· Three.js Β· Vite. Fixed-timestep deterministic physics & planning, decoupled from the render loop. The neural networks β MLP with backprop + Adam, and the genetic algorithm β are written by hand; there is no TensorFlow, no PyTorch, no ONNX. No backend, no build-time secrets. It's a static site.
The render layer (Three.js + UnrealBloom, the green path ribbon, candidate-trajectory viz, the Tesla-style DOM HUD) is a thin skin over a headless core β which is why the same code that renders in the browser is the code the tests drive in Node.
- Classical AV stack β Frenet planner, IDM/MOBIL, Stanley + PID, Kalman tracking
- Pedestrians + hard cases β crossing / occluded / jaywalker
- City map β rounded 90Β° junctions, cross-streets, traffic lights
- Imitation learning driver β behavioural cloning + DAgger
- Neuroevolution driver β learn from scratch, no teacher
- Analytics β live scorecard + head-to-head benchmark
- Weather β rain/fog that degrades perception
- Intersections with cross-traffic & turns
- Reinforcement learning (policy-gradient) driver
- Recording & replay
Built by @codewithfourtix β perception, planning, and a little bit of evolution.




