For Everyone
Racing & planning
Race comparison — side-by-side, same-format default
Compare your finished races head-to-head. PR badges mark the best value for each metric; the default filter is the latest race's distance so you only ever see comparable events.
What it is
The race comparison page puts your finished races side by side
in a table, one race per column. It surfaces splits, placement,
FIT-derived per-leg stats (HR, NP, IF, TSS, pace, decoupling, W'
burned, CdA), and total energy.
For each metric where "best" is well-defined — lowest split,
highest power, lowest decoupling — the column holding that PR
gets a PR badge on the cell.
Where to find it
- On /my-races/ — when you have 2 or more past races, the
Past Races header shows a "Compare past races →" orange
pill. Click it to open the comparison page filtered to your
latest race's distance.
- Direct URL: /my-races/compare/?athlete_pid=...
Comparing a Sprint finish time against a Long-distance one is
mostly noise. By default the page loads with the distance
filter set to your most recent finished race's distance —
so a 70.3 racer lands on a 70.3-only comparison, not a mixed
muddle.
Filter chips at the top let you switch to any other distance
you've raced — only distances you've actually finished show
up as chips, with a count of how many races each contains.
The "All" chip removes the filter.
What gets a PR badge
The platform marks the row whose value is "best" for that metric.
"Best" depends on the metric:
| Metric type |
Best when… |
Examples |
| Times |
lower |
Finish, swim, T1, bike, T2, run |
| Placement |
lower |
Overall #, AG # |
| Power / NP / IF / TSS |
higher |
Bike avg W, Bike NP, Run IF, ... |
| Speed |
higher |
Bike km/h |
| Pace |
lower |
Run pace, Swim pace |
| CdA |
lower |
Bike CdA (more aero = lower) |
| Decoupling |
lower |
Bike/Run Pa:HR drift |
Some metrics intentionally have no PR badge:
- Heart rate — higher isn't better, lower isn't better; it's
context-dependent (heat, hydration, fitness, freshness).
- Total calories — depends on race duration and format, not
a performance metric.
- Pacing verdict — "negative / even / positive" is a label,
not a scalar.
A PR only appears when at least 2 races are in the filter.
A single-race comparison won't show any PR badges — there's
nothing to compare against.
What's sourced from where
The comparison reuses the same recap builder that powers the
post-race recap card and PDF. There's a single source of truth,
so a number on the comparison page will always match the number
on the corresponding race's recap.
- Times + placement come from the Race row (
actual_*,
overall_place, age_group_place).
- Per-leg power / pace / HR / cadence come from a matched
Activity (FIT file). If no FIT is uploaded for that race, those
cells show "—" and the row is hidden if no race in the comparison
has any value.
- CdA comes from the bike FIT via the same back-solver the
CdA calculator uses (
estimate_cda_from_activity).
- W' burned + match count come from the per-second power
stream via the Skiba model (needs CP + W' on file for the athlete).
- Calories come from the recap energy builder.
Hiding empty stuff
To keep the page readable:
- Empty rows are hidden — if no race in the current filter
recorded a value for a metric, that row doesn't render.
- Empty group cards are hidden — a "Run — FIT-derived" block
with zero data for all races in the filter won't show up at all.
This means the page gets richer as you upload more FIT files
and log more races.
Caveats
- IF / TSS comparability — these are normalised against your
current FTP / CP on file. If your threshold has shifted between
two races and you bumped MetricHistory, the older race's
IF / TSS reflects the threshold at the time of the recap
build (we re-fetch the latest, not the historical). Treat
cross-block IF deltas as directional, not exact.
- CdA comparability — air density varies with temperature and
altitude. The CdA card on each individual recap shows the
density source (FIT pressure / FIT-derived from altitude /
default sea-level). Two races at very different altitudes won't
give cleanly comparable CdAs without that context.
- Pacing verdicts are based on first-half vs second-half
averages over the full activity. A multisport FIT's bike leg
is split by the child-Activity record, so the halves are within
the bike, not the whole race.
What's still on the roadmap
- Cross-athlete comparison (coach-side) for teams targeting
the same race
- Trend sparklines in each row showing year-over-year drift
- Goal vs actual column showing how each race did against
its pre-race coach goal
See also