# I built the running coach that actually listens to your body

> Source: <https://dev.to/anshsaini/i-built-the-running-coach-that-actually-listens-to-your-body-5bmd>
> Published: 2026-10-17 10:27:36+00:00

*This is a submission for the [Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle](https://dev.to/challenges/hacktoberfest-week2-2026-10-12)*

I run with Nike Run Club. It tracks every run well, but its plans aren't personal. They don't know my heart rate zones, my actual paces, or what my last few weeks looked like. A tempo session says "comfortably hard" whether my resting heart rate is 50 or 70, and the plan doesn't notice when I miss a week or double my mileage. I wanted a coach that looks at my data and changes the plan because of it.

**OpenStride** is an iOS running coach that reads your data from Apple Health and plans your week around it:

Every week, Gemma (Google's open-weight model) reviews those signals, adjusts the plan, and tells you why in plain words. It runs on the iPhone through MLX, so the app needs no account or server. With the on-device model (the default), your health data stays on the phone.

One-time purchase on the App Store and Google Play: **$10 (₹599 in India)**. No subscription or account needed.

Adaptive training plans are already a paid category (Runna, TrainingPeaks), sold as monthly subscriptions. OpenStride can skip the subscription because the coach runs on the phone: after someone buys it, they cost nothing to serve. The app is iOS only today; the Play Store version comes with Android.

Here's how it would look on the App Store ([full sample listing](https://ansh-saini.github.io/open-stride/appstore/)):

**Demo page with the full 60 s video: [ansh-saini.github.io/open-stride](https://ansh-saini.github.io/open-stride)**

Recorded on my iPhone with a runner who doubled their mileage last week. Gemma 3 4B runs on the phone; the planning step is sped up.

| The week | A session | Signals | What the coach saw | 
|---|---|---|---|

**An AI running coach that reads your body and runs on your iPhone.**

OpenStride reads your runs and heart rate from Apple Health, works out your zones and training load,

and plans each week with Gemma 3, an open-weight model running on the phone.

[**Demo video**](https://ansh-saini.github.io/open-stride)  ·  [** Store listing**](https://ansh-saini.github.io/open-stride/appstore/)  ·  [** How it works**](https://github.com/ansh-saini/open-stride#how-it-works)  ·  **Results**

Nike Run Club plans are templates. They don't know your resting heart rate, your actual paces, or that you doubled your mileage last week. OpenStride builds the plan from your own numbers and changes it when they change.

| What it reads | How |  | 
|---|---|---|
| **Heart rate zones** | Resting HR and the highest HR seen in your runs | Karvonen method | 
| **Pace zones** | Your best recent effort, or pace plus heart rate | Jack Daniels' VDOT | 
| **Training load** | Every run's duration and heart rate | Banister TRIMP, 7-day vs 28-day ratio | 
| **HR drift** | First half vs second half of |  | 

I created several modules which work together in steps to deliver an optimial running plan:

`acute_chronic_load_ratio = 1.98 -> FLAG: above 1.5, injury risk`.` make_easy`, `shorten` or `drop` a day, each with a reason.
If the model fails, a rule-based coach writes the week, so the week still gets planned.

I built six synthetic runners with seeded histories and an expected coach behavior for each:

| Runner | Signal | A good coach should | 
|---|---|---|
| steady | all in range | keep progressing | 
| overreaching | load ratio 1.98 | cut volume, max one hard day | 
| detrained | 21 days off | no hard sessions | 
| hr_drift | 7.7% drift | slow easy pace | 
| missed_sessions | did 20% of last week | not add volume | 
| beginner | 8 km a week | progress gently | 

`coach-eval` runs them through Ollama, and `mlx-eval` runs them through the exact MLX weights the phone uses. Each run is scored on whether the decision parsed, whether the final plan is right, whether the model got it right without a guardrail floor, and whether the note cites a real number.

| Coach | Runs | Valid | Final plan right | Model right on its own | Note matches plan | Note cites the flagged number | Avg time | 
|---|---|---|---|---|---|---|---|
| rules | 6 | 100% | 100% | 100% | 100% | 100% | 0.0s | 
| gemma3:1b (Ollama) | 18 | 100% | 100% | 33% | 100% | 100% | 1.0s | 
| gemma3:4b (Ollama) | 18 | 100% | 100% | 100% | 100% | 100% | 4.8s | 
| Gemma 3 4B (on-device) | 18 | 100% | 100% | 100% | 100% | 100% | 8.0s | 
| Gemma 3 1B (on-device) | 18 | 100% | 100% | 33% | 100% | 100% | 4.3s | 

My own runs exposed two things the personas didn't. None of my runs from the last four weeks have heart rate recorded, so there's no drift signal, and the coach has to work without it. And my "best effort" wasn't all-out, so the race formula underrated my fitness. Now, when heart rate exists, VDOT is also estimated from pace and heart rate reserve, and the higher estimate wins.

SwiftUI, HealthKit, Swift Charts, XCUITest on simulator and device, [MLX Swift LM](https://github.com/ml-explore/mlx-swift-lm) with `mlx-community/gemma-3-text-4b-it-4bit` (2.6 GB, downloaded once), Ollama for the simulator and evals, XcodeGen.

**Privacy that holds up.** Heart rate, resting heart rate and VO2 max are health data. With an open-weight model on the device, the coach has no server to send your data to. A closed API needs one.

**Zero marginal cost.** Every plan runs on the phone's GPU. No per-token bill means a solo developer can price this as a one-time purchase instead of a subscription that has to cover inference.

**Works offline.** After the one-time download it works on a trail with no signal, which is where runners are.

**Inspectable and swappable.** The Insights screen shows exactly what the coach saw and what it answered. Because the weights are open, I could test the same model on my Mac, in the simulator and on the phone, and I can swap in a newer Gemma or a fine-tune without changing the app.

The most common mistake runners make is doing too much too fast. A good week feels great, so the next one gets longer and faster, and the body's tendons and bones can't keep up with the lungs. A static plan can't see that coming. OpenStride watches for it every week:

In the demo, the runner doubled their mileage in one week (load ratio 1.98). The draft plan had 31 km for the next week; the coach cut it to 25 km, kept one hard day, and explained why in the note.

My first user is me. I ran the device test on my own iPhone with two months of my real runs: 22 runs, mostly 3 to 5 km, averaging about 14 km a week.

What it got right: it planned four easy runs and a slightly longer Sunday run, about the same volume I've been doing, at an easy pace close to how I actually run. No signals were flagged, and the note said so instead of inventing a problem.

What it taught me: the first estimate of my fitness was too low, because my "best" run wasn't an all-out effort. That's why the app now also estimates fitness from heart rate. And none of my runs since September 21 have heart rate recorded, so there's no drift to read and the coach is working with less than it could.

I had a ton of fun building this project as it solves a real problem for me, and I know for a fact that many people face this problem. I'm super excited to integrate this with Apple Watch and seeing how that transforms my running experience. It will give me access to real-time heart rate tracking and session feedback, ensuring the coach receives complete data for more accurate planning. Maybe the coach can say "Hey you're going too fast, slow down" into my earphones during my run itself.
