How it works

Methodology

Power Predict turns your Strava archive into a sustainable-watts forecast in four steps: extract per-activity power streams, build a mean-maximal-power curve, fit a critical-power model, and apply a fatigue decay for endurance durations.

1. Per-activity power streams

Each FIT file in your Strava archive contains a 1 Hz stream of power-meter readings. Power Predict reads these in your browser and builds a per-activity mean maximal power array — the best average watts you held across every duration window from 1 second up to several hours, using a sliding-window scan over prefix sums.

Activities without a power stream (older rides, phone-only recordings) are skipped. Only the derived MMP arrays are persisted; raw streams stay in browser memory just long enough to compute, then get discarded.

2. Rolling-best aggregation

We take the maximum across activities at each duration to build an aggregate MMP curve, computed in three windows:

Power Predict fits the model on Last 90 days by default and falls back to All time if 90 days has too few points in the fitting window.

What "best" means

Each value in the table is mean maximal power: the best rolling average power held for that duration in any activity in the window. It is not normalized power. A 20-minute MMP of 280 W means there is a 20-minute slice of one ride that averaged 280 W — nothing more.

Anomaly filter (power-meter glitches)

Real cycling power kinetics bound how much shorter durations can exceed longer ones — even a world-class sprinter's 1-second peak sits roughly 1.5-1.8× their 5-second peak. A power-meter spike (e.g. a single 2000 W sample on a normal ride) or a flatline glitch (a 2-second plateau at 1367 W where the meter reported the same wattage twice in a row) produces ratios well above plausible.

Before merging each activity's MMP into the rolling-best, Power Predict checks adjacent-duration ratios against caps tuned to the upper edge of trained-cyclist data. Any short-duration value that breaches its cap relative to a longer reference is dropped from that activity only — the rest of the activity (longer durations, average power, etc.) still feeds the curve. Other rides' best efforts at the dropped duration carry the table.

Effort-quality filter

Recency weighting on its own can underread your fitness when recent rides have been low intensity — a 30-day base block of zone-2 spinning gets weighted heavily even though those rides don't reflect what you can do at threshold.

Power Predict estimates your FTP from your all-time best 20-minute mean maximal power (Coggan: FTP ≈ 0.95 × MMP20m) and computes an intensity factor (IF = average power / FTP) for each activity. Activities with IF below 0.70 (roughly tempo or harder) are dropped from the recency-weighted regression input. Hard rides anchor the fit; easy rides don't.

The filter is always on. Activities are dropped per-render with no user toggle — the goal is honest data, not knobs.

Why no recency weighting

Earlier versions of Power Predict applied an exponential recency weight on top of the 90-day window. After testing, that turned out to over-penalize older efforts: physiologically, a trained rider's capacity changes only a few percent over weeks, not the dramatic decay implied by a half-life shorter than a couple of months. Within a 90-day window, peak power is peak power — if you hit it once, you can almost certainly hit it again.

The "soft training block" case — recent zone-2 rides making the rider look less fit than they are — is handled by the effort-quality filter rather than time-weighting. Easy rides are excluded from the regression input; the rider's real efforts within the window anchor the fit.

3. The Critical Power model

Power Predict tries the 3-parameter Morton model first and falls back to the classic 2-parameter form when the data is too sparse for the extra parameter to behave.

P(t) = CP + W′ / (t + W′ / (Pmax − CP))

where

With τ = W′ / (Pmax − CP), the model collapses to a linear regression for any fixed τ: P = CP + W′ / (t + τ). We dense-grid τ in [1, 90] s, solve CP and W′ in closed form for each candidate, and pick the τ with minimum residual subject to physical sanity bounds (CP ∈ [50, 600] W, W′ ∈ [1, 60] kJ, Pmax ∈ [CP+100, 2500] W).

The 3-parameter form lets the fitting window extend down to 30 seconds where the 2-parameter hyperbola would predict runaway power. When fewer than three points fall in the fit range, or no τ produces a sane combination, Power Predict falls back to the 2-parameter form fitted on 180–1200 s, which only needs two anchor points but distorts below 3 minutes.

Either way the result reports CP, W′, RMSE, and the number of fit points; the 3-parameter fit also exposes Pmax in the CP cell tooltip and labels the predict block "3-param CP" rather than "2-param CP".

Why the fit line doesn't pass through every dot

The CP fit is a 2-parameter hyperbola — it has only two knobs (CP and W′) to fit your whole power-duration profile. It approximates your data with a smooth curve; it can't connect every observed dot exactly. Add the recency weighting on top (older observations count less in the regression) and the fit can sit visibly below your raw rolling-best, especially during base blocks where recent rides are softer than older peaks.

The dots are history — what you actually rode. The line is the model's smooth forecast of what you can sustain. If the line reads conservatively, you have two levers:

Why 3-20 minutes

