🥊 Doctrine: Training That Fits the Time You Actually Have
Most training apps begin with a program and ask you to bend your week around it. Doctrine starts from the other end: how long do you actually have? Pick ten minutes or ninety, and it composes a session — warm-up, main work, cool-down — around the time that survived the day. It is a HIIT app with no account, no ads, no backend, and nothing leaving the device.
What it does
- Generates a session, not a plan. Choose a duration, a level, a focus, the equipment you actually have, and any joints to work around. The session is built in one tap and can be started immediately.
- Coaches out loud. Exercise names, round call-outs, countdown beeps and halfway announcements are scheduled over a limiter, so cues cut through room noise without clipping.
- Shows the movement. An animated figure, authored as joint-angle keyframes, illustrates the active exercise above its name.
- Counts what happened. Completed and abandoned sessions go into history with real elapsed time and a calibrated calorie estimate; the last seven days sum up on Home.
- Shares a workout as an image. A story or square card lists the work with times and a QR code, so someone can follow the session without installing anything.
- Works offline. After the first visit the app shell and the whole catalog are cached, and every feature except the initial download works with the network off.
The generator is the product
Doctrine's generator is seeded: the same seed always produces the same session, and the seed is printed in the workout's name and on its share card. Level is treated as density rather than obscurity — beginner sessions hold roughly 63% work time, advanced sessions 80% — and the round count and work seconds are solved from the target duration, landing within 2% of the time asked for.
Balance is a property of the construction, not a post-hoc check. Blocks rotate through lower body → push → core → cardio → pull; no exercise repeats, no two movements of the same pattern run back to back, and high-impact work is never stacked. Warm-up and cool-down can be switched off, in which case the main work absorbs their time. The last seven days of training are de-prioritised rather than banned. A generated session is a draft until it is kept: drafts survive a reload, expire after three days, and can be re-rolled slot by slot, locked in place, or shuffled whole.
235 movements, with the coaching data attached
The exercise catalog is the single source of truth. Every movement carries its pattern, primary and secondary muscles, equipment, laterality, impact, skill, intensity and MET value, plus a one-line summary and two or three cues. Phase tags keep a static stretch out of a warm-up, regression ladders link each movement to its easier and harder variants, and contraindication tags mark what to avoid for knees, shoulders, lower back, wrists, ankles or pregnancy.
The vocabulary is locked: the editor is picker-only and saving rejects any name the catalog does not know. That is what lets the figure, the calorie math and the instruction copy all resolve to the same movement instead of drifting apart.
A player that keeps time by the clock
The player flattens a workout into a linear segment queue and derives every deadline from Date.now(), so timer jitter cannot accumulate into drift. Pause freezes the remaining time and restores it exactly; skip advances immediately and records the segment as skipped rather than completed. A prep countdown, synthesised rest between rounds, a screen wake lock, a restart confirmation and a completion screen showing real elapsed time round out the session.
Audio is a pure function of the tick: cuesForTick decides what should be heard, and the player only schedules it. That keeps speech, beeps, volumes and the voice picker testable without a browser, and it means the exercise name is announced during the last seconds of the preceding rest rather than after the work has already started.
Privacy by construction
Doctrine is a static single-page app. There is no backend, no authentication and no API call; workouts, history and settings live in localforage on the device. Export and import are versioned JSON files, so the library is portable without a server. The only way data leaves the phone is when the athlete chooses to share a card.
Tech stack
| Technology | Role |
|---|---|
| Ionic 9 + React 19 | App shell, navigation and UI |
| TypeScript | Strict application code |
| Vite | Build and dev server |
| localforage | Client-side storage for workouts, history and settings |
| vite-plugin-pwa | Installable, offline-capable app shell |
| Vitest + Cypress | Unit and end-to-end coverage |
The result
Doctrine is live at doctrine.t3apps.com and installs as a PWA. It is the studio's argument that a training app does not need an account, a subscription or a server to be useful — it needs a catalog worth trusting, a generator that respects the clock, and a player that keeps time when the phone is in your pocket.