The Pain Forecast
Jun 2026
The Pain Forecast is a cycling performance tool that treats climbing like a weather report. You give it your drivetrain, weight, and FTP (threshold power), and it issues an honest forecast for any climb: how much power the gradient demands, what "warning level" you're riding into, and exactly where you'll crack.
🔗 Live: https://painforecast.jechocarlos.dev
What it does
- Gear Performance Table (the hero) — a live heatmap of power demand across cadence × grade, graded against your FTP and colour-coded on a weather-warning scale (clear → yellow → amber → red). Green tiles are sustainable; red means you're cracked.
- GPX / TCX route analysis — drop in a route file and it detects every climb, searches every gear in your drivetrain for the cheapest way up, and returns a per-climb verdict (Yes / Hold on / Brace) plus an estimated time to the top.
- ETA-matched route weather — give a start time and average speed and it fetches the hourly forecast (temp + feels-like, rain chance, wind speed and direction) for each point along the route, matched to when you'll actually get there. Powered by Open-Meteo — free, global, no API key.
- Carbo-load fuel planner — turns a target distance and intensity zone into a three-day carbohydrate plan, localised for Filipino riders as cups of cooked rice and ulam.
- "Ride the Forecast" climb simulator — a tap-to-pedal physics game built on the same engine: a singlespeed bike that only moves when you pedal, with anaerobic energy (W′) depletion, balance/traction at low speed, and crashes.
How it's built
- Next.js 16 (App Router) + React 19 + TypeScript (strict), deployed on Vercel.
- Pure, framework-free physics engine — speed, power
(
grade·m·g·v + Crr·m·g·v + ½·ρ·CdA·(v+headwind)²·v), gear search, climb detection, and the game's fixed-timestep simulation all live inlib/as pure functions, fully unit-tested with Vitest. The UI is a thin render layer. - Everything runs client-side — GPX parsing, the power model, and the weather calls all happen in the browser. No backend, no database, no env vars.
- Dynamic social cards — a shared link unfurls on social with that exact setup's forecast (verdict, FTP, weight, gear, pace) rendered into the preview image via a Next.js OG route.
- Installable PWA — offline-capable with a network-first service worker so deploys never serve stale chunks.
- Shareable, persistent state — inputs, units, and theme persist in localStorage; a Share link reproduces the entire setup.
Engineering highlights
- Separating the pure model from the UI kept the whole physics layer testable
in isolation and let me reuse the same
power()function across the heatmap, the route forecast, and the climb game. - A deliberate design split documented in the code: the table runs on one selected gear (cadence and gear both set speed, so "best gear per cell" is ambiguous), while route analysis auto-picks the best gear per climb.
- Custom design-system components and a token-based theming layer with a day/night palette that follows the OS with no flash on load.
- Calculations are grounded in real cycling physics (rolling resistance, aero drag, air density, headwind) — presented honestly as estimates, not guarantees.
Stack: Next.js · React · TypeScript · Vitest · Open-Meteo API · PWA · Vercel