Run315

This app against your watch

Where the two methods land

6 compared · 0 genuinely apart

Two estimates agreeing is worth more than either alone. Two disagreeing tells you which question to ask. Neither of them is ground truth — this app infers your threshold from the top of your hard running, Garmin infers it from the shape of your heart rate against your speed, and both are guesses about the same underlying thing.

Threshold heart rate

175 this app 171 Garmin

+4 bpm apart · agree

Both methods put your threshold within a few beats of the same place, which is about as much agreement as either can offer.

Threshold pace

4:22 /km this app 4:32 /km Garmin

-3.8% · near

Garmin's threshold pace detection moved from a chest-strap protocol to automatic detection from wrist heart rate in late 2024. Independent validation of the wrist-based version is thin, and one study measured it reading nearly 12% slow.

Race projections

two models, same runs

DistanceThis appGarmin Apart
5.0 km 19:10 19:07 +0.3% agree
10.0 km 41:52 41:38 +0.6% agree
21.1 km 1:37:07 1:35:57 +1.2% agree
42.2 km 3:32:06 3:27:01 +2.5% near

The two models agree at some distances and not others, which points at the shape of the curve rather than at its level -- the fitted fade exponent is where to look.

Garmin's own, with no equivalent here

52.0
VO2max · Garmin
This app computes none on purpose.
7100
Endurance score
68
Hill score
Our version tests the energy model instead.

No VO2max is computed here, and that is deliberate. A VO2max derived from heart rate and pace is a lossy compression of the heart-rate/pace relationship into one number using a population economy curve — it adds a familiar unit and a false precision, not information. Garmin's carries a mean error around 5–8% with a chest strap, and worse at both ends of the fitness range.

, so the check has to wait for the parsed document. addEventListener('DOMContentLoaded', function () { try { var dm = ['standalone', 'minimal-ui', 'fullscreen', 'window-controls-overlay']; if (!document.getElementById('frozen-note') && (navigator.standalone || dm.some(function (m) { return matchMedia('(display-mode: ' + m + ')').matches; }))) { var pk = 'portal-installed', reg = JSON.parse(localStorage.getItem(pk) || '{}'); reg.Run315 = Date.now(); localStorage.setItem(pk, JSON.stringify(reg)); } } catch (e) { /* private mode - ignore */ } }); // Installable, and readable on the train. See templates/sw.js. if ('serviceWorker' in navigator) { addEventListener('load', function () { navigator.serviceWorker.register('/Run315/sw.js'); }); }
Static demo. A synthetic athlete, frozen to files — every page here is the real app rendering real analysis, but anything that writes (Garmin sync, adding a race, the coach review) is switched off, because a file server has nothing to write to. Source & how to run it for real.