Baseform
Nutrition, training and recovery as a PWA: no server, no account, works offline.
- Role
- Sole developer, own project
- Period
- July 2026, ongoing
- React 18
- Vite
- PWA
- ZXing
- localStorage
- SVG
- Google Health API
- OAuth 2.0
- Anthropic Messages API
What it does
Beyond the first paragraph this page is still in German. A full translation is on its way.
Born from my own need: nutrition, training and recovery in one place, without data leaving the device. Barcode and QR scanning against Open Food Facts, a training plan with 26 data-driven exercise animations, Fitbit values via Google Health and an AI coach that works with the remaining daily budget. No server and no account was the starting condition, not the result, which moves the hard questions from authorisation to storage: who owns the state when there is no server to arbitrate? It is the one project I use every day, and the September fixes all came from that use rather than from a test plan.
Aus Eigenbedarf entstanden: Ernährung, Training und Erholung an einem Ort, ohne dass dafür Daten das Gerät verlassen. Barcode- und QR-Scan gegen Open Food Facts, ein Trainingsplan mit 26 datengetriebenen Übungsanimationen, Fitbit-Werte über Google Health und ein KI-Coach, der mit dem verbleibenden Tagesbudget rechnet.
Kein Server und kein Konto war die Ausgangsbedingung, nicht das Ergebnis. Das verschiebt die schwierigen Fragen von der Berechtigung zur Speicherung: Wem gehört der Zustand, wenn es keinen Server gibt, der schlichtet?
Baseform ist das einzige Projekt, das ich selbst täglich benutze, seit Ende Juli als Trainingsprotokoll im Gym. Die Korrekturen vom September stammen alle aus dieser Nutzung, nicht aus einem Testplan.
Numbers
- lines of code
- ~2300 lines of code
- data-driven exercise animations
- 26 data-driven exercise animations
- servers, 0 accounts
- 0 servers, 0 accounts
Screenshots





Datenverlust bei zwei offenen Fenstern, und was daraus im Schema wurde
Zwei parallel geöffnete Fenster überschrieben sich gegenseitig: Alle Daten lagen als ein JSON-Block unter einem Schlüssel, beide Fenster schrieben ihn komplett zurück, und das Fenster mit dem älteren Stand löschte Tage, die es nie gesehen hatte. Der Fehler hat echte Trainingsdaten gekostet, was ihn zur besten Lektion des Projekts macht. Die Sofortlösung war ein Merge beim Speichern statt eines Überschreibens. Die eigentliche Lehre floss in die Struktur: Im vorbereiteten Supabase-Schema ist es eine Zeile pro Tag und Übung statt eines JSON-Blocks pro Tag. Damit kann der Konflikt nicht mehr entstehen, statt dass er nur behandelt wird, und das ist der Unterschied zwischen einem Bugfix und einer Entscheidung.
Stack
- UI
- React 18.3
- Hooks und JSX
- useMemo für Wochen- und Kalenderrechnung
- Build
- Vite 8.1
- @vitejs/plugin-react
- dynamischer Import für den Scanner
- Scan
- @zxing/browser
- @zxing/library
- EAN, UPC und QR im Browser
- Open Food Facts
- Styling
- Inline-Styles
- zentrales Farbsystem
- handgezeichnetes SVG
- 26 datengetriebene Animationen
- PWA
- Web-App-Manifest
- Apple-Meta-Tags
- Icons
- Installation auf dem Homescreen
- Data
- localStorage
- JSON-Backup
- offline-tauglich
- vorbereitet: Supabase mit Row Level Security
- AI and auth
- Anthropic Messages API
- Claude Sonnet 4.6
- Google Identity Services
- OAuth 2.0 Token Client
- Google Health API mit drei Nur-Lese-Scopes
Status and reading
In daily personal use as a PWA on the iPhone, development ongoing. Repository private, access on request.