Peaktime - Trail Running
The run module estimates trail running time using the Grade Adjusted Pace (GAP) model. GAP accounts for the metabolic cost of running on hills — steep uphills are slower, and moderate downhills are actually faster than flat ground.
Basic Usage
import * as run from 'peaktime/run';
import { parseGPXOrThrow } from 'peaktime';
const route = parseGPXOrThrow(gpxContent);
// Quick estimate without a full GPX point array
const minutes = run.quickEstimate(
15000, // distance in meters
600, // elevation gain
600, // elevation loss
'trained',
'good_trail'
);The GAP Model
Grade Adjusted Pace converts hilly running into equivalent flat-ground effort. The model is based on research by Minetti et al. (2002) and used by platforms like Strava.
Key insight: the most efficient running grade is about -9% (slight downhill). Steep downhills are actually harder than flat ground because of the braking forces involved.
The gapMultiplier function returns a pace multiplier for any grade:
import * as run from 'peaktime/run';
run.gapMultiplier(0); // 1.0 — flat ground (baseline)
run.gapMultiplier(10); // >1.0 — uphill is slower
run.gapMultiplier(-9); // <1.0 — slight downhill is fastest
run.gapMultiplier(-20); // >1.0 — steep downhill is slower than flatPace Calculation
For a given flat pace and grade, calculate the adjusted pace:
const adjustedPace = run.paceForGrade(
5.0, // flat pace: 5:00 min/km
8, // 8% uphill grade
1.2 // terrain multiplier (optional)
);Fitness Levels
| Level | Flat Pace Range | Description |
|---|---|---|
beginner |
Slower | New to running |
recreational |
Moderate | Regular runner |
trained |
Moderate-fast | Structured training |
competitive |
Fast | Racing regularly |
elite |
Very fast | Top amateur |
ultra |
Variable | Ultra-distance specialist |
Terrain Types
| Terrain | Description |
|---|---|
road |
Paved surface |
track |
Running track |
good_trail |
Well-maintained trail |
technical_trail |
Rocky, rooty terrain |
alpine |
Above treeline, exposed |
sand |
Beach or desert sand |
Segment-Level Detail
For GPX routes, the model calculates per-segment pace and GAP:
const params = run.getDefaultRunningParams('trained', 'good_trail');
const estimate = run.estimateRunningTime(route.points, params);
console.log(estimate.movingTime); // Moving time in minutes
console.log(estimate.totalTime); // Including breaks
console.log(estimate.averagePace); // Overall min/km
console.log(estimate.gapPace); // Grade-adjusted paceEach segment shows the grade effect:
for (const seg of estimate.segments) {
console.log(`Grade: ${seg.gradePercent}% → GAP: ${seg.gapMultiplier}x → ${seg.paceMinPerKm} min/km`);
}Equivalent Flat Distance
Calculate how far the same effort would take you on flat ground:
const flatDist = run.equivalentFlatDistance(
15000, // actual distance in meters (unused, for API consistency)
estimate.movingTime, // moving time in minutes
params.flatPaceMinPerKm // flat pace in min/km
);
// A 15 km mountain run might equal 22 km on flat groundPlanning a Run
Use the run planner to find the optimal start time:
const summary = run.createRunPlanSummary(route, new Date('2026-06-21'), 'sunrise');
console.log(summary.plan.startTime);
console.log(summary.plan.runningDuration);
console.log(summary.plan.feasible);Format the result:
console.log(run.formatStartTime(summary.plan.startTime, 'America/Denver'));
console.log(run.formatPace(summary.estimate.averagePace));