mjdc.io Get in touch →
← Back to the notebook

Project

Timing climbs with a broken clock

Estimating a pro’s watts per kilo starts with a climb time, and the race ticker’s clock reads zero. So I rebuilt it from scraps.

8 October 2026 · Michael Conroy

The number everyone argues about in pro cycling is watts per kilo, and you can get surprisingly close to it from your couch. You need the climb’s length, the height it gains and how long the rider took to get up it. Physics does the rest. That’s the idea behind Poursuivant, which estimates how hard the pros climb.

It grew out of an earlier project of mine, cycling-power: a Python version of the standard physics model, plus 3,618 hand-timed TV climb times from climbing-records.com, a community archive. It averaged within about 2% of Lanterne Rouge’s published numbers. In July I started again from scratch, with the old repos as a parts bin and Claude Code doing most of the typing. The rebuilt engine averages 1.59% error against 14 of Lanterne Rouge’s figures. That’s across eight ascents, and it’s agreeing with one very good analyst rather than a power meter. And two of the 14 are their raw W/kg, which isn’t quite what I measure. From a couch, I’ll take it.

The physics turned out to be the easy part. The hard part was the time.

The clock reads zero

Nobody publishes a climb time for most stages. You get finish times, and hand-timed archives cover the famous climbs. For everything else, the best record of what happened is the live race ticker on ProCyclingStats (PCS): every attack, every gap, every rider who’s been dropped. Each entry has a timestamp.

In the archive I validated against, 683 of 684 of those timestamps are zero. The separate timing fields for points along the route were empty in every archive I checked, from 2022 and 2025.

So the ticker tells you everything that happened in a race except when.

The only hard anchors left are t = 0 at the start and each rider’s finish time. Everything in between has to be rebuilt.

Rebuilding a climb time from scraps

Poursuivant uses four kinds of evidence.

Kilometre splits. From 2025 on, PCS publishes per-kilometre times for the front rider on final climbs. When they exist, they win.

Gap reports. “Situation at X km to go”, followed by each group and its time gap. These do most of the work. If the leader’s clock says he hit 7.4 km to go at a certain time, and the ticker says Vingegaard was 45 seconds behind, then I know roughly when Vingegaard got there too.

Finish times. Riders within 3 seconds of each other at the line are a group, and that group gets carried backwards up the road until some piece of evidence splits it.

Physics, for the gaps in between. Time gets shared out by gradient, not distance, because a steep kilometre takes longer than a flat one. On top of that, the climbs get an effort factor of 1.6, because pelotons soft-pedal the valleys and race the climbs that matter.

That factor is the main thing I tuned, against 72 climb times hand-timed by climbing-records.com across 15 Giro, Tour and Vuelta stages from 2022 to 2025. The median error is 69 seconds on summit finishes and 67 on mid-stage climbs. Those are the same 72 rows I tuned on, so I’m marking my own homework. Leaving each row out in turn still gives medians of 63 to 69 seconds.

Tom Pidcock was two people

The first honest pass had a median error of 144 seconds on summit finishes. Then I started carrying each gap report onto every named rider, which should have helped, and the error went up, to 160.

The bug was names. The ticker and the results use different URL names for the same rider, so thomas-pidcock in one place was tom-pidcock in the other. Whole gap reports were silently dropped or pinned on the wrong person. That one bug accounted for most of the early error. Fixing names and nicknames, plus a first go at the effort factor, got it to 126 seconds. Tuning got it to 69.

In a physics project, the bug that mattered most was string matching. I’d like to say I saw that coming.

Ten seconds on Hautacam

Stage 12 of the 2025 Tour finished on Hautacam: 13.5 km at 7.9%. Pogačar attacked about 2.5 km into the climb and rode away from everyone. It’s also the one stage in my set with kilometre splits for the leader, so it’s the best case. His fifth kilometre took 1:59, which is 30 km/h up a mountain. The eighth took 3:21.

Behind him the evidence is a handful of gap reports. At 10.2 km to go: Vingegaard 5 seconds down, Roglič, Lipowitz and Gall at 22, Evenepoel at 1:39. At 7.4 km to go: Vingegaard at 45 seconds, Roglič’s group at 1:32, Evenepoel at 1:48. Ben Healy is 5:52 down in both reports, nearly three kilometres apart, which is either remarkable consistency or a stale number. The pipeline has to cope with both.

