We pointed information theory at 25 years of CrossFit and built a tool that grades programming
Every Workout of the Day from crossfit.com since February 2001, 6,800+ workouts, run through Shannon entropy, network science, and 3D visualization. It updates itself every morning and runs with no server.
The problem
Coaches and athletes argue about CrossFit programming with vibes, not data. Is it really "constantly varied"? Which movements actually dominate? Has the methodology drifted over 25 years? The answers were buried in a quarter-century of daily workout posts that nobody had ever pulled together and measured. We scraped all of it, classified every workout by movement, energy system, time domain, and load, then ran the kind of math you would use on a stock portfolio or a communication channel against the question "how varied is this programming, really?" The result is a public tool that turns 25 years of CrossFit.com into something you can actually read.
What we built
The modules of the system.
Today's WOD
The daily entry point. Pulls the live workout from crossfit.com, classifies it on the spot (modality, structure, time domain, load profile), shows the movements it detected, and surfaces the most similar past workouts using Jaccard similarity. A date picker lets you browse and analyze any single day in 25 years. Rest days and journal articles get an amber banner instead of a fake workout.
Report Card
Letter grades, A+ to F, for programming quality. Instead of opinions about whether the programming is good, it scores the actual distribution of movements, energy systems, and time domains and tells you where it falls short.
Variance Analysis
The statistics core. Shannon entropy measures how varied movement selection truly is, the Herfindahl-Hirschman Index (borrowed from antitrust economics) measures concentration, Pareto analysis shows the 80/20 of movements, autocorrelation hunts for periodic patterns, and Markov chains model what tends to follow what.
Network Science
Treats movements as a graph where two movements are connected if they appeared in the same workout. PageRank finds the most central movements, betweenness centrality finds the connectors, and community detection finds the natural clusters of movements that travel together.
Hopper Readiness & What's Missing
A modality-by-time-domain coverage matrix that finds the blind spots. It literally maps which combinations of movement type and workout length have never been programmed, then turns those gaps into plain recommendations of what's under-trained.
3D Visualizations
A Three.js force-directed graph of the movement relationship network you can spin and explore, plus a 3D co-occurrence terrain that renders movement pairings as a landscape using instanced rendering for performance.
Fitness Model views
The CrossFit framework made measurable: a radar across the 10 physical skills, push/pull/squat balance with ratio gauges, phosphagen/glycolytic/oxidative energy-system classification of every workout, and work-capacity analysis across time and modal domains.
Database & decoders
A browsable encyclopedia of every tracked movement, a named-WOD directory (Hero and benchmark workouts), all 6,800+ workouts, and a Workout Decoder that prints a 'nutrition label' for any workout plus a similarity finder. Calendar heatmap gives a GitHub-style 25-year density view, and Year vs Year compares any two years side by side.
How it fits together
The whole app is static. There is no server, no API, and no database at runtime. All 6,800+ workouts live in a single ~2.6 MB JSON bundle that loads once when the page opens, and every analysis (entropy, network graphs, grades, 3D scenes) is computed in the browser in TypeScript. That makes it cheap, fast, and almost impossible to take down. The data stays current through a two-stage autopilot. A GitHub Action runs every morning, scrapes the day's workout from crossfit.com, classifies it, and commits it to main. The scraper is defensive: if the site is down, returns an error, or posts a rest-day article instead of a workout, it exits cleanly without corrupting the data. A cron job on the DigitalOcean VPS then polls main every 30 minutes through the morning window, and the moment a new commit lands it pulls and rebuilds. Caddy serves the fresh static files instantly over HTTPS. No human touches it.
Under the hood
The decisions that mattered.
Borrowing math from economics and information theory to answer 'is it actually varied?'
CrossFit's central claim is that it's 'constantly varied.' That's a measurable claim, so we measured it. Shannon entropy, the same formula used to quantify information in a communication channel, scores how unpredictable movement selection is and normalizes it to a 0-100 variance score against the theoretical maximum where every movement appears equally. To measure the opposite, concentration, we pulled the Herfindahl-Hirschman Index straight out of antitrust economics, where regulators use it to judge whether one company dominates a market. Here it judges whether a handful of movements dominate the programming. Add Pareto analysis (the 80/20 curve of which movements carry the load), autocorrelation to detect periodic cycles, and Markov chains to model transitions, and you get an honest, math-backed answer instead of a slogan. Every function is documented in plain English so a coach, not just a statistician, can read what it means.
A defensive scraper that knows the difference between a workout and an article
crossfit.com doesn't post a clean workout every day. Some days it's a rest day, some days it's a journal article, and the workout text is wrapped in commentary like 'Compare to', 'Stimulus and Strategy', and 'Post time to comments'. A naive scraper would ingest garbage. Ours only scans the first few lines of the extracted text for named-WOD matches and treats everything past the article markers as commentary. The load regex accepts hyphenated weights so '105-lb', '125 lb', and '185lb' all parse. And because the famous benchmark WODs (Murph, Fran, Helen, Grace, Diane, Annie, DT, Cindy) sometimes only get named in the article body, there's a movement-signature fallback that recognizes them by their rep-and-movement fingerprint even when the name is missing. A companion reclassify script can re-run the latest classifiers over scraper-era entries with a dry-run and a date floor, so improving the logic never silently rewrites 25 years of history.
Why we chose poll-and-rebuild over a fixed cron time
GitHub Actions cron is notoriously jittery. The daily scrape is scheduled for 14:00 UTC but can fire anywhere from on time to two-plus hours late. A VPS rebuild pinned to a fixed time would regularly pull before the new commit even existed, and the site would show yesterday's workout. So the VPS doesn't guess. A cron job runs every 30 minutes through the morning window, does a cheap git fetch, and only triggers a build when origin/main has actually advanced. It's resilient to upstream timing and it logs every decision. The payoff is that 'updates daily' is true without anyone watching it.
GPU-friendly 3D that stays readable in both light and dark mode
The movement network has a lot of nodes and edges, and the co-occurrence terrain is a dense grid of movement pairs. Rendering that naively would tank the frame rate, so the terrain uses instanced rendering to draw thousands of cells in one pass. The whole app supports light and dark themes driven by CSS variables and persisted in localStorage, but the 3D canvases are intentionally locked to dark backgrounds in both modes, because WebGL scenes read far better on dark and flipping them would hurt clarity for no benefit. Brand colors and the functional data-viz palette (modality, grade, heatmap colors) are also intentionally fixed, so a theme switch never changes the meaning of a color.
Release log
What we shipped.
Scraped and classified every CrossFit.com Workout of the Day from February 2001 to present, 6,800+ workouts, into a single static JSON bundle with movement, energy-system, time-domain, and load classification.
Built the statistics core in TypeScript: Shannon entropy, Herfindahl-Hirschman Index, Pareto, autocorrelation, Markov chains, plus network science (PageRank, betweenness, community detection) on the movement co-occurrence graph.
Added the Three.js force-directed movement network and the instanced 3D co-occurrence terrain, the 25-year calendar heatmap, report-card grading, and the Workout Decoder.
Shipped the GitHub Action scraper (started running 2026-03-26) and the poll-based VPS rebuild so the site refreshes itself every morning with zero manual steps.
Moved from GitHub Pages to a Caddy static file_server on the DigitalOcean droplet at wod.persistenceathletics.com, with the old GH Pages URL now redirecting.
The outcome
A public, self-running data-science showcase that answers real questions about CrossFit programming with real math instead of opinion. It loads as a single static bundle, computes everything in the browser, and refreshes itself daily with no server and no babysitting. For Persistence Athletics it's both a credibility piece and a programming reference, and for anyone curious it's a free, honest look at how 25 years of the most-watched fitness methodology actually breaks down. It runs in production at wod.persistenceathletics.com.
Put your business on autopilot