A free web app for marathons and half marathons that turns a race course, a goal time and a recent result into a km-by-km pacing and fueling plan with coach-style notes. Built solo, end to end.
Plan overview: goal vs. realistic finish in race-day conditions, course summary and forecast.The same plan on a phone.
Problem
Even splits fail on real courses. Generic pacing tools adjust for elevation at best and ignore race-day weather, wind relative to the course heading, and the late-race fade.
What I built
A free web app that turns a course (a growing list of major races with verified elevation profiles, or any GPX) plus a goal time and a recent result into a km-by-km (or mile) pacing and fueling plan.
Coach-style notes for every segment, a printable pace band, a Garmin course export and guides for COROS and Apple Watch.
Solo, end to end: product, UX, code and infrastructure.
Built for my own sub-3 marathon attempt (current PB 3:02). I'm user #1.
680+ unique visitors and 90+ generated plans since going live in Aug 2026.
A plan narrative costs about USD 0.05, median generation time 18 s.
1,500+ automated tests.
Pace for every kilometer over the elevation profile, with uphill sections and the modeled fade zone.Advanced view: grade, pace, weather adjustment and cumulative time for every kilometer.
Architecture
App
Next.js 15 (App Router), TypeScript and Tailwind CSS, deployed on Vercel with a preview deployment per branch.
Data & auth
Supabase: Postgres plus magic-link auth.
Pacing engine
Deterministic: GPX parsing, segmentation, Grade Adjusted Pace and a late-race fade model. Metric internally, unit conversion only in the presentation layer.
Weather
Open-Meteo forecasts, including wind by course heading. One free forecast refresh before race day re-solves splits and notes together.
i18n
Full EN / DE / PL with next-intl.
E-mail & analytics
Transactional e-mail via Resend. Cookieless, self-hosted analytics (Umami).
Payments
Stripe one-time checkout implemented and kept behind a feature flag. The product is currently free.
The numbers come from the deterministic engine, tested against reference fixtures from courses I've raced (e.g. Warsaw).
An LLM should never invent a split.
<Claude />
The narrative: LLM
Claude (Sonnet) writes the segment-by-segment race narrative from the engine's numbers, in the user's language. It explains the numbers and never computes them.
Structured output with Zod, validators that check every pace and time in the text, one targeted repair call and a fallback on failure.
Model choice: Claude Sonnet, picked after measuring latency, cost and rejected outputs.
Race calendar: Claude Haiku reads race dates from official race sites, and only dates confirmed by a verbatim quote from the source are saved.
Race strategy: pace ranges come from the engine, the segment narrative is written by Claude.
How I built it: AI-native workflow with Claude Code
Project CLAUDE.md
Domain rules, conventions and a definition of done.
Custom slash commands
A feature workflow (explore, plan, approval, branch, code, verify, preview, PR) and a UX audit command.
Code-reviewer subagent
Checks every diff against project rules: units, migrations, i18n, LLM model choice, payment flag.
Post-edit hook
Runs ESLint on changed files.
Preview before production
Every branch gets a Vercel preview, checked on mobile before merge.