// Lights out — open-source race engineering

A pit wall for fans.

Broadcasts show what happened. A pit wall knows what's next. Three live-timing feeds, one normalized model, transparent predictions on top — a race engineer on your couch.

0
Series, one data model
0
Open codebases
0×
Replay speed, full weekends
0%
Self-hosted & local-first
Box box
Live timing Strategy AI SignalR decoded Record / Replay Transparent models Self-hosted
F1
// Sector 01 — Formula 1

Every sector. Live. Decoded.

Formula 1 logo

The official live-timing SignalR feed, reverse-engineered end to end: negotiate, subscribe, inflate the compressed deltas, deep-merge into a living NormalizedState. A Bun + TypeScript monorepo serves it to a React dashboard with the PredictionEngine fanning out over WebSockets.

01SignalR feed
livetiming.formula1.com
02negotiate
handshake
03websocket
connect
04Subscribe
Streaming/Subscribe
05inflate(.z)
decompress deltas
06deep-merge
state store
07NormalizedState
one model, every series
Pitlane Live — replay fixture Replay 5×
P1VRDScarlet GP 28.34131.09224.778LEADER
StintM · Lap 18
P2KSLAzure Racing 28.40231.24424.701+1.247
StintH · Lap 11
P3ONZMeridian F1 28.56631.18724.890+3.815
StintM · Lap 6
P4HAVScarlet GP 28.47131.39624.933+5.002
StintS · Lap 3
P5RKOBorealis 28.61031.04425.011+7.420
StintM · Lap 22
▴ Hover a row — tyre stints expand. Purple = session best, green = personal best. Fictional data from a replay fixture.
F2
// Sector 02 — Formula 2

Same model. New grid.

Formula 2 logo

The F2 feed speaks a different dialect — streaming/GetData2, clientProtocol 2.1 — but lands in the same NormalizedState. Predictions and views are series-agnostic, and F2 rules come built in: the Feature-race mandatory pit stop, F2 points, F2 format. Hover a model to read its math.

Model 01

Quali pace

Who takes pole, from evolving track grip and each driver's push-lap deltas.

StatisticalFlip ▸
Inside the box

Readable math

pace = best_lap − grip_evolution(t) ± push_delta · σ(last 3 runs)

No black boxML-swappable
Model 02

Race pace

True long-run speed, corrected for fuel burn and tyre phase — not headline laps.

StatisticalFlip ▸
Inside the box

Readable math

pace = median(clean laps) − fuel_corr · lap + tyre_deg(compound, age)

No black boxML-swappable
Model 03

Race outcome

Finishing-order probabilities, updated every lap as stints and gaps evolve.

StatisticalFlip ▸
Inside the box

Readable math

P(finish) = f(pace_delta, pit_window, track_position, deg_curve)

No black boxML-swappable
packages/core

The shared contract — normalized data model (Zod) and prediction types.

packages/ingest

SignalR client, decompression, delta merge, state store — and the session recorder.

packages/predictions

Transparent statistical models for pace, quali and outcome.

apps/server

Broadcast + prediction host. REST /api/snapshot, WebSocket fan-out.

apps/web

The dashboard — Vite + React + Zustand, per-session views.

apps/ingest-cli

Record a live session, or replay a captured one at any speed.

WEC
// Sector 03 — Endurance

24 hours on your lock screen.

FIA WEC logo

Endurance racing outlasts your attention span — so the WEC companion follows you instead. A FastAPI backend on a Mac mini polls public timing endpoints behind a single swappable adapter; a native SwiftUI app connects over Tailscale and pins the race to your Dynamic Island.

WEC
Live Activity · Hypercar
Car #8 — 14:22:51 to go
P2gap +3.2s
Lap 198FCY ▴ S7
▴ Hover the island — Live Activities via APNs push
01
Live Activities, pushed

Race state on the lock screen and Dynamic Island, with optional APNs pushes — no app open, no stream running.

02
One adapter, swappable source

FastAPI serves /api/sessions/live and per-session snapshots; the timing source can be swapped for an official feed later.

03
Self-hosted ops, for real

launchd services on a Mac mini, Tailscale serve for secure remote access, a pytest suite and live-session verification scripts.

// Parc fermé — the killer feature

Record it. Replay it. Backtest it.

Full race weekends are captured as replay fixtures. That means offline development, demos without a live session — and the real prize: backtesting strategy calls against what the teams actually did.

RECingest-cli — replay
$ bun run replay -- sample --speed 5
fixture loaded monza-race.ndjson
frames 1,284,003 · cars 20 · laps 53
inflate(.z) → deep-merge → NormalizedState OK
replaying at — predictions live on :3000
● record tonight's session: bun run record -- --session quali
// Pit board — what's next

The road to a virtual race engineer.

LAP 01
Backtest the strategy calls

Undercut windows and pit timing, scored against actual team decisions across recorded weekends.

LAP 02
Plain-English narration

Live strategy talk — "box now, the undercut is open" — generated from the prediction stream.

LAP 03
Sharper F2 field shapes

Refine per-field JSON shapes from more live session captures.

LAP 04
Licensed data sources

Official feeds slotted in behind the existing adapters — the architecture is ready.