Poursuivant’s reconstructed time for Pogačar was 35:17. The hand-timed figure is 35:07. Ten seconds out. Vingegaard came back 27 seconds slow, and Roglič, Lipowitz and Gall 11 seconds each. Evenepoel, Vauquelin, Onley and Johannessen were the miss, each about a minute too fast. Across all nine riders the median error was 27 seconds, and hand timing itself is only good to about 5.

Here it is drawn out. Pogačar’s line runs flat along the top, and the chasers fan out behind him like the wake off a boat.

Poursuivant

Time gaps on Hautacam, Tour de France 2025

In the archived race tickers, 683 of 684 events are stamped zero. So Poursuivant pieces each climb back together from the gaps, the finish times and some physics, then checks its work against times clocked off the TV.

Every ticker event on this climb is stamped 00:00. Rebuilt from the gaps and Pogačar’s kilometre splits, his 35:07 climb comes out at 35:17, ten seconds out. For the nine riders below, the median miss is 27 seconds. Over 47 hand-timed summit-finish times it blows out to 69.

    Hautacam, foot to summit, timed by hand and rebuilt by Poursuivant
    RiderHand-timedRebuiltMiss
    Pogačar35:0735:17+10 s
    Vingegaard37:1737:44+27 s
    Lipowitz37:3037:41+11 s
    Johannessen38:0736:58−69 s
    Onley38:0736:58−69 s
    Vauquelin38:4037:38−62 s
    Evenepoel38:4237:38−64 s
    Gall39:0939:20+11 s
    Roglič39:1539:26+11 s

    Gaps and splits come from public race reports, and the hand times from climbing-records.com. The rest is Poursuivant. Between reports the lines are a smooth guess. Summit gaps assume everyone hit the bottom of the climb together.

    Below 6%, it’s an air race

    On a climb, your power goes three places. Most of it lifts you and the bike. Some of it shoves air out of the way. A little goes into the tyres and the drivetrain. Gravity is simple to model. It only needs weight and how fast you gained height. Air is the problem. It depends on speed cubed, your drag area, the air density and the wind, and that last one is rarely known well.

    On Hautacam the model puts about 84% of Pogačar’s power into gravity and under 10% into air. That’s the good regime. Get the time right and the power follows.

    The number Poursuivant reports is what Lanterne Rouge call étalon W/kg, after the French for a reference standard. It’s the W/kg a 60 kg reference rider would need to match the same time. It needs no rider weight, so a group, or a rider whose weight nobody publishes, still gets a number. Pogačar’s Hautacam comes out at 6.8.

    Now flatten the road. For a 60 kg pro holding 6 W/kg, air costs as much as gravity at a gradient of just 2.4%. Into a 3 m/s headwind, it’s 3%. (For a 75 kg rider at 3 W/kg, the crossover is 1.4%. For the rest of us, every real climb is a gravity problem.)

    The Poggio is 3.7 km at 3.9%. At record pace, a 3 m/s headwind would cost the 60 kg reference rider about 46 seconds. An extra 5 kg would cost about 10. On the Zoncolan, at 12.2%, it flips. Five kilos costs 2:39 and the headwind 2:12. Run the Poggio record through the same model and it prints an étalon of about 9, more than any mountain ascent in the archive, which tells you more about the air than about the riders.

    So Poursuivant doesn’t publish below a 6% average gradient, or for efforts under 10 minutes. Lanterne Rouge draws the line at 4%. I’d rather publish less.

    Even above the line, wind matters. A steady 3 m/s headwind on Hautacam would have cost Pogačar 3:15 at the same power. He beat Vingegaard up the climb by 2:10. That’s why every number comes with an error band, ±6% when I trust the wind estimate and it’s calm, ±10% otherwise.

    Some of my numbers are impossible

    Before a number goes on the site, Poursuivant checks the gradient, duration, confidence and geometry. What it doesn’t check is whether the answer is possible.

    Right now 17 rows on the site claim a higher étalon than the best hand-timed ascent in the archive, Pogačar on Plateau de Beille in 2024 at 7.1. The worst says 8.4. That isn’t a leap in human performance. It’s a bad input wearing a ±6% band. A ceiling on étalon and VAM (metres climbed per hour) is next on the list.

    The bands also leave out timing error. They only cover the model and the wind. A 69-second miss on a 35-minute climb is about 3% of the time, so roughly 4% of the power, and none of that is in the band.

    What I’d tell you to steal

    Pogačar’s Hautacam came back ten seconds off. Not bad for a clock that reads zero.

    More from the notebook.All writing →