Below about 3 minutes, anaerobic / neuromuscular contributions distort the simple two-parameter relationship. Above 20 minutes, fatigue and substrate-depletion effects pull power below the model's prediction. Fits done outside this window are unreliable.

4. Fatigue decay for endurance durations

If you ask the raw CP model what you can hold for 8 hours, it will answer "approximately CP" — the asymptote. That's wrong: real cyclists fade well below CP over those durations as glycogen runs low.

For predictions beyond the 20-minute window we apply a Riegel-style power-law decay:

P(t) = Panchor × (tanchor / t)k

The anchor is whichever is later: your longest observed MMP point, or the 20-minute model prediction. Anchoring at real data when available means a 12-hour forecast is grounded in your actual 4-hour or 6-hour ride, not extrapolated all the way from the CP fit.

The fatigue exponent k defaults to 0.15. Skiba publishes 0.07, which fits the 1–4 hour range; observed pro and amateur ultra-endurance data falls off faster than that. A gentle default leaves long-duration predictions too optimistic, so Power Predict defaults k to 0.15 — a firmer falloff partway to the 0.20 fatigue clamp — which avoids the trap of ultra predictions reading too close to CP.

When your archive contains at least three MMP points between 20 minutes and 4 hours, Power Predict fits a personal k by linear regression on log−log MMP data: log P = ak · log t. The fitted value replaces the default at predict time, both in the chart's fatigue line and in the predict form. The result is clamped to the 0.04–0.20 envelope to ignore degenerate fits driven by a single low-effort long ride. The Fatigue k stat in the predict block reports which value is in use and how many points it was fitted on.

DurationTypical fraction of CPPower Predict default
1 hour≈ 95%≈ 89%
4 hours≈ 85%≈ 80%
8 hours≈ 75%≈ 75%
12 hours≈ 68%≈ 72%

Without observed long-duration MMP, the 1-hour and 4-hour numbers run a touch low — that's a known trade-off of the steeper k. Once your archive contains long rides, the anchor moves to those real points and the prediction tightens up.

The "extrapolated" badge in the predict UI marks any duration outside your observed MMP range. The confidence band widens logarithmically with how far out you've reached.

References

Fitness drift on the all-time fallback

When the last-90-days window has too few MMP points to fit a model, Power Predict falls back to your all-time data. The all-time set is dominated by peak efforts that may be years old, when the rider was demonstrably fitter (or less fit) than today — an unmodified fallback would anchor the fit to a stranger.

To correct for this we estimate eFTP at each activity's date as 0.95 × best 20-min MMP across the surrounding 90 days, then anchor eFTPnow from the same window ending today. Each activity's MMP values are scaled by eFTPnow / eFTPthen before merging into the all-time rolling-best, with the ratio clamped to [0.7, 1.3] so a sparse historical window can't produce wild adjustments. Activities whose window has no 20-min effort pass through unchanged.

Drift normalization only feeds the fit's fallback path. The displayed all-time table column stays raw history — the dots are still what you actually rode. eFTPnow is reported as a stat on the predict panel.

Manual mode (no archive)

For users who haven't uploaded an archive but know their FTP, the landing page exposes a "no archive yet" panel that synthesizes a CP/W' fit on the fly: CP ≈ 0.95 × FTP (Coggan: FTP is roughly 1.05 × CP). When the rider also enters a 1-minute sprint number, W′ is solved from the 2-parameter hyperbola at t = 60 s. Otherwise W′ defaults to 18 kJ — the middle of the trained-cyclist range. Result is clamped to [5, 40] kJ so a wildly out-of-range sprint number can't produce an absurd hyperbola.

Manual mode is intentionally coarse. It can't anchor the long-duration end of the curve to real data, so 4-hour and 12-hour predictions fall back to the model's pure decay rather than observed history. Upload an archive when possible — the prediction quality improves materially.

Form-based prediction nudge (CTL / ATL / TSB)

Power Predict computes the standard cycling training-load triumvirate from your activity history and uses it to nudge predictions toward your current state of rest or fatigue:

The predict block surfaces TSB as a Form stat. A small multiplier flows from TSB back into the prediction: peaking riders get a few percent up, deeply fatigued riders get a few percent down, capped at ±5 % so this is a nudge, not a rewrite. The output's confidence band carries the same multiplier.

The motivating case: a rider with a high CP who's spent 30 days on zone-2 base reads low on the rolling-90-day MMP because the recent rides aren't peak efforts. CTL stays high, ATL drops, TSB swings positive — the form adjustment recognizes they're rested and lifts the prediction by a couple of percent. Conversely, after a hard block: TSB negative, prediction trims a couple of percent, the model is honest about fatigue.

Limitations and planned improvements

Today's predictions assume your last 90 days of data accurately reflect your current fitness. Issues planned for upcoming releases:

Track these on the project milestones.