The problem
A running watch writes a .fit file for every run. The platforms that read those files decide which metrics you see, and they keep your data on their servers.
This explorer reads the files in the browser instead. Drop one run or a year of runs. The app decodes them, stores them in a local database, and computes the training metrics. The files never leave the machine, and no server exists.
The architecture
| Layer | Job |
|---|---|
| Ingest | Decodes FIT with the FIT SDK and puts every channel on a 1 Hz grid |
| Metrics | Pure functions over typed arrays, with no database and no DOM |
| Storage | One DuckDB-Wasm file in the Origin Private File System |
| UI | uPlot charts, tables, and a run detail screen |
The database keeps the per-second records, not only the results. Each run carries a fingerprint of the metric versions that produced its numbers. When a metric changes, the app recomputes every run with an old fingerprint from its stored records. No one imports a file again.
That is the same problem a warehouse has with derived tables, solved in a browser tab.
The stack
| Part | Choice |
|---|---|
| Database | DuckDB-Wasm on OPFS |
| FIT decoding | @garmin/fitsdk |
| Charts | uPlot |
| Build and tests | Vite and Vitest |
Design choices
It runs on plain static hosting. The app uses the single-threaded version of DuckDB, which needs no special server setup. That is why the demo can live on GitHub Pages.
It pins one data library. DuckDB hands results back through a library called Arrow, and a second version of it gives wrong answers without an error. The project keeps exactly one version.
It never calls home. DuckDB can download add-ons from the internet by itself, and the app turns that off. The only network call is the optional weather lookup, and it sends a location rounded to about 11 km.
It works in one tab at a time. The browser lets only one tab hold the database file. Open the demo in a single tab.
Try it
The live demo runs the same build. Export a .fit file from your watch platform and drop it on the page. Your data stays in your browser.