@slow-bloom/runner-tools
v0.2.0
Published
Pure client-side exercise science algorithms, running pace formulas, and track utilities.
Downloads
161
Maintainers
Readme
🏃 Runner Tools
Pure client-side exercise science algorithms, running pace formulas, and track utilities with zero dependencies. Built for runners, coaches, and sports data hackers.
Live Demo
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
Highlights
- 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.
Installation
# 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/[email protected]/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.
Quickstart
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);
Modules & Documentation
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 ($T_2 = T_1 \cdot (D_2/D_1)^b$) | 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 |
Operating Ranges & Principles
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 |
Contributing
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:websiteLicense
MIT License © 2026 Slowbloom Studio
