Pure client-side exercise science algorithms, running pace formulas, and track utilities with zero dependencies. Built for runners, coaches, and sports data hackers.
Experience these algorithms live in action on the web:
- VDOT & Training Pace Calculator
- Race Predictor
- Heart Rate Zones Calculator
- Age-Grading Calculator
- Running Efficiency Calculator
- Pace & Split Calculator
- Running Track File Converter (δΈζ)
- GPX & FIT Track Comparator (δΈζ)
- GPS Difference Analyzer (δΈζ)
- Cadence Metronome & Audio Generator (δΈζ)
- Race Week Planner
- Runner Strength Timer (Chinese)
- Weekly Mileage Ramp-Up Calculator
-
Pure & Zero Dependencies: 100% pure TypeScript formulas with zero runtime dependencies. Runs anywhere: Node.js (dual ESM and CommonJS with full
.d.ts/.d.cts), browsers, Bun, Deno, and Cloudflare Workers. - Scientifically Grounded: Implements Daniels & Gilbert's oxygen consumption models, Riegel's endurance power laws, Karvonen heart rate reserve equations, and World Masters Athletics (WMA) road standards.
-
Built-in i18n & Extensible: Multilingual support with hierarchical locale fallback (
exact tag$\to$ base language$\to$ English default) and deep custom dictionary merging. -
Strict Null Safety: Clear contracts where invalid, unphysiological, or unsolvable inputs return
nullinstead of throwing or generatingNaN. - No Silent Clamping: Discontinuous boundary cases and out-of-domain offsets are explicitly exposed rather than clamped silently.
- Local File Conversion: Read FIT, GPX, TCX, KML and CSV; export all five plus GeoJSON. A standalone Web Worker keeps decoding, editing and encoding off the browser's UI thread, without CDN parser imports or file uploads.
- Honest Track Comparison: Align tracks on overlapping timestamps or explicitly labeled route progress, interpolate mismatched sampling rates, and keep measured device distance separate from GPS-derived estimates.
- Deterministic Interactive Tools: Audio scheduling follows the Web Audio clock, strength intervals follow monotonic elapsed time, and race-week calendar output is testable and locale-aware.
# npm
npm install @slow-bloom/runner-tools
# pnpm
pnpm add @slow-bloom/runner-tools
# yarn
yarn add @slow-bloom/runner-toolsOr directly via CDN in HTML:
<script src="https://cdn.jsdelivr.net/npm/@slow-bloom/runner-tools@0.2.0/dist/runner-tools.global.js"></script>
<script>
const result = RunnerTools.calculateVDOT({ distanceMeters: 5000, timeSeconds: 1200 });
if (result) {
console.log('VDOT:', result.vdotFormatted); // "49.8"
}
</script>The browser bundle and dist/runner-tools.worker.js are supported distribution
artifacts. Host both on the same origin when using createFileConverterClient;
the worker installs a message handler and should not be imported into application
code as a regular module.
import {
calculateVDOT,
predictRaceTime,
calculateHeartRateZones,
solvePace,
parseGPX,
serializeToTCX,
analyzeTrack,
createCadenceMetronome,
createStrengthTimer,
createRaceWeekPlan,
} from '@slow-bloom/runner-tools';
// 1. Calculate Daniels VDOT & training paces
const vdot = calculateVDOT({ distanceMeters: 5000, timeSeconds: 1200 });
console.log('Easy Pace:', vdot?.zones.E.lowPaceFormatted); // "5'03\""
// 2. Predict marathon finish time from a 10K
const marathonSecs = predictRaceTime(10000, 2700, 42195, 1.06); // ~12421s (3:27:01)
// 3. Calculate Karvonen Heart Rate Reserve (HRR) zones
const hr = calculateHeartRateZones({ method: 'karvonen', maxHR: 190, restingHR: 55 });
console.log('Zone 2:', hr?.zones[1].bpmFormatted); // "137 - 150 bpm"
// 4. Solve pace from distance and duration
const pace = solvePace({ distance: 10, timeSeconds: 2700, unit: 'km' });
console.log('Pace:', pace?.paceFormatted); // "4'30\""
// 5. Parse GPX and export as Garmin TCX
const activity = parseGPX(gpxContent);
const tcxContent = serializeToTCX(activity);
// 6. Analyze GPS distance without treating it as ground truth
const track = analyzeTrack(activity);Detailed mathematical derivations, physiological domains, and complete API specifications are documented in dedicated guides:
| Module | Category | Scientific Model / Basis | Documentation |
|---|---|---|---|
vdot |
Formula | Daniels-Gilbert oxygen power equation & E/M/T/I/R training paces | docs/formulas/vdot.md |
race-predictor |
Formula | Peter Riegel's endurance power law ( |
docs/formulas/race-predictor.md |
heart-rate-zones |
Formula | Karvonen (HRR), %MaxHR, and Joe Friel 5-zone LTHR models | docs/formulas/heart-rate-zones.md |
age-grading |
Formula | World Masters Athletics (WMA) 2020 road standards & scoring tiers | docs/formulas/age-grading.md |
running-efficiency |
Formula | Vertical Ratio (VR), Duty Factor (DF), and Aerobic Efficiency Factor (EF) | docs/formulas/running-efficiency.md |
pace |
Formula | 3-way pace/time/distance solver, unit conversions & split tables | docs/formulas/pace.md |
weekly-mileage |
Formula | 10% progression rule, ACWR recovery periodization & deload cycles | docs/formulas/weekly-mileage.md |
files |
Tool | FIT, GPX, TCX, KML and CSV parsing, GeoJSON export, cropping, merging, GPS redaction and local Web Worker conversion | docs/files/running-file-converter.md |
track-analysis |
Tool | Recorded-vs-GPS provenance, timestamp/progress alignment, interpolation, split and drift indicators | docs/files/track-analysis.md |
race-week |
Planner | Localized race-week template, pacing/fueling timeline and RFC 5545 calendar export | docs/formulas/race-week.md |
audio |
Tool | Cadence target/tap tempo, cancellable Web Audio metronome, WAV and optional MP3 export | docs/audio/cadence-metronome.md |
timers |
Tool | Workout schema/presets, timeline, elapsed-time strength state machine and cue adapter | docs/timers/strength-timer.md |
i18n |
Guide | Custom dictionaries, locale registration, and fallback resolution | docs/guides/i18n-and-customization.md |
All algorithms conform to strict physiological domains:
| Module | Input Boundaries | Return Contract on Out-of-Domain |
|---|---|---|
| VDOT | Distance: 400 m β 200 km; Time: 30 s β 100 h; VDOT: 15 β 85 | Returns null |
| Race Predictor | Base/Target Distance > 0 m; Exponent: 1.00 β 1.30 | Returns null |
| Heart Rate Zones | Max HR: 80 β 240 bpm; Resting HR: 30 β 120 bpm (Max > Rest) | Returns null |
| Age Grading | Age: 5 β 100 years; Standard road distances (5K, 10K, Half, Full) | Returns null |
| Running Efficiency | Cadence: 100 β 260 spm; GCT: 100 β 500 ms; Duty Factor < 50% | Returns null for invalid components |
| Pace Solver | Distance: 0.01 β 10,000; Pace: 60 β 3600 s/unit; Exactly 2 defined fields | Returns null |
| Weekly Mileage | Volume: 1 β 500 units; Max Weekly Increase: 1% β 50% | Returns null |
| Track Analysis | 2+ GPS points; comparison samples: 2β10,001 | Throws FileConversionError |
| Race Week | Standard 5K/10K/Half/Marathon; volume: 1β500; frequency: 2β7 | Returns null |
| Cadence Audio | 120β210 spm; export: 1β900 s; WAV: 8β192 kHz | Throws CadenceAudioError |
| Strength Timer | 1β100 exercises; rounds: 1β10; import: β€1 MiB | Throws StrengthTimerError |
We welcome community contributions, sports science peer reviews, and bug reports! Please review CONTRIBUTING.md for local development workflows and test guidelines.
# Run unit tests
npm test
# Verify type definitions
npm run typecheck
# Build dual bundle & browser distribution
npm run build
# Verify the packed ESM, CommonJS, and TypeScript consumer entry points
npm run test:package
# Measure coverage
npm run test:coverage
# In the Apex Run collection checkout, build and copy the browser bundles,
# source maps and license into both ../website/apexrun and ../website/apexrun-zh
npm run sync:websiteMIT License Β© 2026 Slowbloom Studio