Skip to content
All builds

TonnageWorkout Logger

What it does for you

A phone-first strength log with a progression engine, rest timer, plate calculator, and hand-drawn SVG charts of your progress.

Tonnage screenshot

Screenshots

Tonnage screenshot 1
Tonnage screenshot 2
Tonnage screenshot 3

In more detail

Tonnage is a phone-first gym log. You fill it in between sets, it tells you what to lift next, and it draws your progress as you go. No account: routines, a rest timer, plate and 1RM calculators, and charts, all in the browser.

Honest limits

Not tested by hand: the rest timer across a real phone sleep, file import from a phone's file picker, and a real multi-week log. The smoke test covers the click paths in headless Chrome only. Storage is localStorage, so it lives on one device. It runs and passes its checks; nobody has trained on it for a month.

How I built it

Claude Code built this over more than one run. The logic core and most of the UI came from earlier runs that stalled on the dropping API stream. The finishing run on September 23 took about 15 minutes.

The core is lifts.js: units, Epley and Brzycki 1RM formulas, plate math, PR detection, progression advice (add load, add reps, or deload after three stalls), CSV parsing, and a seeded 12-week demo. The UI is a thin layer of small view modules and SVG chart helpers on top. I set the spec, the phone-first priority, and what progressive overload should look like.

The finishing run was mostly about reading its own screenshots, and they showed real bugs. The demo seed missed reps so often that the bench chart ended on a deload, and on phones the bottom tab bar was drawn over the header. It fixed both, then added verify checks that every referenced file exists and that no em dash appears anywhere in the folder.

What I learned

  • backdrop-filter on a sticky header becomes the containing block for position: fixed children, so the bottom tab bar pinned to the header on phones. Dropping the filter under 760px fixed it.
  • Seeded demo data is a design decision, not filler. The first seed triggered the deload rule twice, which made a working progression engine look like a failing lifter. After tuning the miss rate, bench e1RM climbs from 181 to 244 lb over 12 weeks and the chart tells the feature's true story.
  • Headless Chrome will not render a viewport narrower than about 512px, so real phone screenshots need a 390px iframe wrapper.
  • A rest timer that stores an absolute end time cannot drift when a phone sleeps. That is simpler and more correct than any interval countdown.
  • Many small modules and edits under 100 lines meant a dropped call cost minutes, not the file.

What carried forward

This finished on the tiny-writes pattern from the #31 pilot. The honest part: #33 Tidemark had already hit the same backdrop-filter bug three days earlier, and the lesson did not travel, because it lived in #33's case study and not in the brief this builder read. That is a real argument for feeding learnings back into the brief, not just logging them. The pure core plus verify.mjs pattern goes back to #9.

If I took it further

I would move sessions to IndexedDB with a sync option, then spend a few real weeks logging with it on a phone.

#034 Built with Claude Code September 23, 2026 285 automated checks

Want one like this?