← PROJECTS CEBAN MARIUS / PROJECTS / GYMLOG
09 — PWA Development

GymLog.

A mobile-first PWA workout tracker — day-based training organization, exercise cards with sets/reps/weight logging, customizable rest timers with circular progress, and a volume history chart. Zero frameworks, zero dependencies, fully offline.

Role
Designer & Developer
Year
2025
Stack
HTML · CSS · Vanilla JS · Service Worker · Web App Manifest · localStorage
Status
LIVE
Live Site

GymLog is a personal workout companion built to run natively on mobile, installed from the browser to the home screen. Training sessions are organized by day — Monday through Sunday — with exercise cards that track multiple sets per exercise. Each set row captures reps and weight; tapping the check button marks it green and fades the inputs. A summary counter shows completed sets at a glance.

Each exercise card also has a built-in rest timer button. Tapping it opens a circular countdown panel — default 90 seconds, adjustable in 15-second steps. When the timer completes, the circle turns green and the device vibrates. A compact mini bar at the bottom of the screen persists while the timer runs, so you can log the next exercise without losing track of the rest.

· · · ◆ · · ·

Most gym apps require an account, a subscription, or constant network access. GymLog runs entirely on localStorage — no server, no login, no sync. Opening it is as fast as any native app because it ships as a PWA with a Service Worker that caches all assets after the first load.

The design constraint was simplicity. A gym floor is a hostile UX environment: sweaty hands, no time, short attention spans. Every interaction had to be reachable in one tap. The large input fields, the green flash on set completion, the persistent mini timer bar — all exist to reduce cognitive load while lifting.

Day-Based Training Organization

Seven day pills (MON–SUN) scroll horizontally at the top of the workout view. The active day is highlighted in red. Each day stores its own exercise list independently in localStorage. Exercises are added via a bottom sheet modal — name, muscle group, and technique — and persist across sessions. A PR (personal record) gold badge appears automatically on any exercise set that matches or beats the previous best weight.

Workout — Monday
MON TUE WED THU FRI
4
exercises
12
sets done
🔥 5
streak
[ + ADD EXERCISE ]

Rest Timer & Progress Chart

The circular rest timer uses an SVG stroke-dasharray / stroke-dashoffset technique to draw the countdown arc. Duration is configurable per-session; the default is 90 seconds. A mini timer bar with a pulsing red dot persists at the bottom of the workout view while counting, so navigation between exercises never interrupts the rest. The Progress tab renders a bar chart of weekly volume (total weight × reps) for any exercise, loaded dynamically from localStorage history.

Rest Timer — Squat
1:03
90s
+
1:03
SQUAT

What shipped

A complete PWA workout tracker: 7-day training log, exercise CRUD with muscle group and technique, multi-set logging with reps and weight, set completion with green highlight, PR badge for new personal records, circular rest timer with adjustable duration and haptic feedback, persistent mini timer bar, per-exercise notes, progress bar chart with exercise selector, dark/light mode, JSON backup export/import, and a dismissible install-to-home-screen prompt.

What I learned

localStorage performance on mobile degrades noticeably when re-serializing large nested objects on every keypress. Debouncing the input handlers — only persisting after a 300ms pause — eliminated the lag. Building the circular progress indicator without a charting library required understanding SVG coordinate geometry and the relationship between stroke-dasharray and the circle circumference (2πr). The mini timer bar, added midway through development, turned out to be the single most impactful UX decision: without it, users lost count of rest time the moment they scrolled down to log the next set.

Progress
Squat — Weekly Volume
MON
TUE
WED
THU
FRI
Best: 100kg × 8
Total: 16,800 kg lifted
NEXT PROJECT
Blocco →