The Problem
Watching an F1 session replay means juggling a video player and a timing screen that are out of sync. Live sessions have the same problem: the official timing feed is delayed, and there is no easy way to overlay car positions, telemetry, and race control messages on the actual footage. This repo builds a single web app that syncs real-time data to a replay or live broadcast, so you watch one screen instead of two.
What This Does
F1ReplayTiming is a Next.js/FastAPI app that renders live and historical F1 sessions with a synced track map, leaderboard, telemetry charts, and race control messages. The backend (backend/main.py) pre-processes session data via FastF1, stores it locally or in Cloudflare R2, and serves both the API and the static frontend from one port. The frontend (frontend/src/app/live/page.tsx) handles the live view; frontend/src/app/replay/page.tsx handles replays.
The core feature is synchronization. For replays, you can upload a screenshot of the timing tower and the app matches it to the session data (backend/routers/sync.py). For live sessions, it connects to the F1 SignalR stream (backend/services/live_signalr.py) and applies a broadcast delay slider.
How It Is Wired
Execution starts at lifespan in backend/main.py, which sets up the app and reaches 18 functions. The two entry points that do the real work are start in backend/routers/live.py (reaches 21 functions) and main in backend/compute_pit_loss.py (reaches 51 functions, called from one place). auth_middleware in backend/main.py guards the API with a passphrase token generated via SHA-256.
Everything routes through backend/services/r2_storage.py — its get_json is called from 13 places, put_json from 5. It is the storage hub for all session data. backend/services/f1_data.py is the other hub, defining 34 functions that fetch schedules and session data from FastF1. backend/services/live_state.py holds 32 functions and 2 classes that parse and sanitize live telemetry frames.
A request path looks like this: start -> get_json -> _get_client (network via boto3.client) for R2 storage. There is no circular dependency in the import graph, but the deep nesting (max depth 8) in backend/routers/live.py and backend/services/f1_data.py makes the control flow hard to follow.
How To Use It
The README documents a Docker-based setup. Clone, copy .env.example to .env, and run:
git clone https://github.com/moses-y/F1ReplayTiming
cd F1ReplayTiming
cp .env.example .env
docker compose up
The defaults work without extra config. Set PORT in .env to change the port. Sessions are processed on demand, or you can pre-compute them in bulk via backend/precompute.py.
Real-World Use
A fan runs this on a home server during a race weekend. They open the live view, connect to the F1 SignalR stream, and watch the track map update every 0.5 seconds with car positions. After the race, they switch to replay mode, upload a screenshot of the timing tower from their recording, and the app syncs the telemetry to the video.
Code Health & Issues
Static analysis found 37 issues (8 high, 29 medium) across 6 kinds. The high-severity ones:
- High - Duplicated code blocks - 255 repeated 6-line blocks across 14 files, including
backend/compute_pit_loss.pyandbackend/compute_pit_loss_v2.py. Extract shared helpers. - High - Deep nesting - 18 instances of indentation depth 8 in
backend/routers/live.py,backend/services/f1_data.py, andbackend/services/live_signalr.py. Flatten with early returns. - High - Oversized files -
backend/services/f1_data.py(1357 lines),backend/services/live_state.py, andfrontend/src/app/live/page.tsx. Split by responsibility.
SDLC gaps: no LICENSE file (default is all rights reserved), no test suite despite 57 source files, and unpinned GitHub Actions (docker/login-action@v3 etc.) that should be pinned to commit SHAs.
The Bottom Line
A functional, feature-dense F1 timing app that solves a real synchronization problem. The architecture is clean on the surface — single container, no CORS — but the backend has accumulated significant duplication and oversized modules that will make changes risky. Suitable for a technical F1 fan who wants a self-hosted timing overlay; needs a license and tests before it is production-grade for a client.