Citius Chrono

Race results rebuilt from finish-line video.

What was the problem?

My running club’s head coach, Dany, organized a college cross-country race, and I was in charge of publishing the results. The finish was timed by hand: one volunteer saved a time each time a runner crossed the line, and two of us wrote down bib numbers further down the chute. When runners arrived in groups and swapped places, their times swapped too, and nobody could tell which results were wrong.

What was the idea?

I knew two iPads had filmed the finish, so I asked for the videos. I figured I could set up a system that analyzes them: find the exact moment each runner crosses the line, and read each runner’s bib number from the video.

What did I build?

I built a one-off tool for this one race, with Claude. I gave it the two videos and the registration lists. We lined up the two recordings by their sound, and read the race clock in the video, so any frame gave a race time. A first try with Apple’s Vision framework kept losing runners between frames, so we switched to tools made for video: RF-DETR finds each runner, and ByteTrack follows them across the line. Gemini reads a small crop of each bib, and every bib had to be on the race’s registration list. I checked the last 20 or so unclear cases myself. Then I shipped a small web app to replay the finish, with both videos in sync and the ranking filling in as runners cross. I made it a web app because I wanted to see it on a big screen, and share it with some friends through a link, with no app to download.

Citius Chrono. Rebuilding the results, in one day: on my two Macs, the two iPad videos were lined up by sound, RF-DETR and ByteTrack found the frame where each runner crossed the line, and Gemini read small crops of each bib. I decided about 20 unclear cases myself. The results and the videos went to Cloudflare. When someone watches the replay: the browser, behind a private access code, asks a Worker for the pages, and the Worker streams the videos from R2.

USER’S DEVICECLOUDFLAREMY MACSOUTSIDE SERVICESABresults + videosCTwo iPad videosLined up by soundRunner trackingRF-DETR, ByteTrackGeminiReads the bibsI decided about20 unclear casesThe replayResults, videos in R21streams2BrowserSynced replayWorkerServes the pagesnot involvednot involvednot involved

What happened?

Both results lists ended up with the same 437 finishers: the one we wrote by hand during the race, and the one I rebuilt from the video. 430 of the 437 times agreed within half a second, and in four places the order was different. I watched those four finishes in the replay, and the video order was the right one.

I sent the corrected list to Dany. In the end, the system I built gave us the right results.

Say hello

Have a project in mind, a role you think I’d be interested in, or a question about something I’ve built? Send me a note.

hello@victorc.dev