Back to Projects
Building·Since Jul 2026

Wodmark

A training log for functional fitness. Every block is a triple: what's held constant, what's measured, how it scores.

ReactTypeScriptViteRadix UIRepo
Wodmark

Why I built it

Functional fitness doesn't log the way a normal lifting app expects it to. An EMOM and an AMRAP can use the exact same movements and still be completely different things to record — one holds time constant and measures work done, the other holds work constant and measures time. Most training-log apps flatten that into a generic “workout” and lose the distinction.

Wodmark is built around a different unit: a block is a triple — what's held constant, what's measured, how it scores. An EMOM and an AMRAP built from identical movements come out as genuinely different objects, because they are.

Wodmark's gym-admin week view, authoring blocks for each day

The bet on privacy

Gym staff can see aggregates, never a named athlete's log, and aggregates only render once five or more athletes are logging — below that, the dashboard shows “suppressed” instead of a number. That's enforced in the API layer, not a setting someone could toggle off, because a privacy policy I could turn off isn't one I trust either.

Who pays

The category's usual model charges the gym and gives the athlete a login. I inverted it: the athlete pays, and gyms get the authoring tools for free as a distribution channel. It's a bet that athletes who actually want their history to survive a gym switch will pay for it themselves, and that gyms will hand out a free tool more readily than they'll buy one.

Wodmark's athlete app, showing today's workout

Where it actually is

Three consoles are running against seeded, in-memory data — app-admin, gym-admin, and the athlete view — with 140 tests covering the domain logic. There's no real backend yet; the API layer is deliberately built as the seam a real one plugs into later, not a shortcut I'm hoping to forget about.

What I'd tell someone building this

Model the domain before the schema. The block-as-triple idea only showed up after I'd tried and rejected two flatter data models — the extra week spent arguing with myself about it was cheaper than the migration would have been.

React · TypeScript · Vite · Radix UI