Skip to content

v0.3.0 — Live on the web, installable on your phone, and your data is yours

Choose a tag to compare

@beadon beadon released this 13 Jul 10:44
· 44 commits to main since this release
3f79332

The release where RacePlex became something you can actually keep on your phone — and where your data became yours to take with you.

On the numbering. RacePlex restarted versioning at 0.x when it forked from Dove's DataViewer. The v2.x/v3.x tags in the git history belong to upstream and came along with their commits.


It's live, and it installs

beadon.github.io/RacePlex — open it and it runs. Nothing to install, no account, no upload.

If you want it on your phone, add it to your home screen and it works like an app, offline, at the track with no signal. That needed HTTPS: a browser will only register a service worker on HTTPS or localhost, so over a plain-HTTP LAN address navigator.serviceWorker doesn't even exist and the offline copy could never be built. GitHub Pages serves over HTTPS, which is the whole requirement.

Serving from a subpath (/RacePlex/) quietly broke four things, all fixed here: runtime asset fetches (/tracks.json was resolving outside the deployment, so the track list came up empty with no visible error), the PWA manifest's start_url and scope, the router basename, and the self-hosted fonts, which 404'd on every deploy.


Your data is yours

Everything RacePlex knows lives in your browser and nowhere else. That is the point of it — no server sees your rides. It is also the risk: clear the browser, or move to a new phone, and it is all gone.

Settings → Your data now downloads the lot as a .zip:

  • every session log, in its original format (so they open in other tools too — you are not locked in),
  • your vehicles, setups, notes, custom tracks, lap snapshots and settings, as plain JSON you can read in any text editor,
  • your saved CSV column mappings, so a device you taught RacePlex to read once stays readable.

The same screen restores an archive, which is how you move to a new browser or a new device. Restoring never overwrites a session that's already there. Session videos are included only if you tick the box, because they can run to gigabytes.

The export is also on the Tools tab and in the Files drawer, under the storage bar — the moment you see how full the browser is, is the moment you want a way out of it.


A home screen

The app now opens on a dashboard instead of a welcome page: your recent sessions first, then your garage, tracks, devices and tools, with a persistent nav bar across the top (or along the bottom, on a phone). Adding data is its own zone rather than the only thing on screen.

Compare sessions side by side: pick two or more from the browser and they're resampled onto a shared distance axis, so the same corner lines up across runs on one map.

Local users. More than one rider sharing a browser now gets their own sessions, garage and settings, without an account.


Recording and importing

RaceBox and Dragy record live over Bluetooth. No logger file to export first — connect and go. Chrome or Edge, desktop or Android.

FIT files import (Garmin, Wahoo, Coros, Suunto). This is the largest installed base of GPS fitness devices, so you can try RacePlex with a ride you already own. They log at about 1 Hz — roughly 11 m between fixes at 40 km/h — which is enough to plot a ride and too coarse to place a braking point.

RaceChrono CSV v3 imports.

GoPro chapter-split recordings fold into one session. A long recording is split across several .mp4 chunks; they now stitch into a single continuous timeline.


The stance tool now tells the truth

The old version compared your board's grip against the 0.37 g at which a board pitches over the nose, and told nearly every rider they'd tip before they'd skid.

That is true of an all-wheel board, and false of almost every board actually being ridden. An eskate has no friction brakes — the motors are the brakes — so only the wheels a motor drives can slow it down, and nearly every board is rear-driven. On a rear-driven board, braking unloads the very wheels doing the braking. As the rear lightens it loses the grip it needs to brake, so it can never reach the threshold that would lift it:

a_slip / a_endo  =  μ·f·z / (L + μ·f·z)  <  1     for every board, always

A dual-rear board tops out around 0.17 g — under half of what an endo needs — and the rear breaks traction and slides instead. It cannot endo under motor braking, on any geometry. An all-wheel board keeps the full ~0.6 g of urethane grip and does pitch, with the rear lifting at around 0.37 g.

The tool now reports your g budget against what your drivetrain can actually deliver.


Under the hood

A major-version dependency migration: React 19, Tailwind 4, Vite 8, TypeScript 6, ESLint 10, react-router 7, react-resizable-panels 4.

That last one is worth calling out, because it shipped a bug with a fully green build. react-resizable-panels v4 silently redefined a bare size number from percent to pixels (a unitless string is now percent). The prop is typed number | string, so defaultSize={30} still compiled — and the Pro view's left column rendered as a 30-pixel sliver with its labels wrapping one letter per line. tsc, ESLint, ~2,500 tests and the build were all green. Only opening the app found it.


Fixed

  • The Pro view's left column rendered as a ~45px sliver after the resizable-panels bump.
  • Self-hosted fonts 404'd on every deploy.
  • The track list silently came up empty on the deployed site.
  • The locale parity check passed while broken, because a test's exit code was being masked by a pipe.