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.




