Skip to main content
All projects
Web Apps
/projects/fitpal

FitPal — Offline Fitness Tracker

project.fitpal.local
FitPal — Offline Fitness Tracker project screenshot
  1. Problem:

    Tracking workouts and nutrition usually requires heavy apps with accounts, ads, and cloud dependencies.

  2. Solution:

    Built a professional-grade, zero-dependency PWA fitness dashboard that works entirely offline using localStorage.

  3. Impact:

    100% private, instant-load fitness tracking with comprehensive analytics, diet plans, and progressive overload tracking.

Overview

FitPal is a professional-grade, zero-dependency PWA fitness dashboard built for serious athletes. It is contained entirely within a single HTML file, requiring no accounts, no cloud, and loading instantly. It supports 6-day PPL splits, drop sets, analytics heatmaps, nutrition tracking, and full data export/import via JSON.

Results & metrics

  • 100% private fitness and workout logging with zero cloud storage dependencies
  • Instantaneous sub-100ms app loading through Service Worker asset caching
  • Zero-dependency single-file architecture runnable on any modern web browser
  • Complete JSON data export and import for seamless multi-device backups

Before / after

  • Data Privacy

    Before: Data stored on external servers with required accounts

    After: 100% private, local-only storage via localStorage

  • App Loading

    Before: Network dependent loading times

    After: Instant load with offline-first caching via Service Worker

  • Connectivity Dependency

    Before: App completely unusable in gyms with poor cellular reception

    After: Fully functional offline PWA with reliable background caching

What I built

Local-only storage

Workouts and nutrition stay in the browser's localStorage: no accounts, no cloud.

Works offline

A service worker loads pages network-first and assets cache-first, with offline fallback.

Stack

  • Vanilla JavaScript, ES2022
  • CSS3, Custom Properties, Grid, Flexbox
  • PWA, Service Worker, Manifest
  • Netlify Deployment, with strict security headers

Decisions

Key trade-offs and design calls that shaped the final delivery.

  • Zero-dependency single file

    Why
    Context
    Wanted maximum simplicity, performance, and portability.
    Decision
    Built the entire application in vanilla HTML/CSS/JS without frameworks to ensure instant load and easy maintenance.
  • localStorage for persistence

    Why
    Context
    Privacy and offline usage were top priorities.
    Decision
    Avoided any backend database or accounts, storing all workout and nutrition data strictly in the browser's localStorage.

Architecture

The primary system boundaries, runtime pieces, and how the project was structured in production.

Diagram, 5 connections:
  1. Vanilla HTML5 / CSS3 / JS UI ↔ Service Worker
  2. Service Worker → App Cache & Static Shell (Cache-First)
  3. Service Worker → Netlify CDN with Strict CSP (Network-First)
  4. Vanilla HTML5 / CSS3 / JS UI ↔ Encrypted localStorage (Workout & Nutrition Data)
  5. Vanilla HTML5 / CSS3 / JS UI ↔ JSON Backup Export & Import
Groups: Client Browser Layer: Vanilla HTML5 / CSS3 / JS UI, JSON Backup Export & Import; PWA Offline Engine: Service Worker, App Cache & Static Shell; Private Device Storage: Encrypted localStorage; Static Edge Delivery: Netlify CDN with Strict CSP.
  • Vanilla JS & localStorage

    Frontend & Persistence

    How

    100% private, local-only data persistence with no cloud dependencies or accounts.

  • Service Worker

    Offline Capability

    How

    Network-first for navigation, cache-first for assets, providing robust offline fallback.

  • Netlify & Security Headers

    Security & Deployment

    How

    Strict-Transport-Security, Content-Security-Policy, and X-Frame-Options configured via netlify.toml.

Mermaid source

Paste into mermaid.live to edit the diagram.

flowchart TD
  subgraph Browser [Client Browser Layer]
    UI[Vanilla HTML5 / CSS3 / JS UI]
    EXP[JSON Backup Export & Import]
  end
  subgraph Worker [PWA Offline Engine]
    SW[Service Worker]
    CACHE[(App Cache & Static Shell)]
  end
  subgraph Persistence [Private Device Storage]
    LS[(Encrypted localStorage)]
  end
  subgraph Edge [Static Edge Delivery]
    NET[Netlify CDN with Strict CSP]
  end
  UI <--> SW
  SW -->|Cache-First| CACHE
  SW -->|Network-First| NET
  UI <-->|Workout & Nutrition Data| LS
  UI <--> EXP

Pipeline

How changes moved from development through validation and deployment.

Select a step for its detail

Incidents

Operational failures, rehearsals, or recovery moments that changed how the system was run.

  • Medium

    Service Worker cache staleness on application update

    Resolution

    Implemented skipWaiting and clients.claim lifecycle hooks with versioned cache naming

    Lesson: PWA service workers require explicit cache invalidation logic to deliver app shell updates immediately

  • Low

    localStorage quota saturation during extensive history logging

    Resolution

    Added automated JSON backup export prompts and efficient binary payload serialization

    Lesson: Always design client-side persistent apps with data portability and quota warnings