Workout Tracker

paused

Full workout tracking system with 2+ years of gym data and visualizations

Tech Stack

Node.jsSQLiteRemotionReactTypeScriptTailwind CSSdayjs

TL;DR

Notes app to SQLite database to visualization

Notes

December 9, 2025 at 11:18 PM

Workout

10/1
  –  weight: 210
  –  CGBP - 160 5x12
10/4
  –  weight: 207.9
  –  Bench - 190 5x8
  –  Leg Press - 295 5x12
10/5
  –  Weight: 208
  –  narrow grip lat pull down - 135 5x5
10/6
  –  Weight: 206.8
  –  squat - 190 5x5
  –  Barbell shrug 190 5x6
10/7
  –  weight: 206.6
  –  Narrow grip lat pull down - 140 5x5
  –  Bench: 195 5x6
  –  Machine pec fly: 4x12 115
10/8
  –  weight: 206.2
  –  Shoulder press: 95 5x5
10/9
  –  weight: 207.8
  –  Squat: 195 5x5
  –  Bench: 200 5x5
  –  Machine pec fly: 4x12 120
  –  Barbell Shrug: 195 5x6
10/15
  –  weight: 210.5
  –  Shoulder press 100 5x5
  –  Leg press 300 5x12
  –  Narrow grip lat pull down 145 5x5
10/16
  –  weight:209
  –  Bench: 205 5x5
  –  Barbell Shrug: 200 5x6
  –  EZ barbell curl: 75 3x12
  –  Rope push down: 47.5 4x12
  –  Machine pec fly: 4x12 125
10/17
  –  weight: 208.3
  –  Squat: 200 5x5
  –  Narrow grip lat pull down 154 5x5
  –  Seated leg curl: 100 3x12
  –  Leg extension: 95 3x12
  –  Upright supinated row: 85 3x12
10/22
  –  weight: 210.3
  –  Bench: 210 5x4
  –  Narrow grip lat pull down 160 5x5

Sample of raw workout data from Notes app

workouts.db
sqlite> SELECT
  ec.name,
  COUNT(*) as sessions
FROM workout_exercises we
JOIN exercise_catalog ec
  ON we.exercise_id = ec.id
JOIN workouts w
  ON we.workout_id = w.id
WHERE ec.name = 'Bench'
  AND w.date LIKE '2024%'
GROUP BY ec.name;

name  | sessions
------|----------
Bench | 63

SQLite query results

30-second animated progress video

What We (✨Claude✨) Built — Claude also wrote this summary because I am incapable of writing

Phase 1

Data Pipeline

Two years of gym notes, zero consistency. Some days I'd write Bench - 190 5x8, other days Bench: 5x8 190 with the order flipped. Pull-ups were just a list of reps: 10, 8, 6, 5. And I never once wrote the year.

The parser handles 10+ format variations using sequential regex matching. It tracks context too - when it sees 185 5x5, it knows that's a pyramid set for whatever exercise came before.

Result: 3,112 lines → 421 workouts, 2,305 exercises, normalized SQLite schema

Phase 2

Progress Visualization

With clean data, I used Remotion to generate a 30-second TikTok-style video (1080x1920, 30fps). Built in React with pure SVG - no charting libraries.

The trickiest part was the animated weight chart. Two bugs kept breaking it: the Y-axis would jump as new data appeared (fixed by calculating scale upfront), and layout shifted at frame 60 (fixed by always rendering at least one point).

Features: GitHub-style heatmap, animated weight chart (210→232.5 lbs), travel break highlights, TikTok-safe zones (12% top, 10% bottom)