Race-time prediction for cyclists with power meters

Predict your race time.

Open-source race-time prediction with per-split surface Crr. Pick a pro race or upload a course, set your FTP, weight, CdA, effort. Get a finish-time prediction plus a head-unit FIT (Garmin Power Guide overlay for Edge 540+, Fenix 7+, Forerunner 955+; FIT Workout for Wahoo, Hammerhead, Bryton, and older Garmin).

Built for races and high-effort days. Honest numbers when you're at your ceiling; loosens up when watts are held back. Validated on pro rides because that's where public power data lives.

The model also tracks the pacing curve across the course, not just the finish. Intermediate checkpoints at Château de Vincennes land within tens of seconds of Remco's real splits. Details on Methodology.

The model targets what a rider can do at their public-data ceiling, not what they did. On the Pogi tile above the +1.49% bias is the slow side: he rode the GC podium-duel ~3 minutes harder than the steady-state model predicts. Effort 1.0 means "you're at your ceiling", not "you'll attack mid-race".

1. Pick a route
2. Your numbers
Rider
Or try as:
Men pro
Women pro
Sex
↑ Pick a route above to continue
Plan and preview free. Email magic-link sign-in needed only to download the FIT (so analyzer abuse can't be anonymous).
Or analyze a past ride →

How accurate is this?

3.76%
MAE on 115 pro rides with measured inputs (CdA, FTP, mass). 95 % CI [3.30%, 4.26%].
6.70%
MAE on 52 amateur rides. 95 % CI [5.68%, 7.76%].

Without measured inputs (FTP + weight + intent only) those land at 6.41% pro and 10.85% amateur: each override the user supplies (real CdA, current FTP, route intent) tightens the prediction. Full breakdown + held-out evidence + OTS-stamped pre-race forecasts on the Methodology page.

Custom-course uploads are end-to-end encrypted. The AES key lives in the URL fragment (the part after #), which browsers don't send to the server. Only someone with the full link can decrypt the route. The server only ever sees ciphertext. Privacy →

Pre-race forecasts for the 2026 spring classics + Tour de Romandie are git-committed and OpenTimestamps-stamped to the Bitcoin blockchain before the gun. The proof says: this prediction existed before the race, no hindsight tuning. Per-race results sit on the Methodology page.

Run it locally

Open source, MIT. Repo flips public with the v1.0 SemVer commit (PyPI publish lands the same day). Until then the link 404s; the install path will work the moment the repo flips.

git clone https://github.com/sam-dumont/bike-power-model
cd bike-power-model
uv sync --extra api --extra analysis
uv run bpm --help

Full install + license + integration notes on Developers.

Why this exists

One thing led to another. The original goal was a single Paris-Roubaix Challenge: prep a plan, write it onto the Edge as a Garmin Power Guide, ride. Turned out the two FIT messages behind Power Guide (352 and 353) weren't documented publicly, so they had to be reverse-engineered from binary dumps before anything else.

Once the format was writable, what to write was the next problem. Garmin's native plan only sees gradient: not Arenberg cobble, not a 33 km/h Zeeland headwind, not the mud-slick descent out of La Redoute. So the next step was a Martin-1998 power balance to drive better targets. Then per-surface Crr because cobble isn't tarmac, asymmetric wind because real weather isn't uniform, tier-aware draft schedules because pros, cat-2s, and sportive groups don't shelter the same. Heat / altitude / fatigue / sector-aware rain decay piled on after. A long thread of "OK the prediction is wrong on this stage, why?" → fix → next stage. Methodology →

bikepowermodel.fit · open-source race-time prediction · per-split surface Crr · Garmin / Wahoo / Hammerhead / Bryton