Your comfortable temperature range and riding speed live in Settings (the gear in the header).
Every score starts at 10.0 and subtracts penalties for the conditions. The result is clamped to 1–10. If a measurement is unavailable, its penalty is skipped rather than counted as zero — unknown is not the same as perfect.
Penalties
- Wind (0–4): ≤10 km/h (0), ≤20 (−1), ≤30 (−2), ≤40 (−3), >40 (−4). Headwind ×1.3, tailwind ×0.7, crosswind ×1.0.
- Gusts (0–1.5): based on the gap between gust and mean wind — that spread is what actually moves the bike.
- Temperature (0–6): free inside your comfort band (15–25°C by default), then growing with distance outside it. Uses feels-like when available.
- Wind chill on the bike (0–1.5): only below freezing. Computed from the airspeed you actually meet at riding speed, which is why a road pace chills harder than a trail pace.
- Precipitation (0–5): the worse of how likely rain is and how hard it is falling.
- Humidity (0–2): ≤60% (0), ≤80% (−1), >80% (−2). At ≥90% the total is capped at 4.
- Visibility (0–3): ≥10 km (0), ≥5 (−1), ≥2 (−2), <2 (−3).
- UV (0–1.5): ≤5 (0), ≤7 (−0.5), ≤9 (−1), above (−1.5).
- Surface (gravel and MTB only, 0–3): from the forecast model's soil moisture, which already accounts for drying by sun and wind. Both ends cost: saturated is mud, bone-dry is loose and blown out, and "tacky" in between is free. Where soil moisture is unavailable it falls back to rainfall over the last 48 h (gravel) or 72 h (MTB).
Per-discipline weighting
The same penalties are weighted differently per discipline, because the same weather is not the same ride:
| Factor |
Road |
Gravel |
MTB |
| Wind | ×1.0 | ×1.5 | ×0.7 |
| Gusts | ×1.0 | ×1.0 | ×0.6 |
| Precipitation | ×1.2 | ×1.0 | ×0.9 |
| Surface / mud | — | ×0.8 (48 h) | ×1.0 (72 h) |
Hard ceilings
Some conditions cap the score no matter how good everything else is: extreme heat (cap 2.0), thunderstorms (1.5), freezing rain (1.5), near-freezing with precipitation (2.5), and snow (2.0 road / 3.0 off-road).
Score bands
🟢 8–10 Excellent · 🟡 6–8 Good · 🟠 4–6 Fair · 🔴 2.5–4 Poor · ⛔ below 2.5 Skip it. The same band drives the badge, the colour, and the wording everywhere in the app.
Note: wind is treated as a crosswind by default, since your route direction is unknown.
🚴🏼 A bit more context: Blog post on the project.