Portfolio/RideMetri
🎬 Creator Tool · Browser-only · Owned end to end

RideMetri

Telemetry overlays for your ride videos. RideMetri reads the speed, cadence, heart rate and elevation off a Garmin track, draws them over your footage, and exports a clean PNG sequence, frame for frame in sync with the ride. It runs entirely in the browser.

Role
Solo: product, engineering, design
Timeline
2026 – Present
Type
Product I own
Platform
Web · React + Canvas · 100% local
ridemetri.app/app
🎬 Load Video⬇ Load GPX
Trail loaded · click a point to inspect
🏔 Outdoor🛰 Satellite📍 Topo
Video Previewcol-de-nice.mp4
38
KM / H
♥ 152
⏮▶⏭0:42 / 3:15
Trail
Col de Nice · morning loop
24.8
Distance (km)
612
Elev. gain (m)
1240
Max alt. (m)
3184
Track pts
Video Sync
GPX offset (s)-1.4
GPX speed1.00×
HUD Overlay
Show all HUDs
Show map panel
Route arrow
Global opacity100%
Export · 25fps · 1080×1920
▶ POINT A
0:12
⏹ POINT B
2:48
Duration2:36
Frames @25fps3900
Est. ZIP size~186 MB
⬇ Export ZIP of PNGs

From the track
to the timeline.

A ride leaves two files behind: a Garmin track full of numbers and a clip full of motion. Putting the numbers on the clip usually means a paid desktop app or a plugin, an export queue, and a lot of manual keyframing. That is enough friction that most amateur cyclists posting a Reel just don’t bother.

The product bet: the thing standing between the ride and the post isn’t features, it’s setup. So RideMetri does it in a browser tab, with no account, no upload, and exactly one export target: the 9:16 frame every short-form platform wants. Drop in a GPX or TCX track and your footage; it parses every point, draws the route on a map, and lets you nudge the offset and playback rate until the gauges move with the ride. Lay out the widgets, mark the stretch you want, export a numbered PNG sequence zipped for your editor.

The core UX insight is that syncing a GPS clock to a camera clock can’t be automated reliably across arbitrary footage. So instead of hiding a fragile auto-align, I made syncing by eye fast and legible: one offset field, one rate slider, a live readout of what each is doing.

Everything (parsing, compositing, encoding the frames) runs on the client. The files never leave the machine, which is a positioning choice as much as a technical one.

TypeBrowser-only creator tool
OwnerMe, solo, end to end
Front endReact 19, TypeScript, Vite, plain CSS
RenderingCanvas 2D · frame-by-frame compositor
MapsLeaflet · outdoor / satellite / topo tiles
InputGarmin GPX / TCX + MP4 / MOV / WebM
Output1080×1920 · 25fps · ZIP of PNGs (JSZip)
PrivacyFiles never leave the browser

Compete on setup time,
not on features.

The desktop tools already win on power. The only opening is the gap between finishing a ride and posting it, so every scoping decision was made to shrink that gap.

01
The user
An amateur cyclist or runner who films the odd ride and wants a clean telemetry overlay for a Reel. Not a pro editor, not a data nerd. If it takes longer than the edit itself, they won't use it.
02
The wedge
No install, no account, no upload, no render queue, one output size. Each of those is a step removed from 'ride done' to 'clip posted'. That chain, not the feature list, is what RideMetri is optimised for.
03
The non-goals
No landscape or square export, no cloud library, no editing timeline, no team features. RideMetri makes an overlay and hands it to the editor the user already has. Everything else is somebody else's product.

How I judge it. One test: someone who has never seen it should get a synced, exported overlay of a real ride inside ten minutes, on a laptop, without asking me anything. If a step needs explaining, that step is the bug.

The idea was fine.
The sync step was the risk.

The existing options are clear: desktop apps like the ones from the action-camera makers, a few paid plugins, and DIY After Effects. All of them are capable and all of them are heavy. The gap for a lightweight, browser-based overlay was obvious; the open question was whether the hard part could be made pleasant.

That hard part is sync. I built a throwaway prototype that tried to auto-align GPS speed to motion in the footage. It worked on a clean test clip and broke on everything real: shaky mounts, stop-start riding, tunnels with no signal. That result killed auto-sync as the primary path and made “sync by eye, done well” the core design problem rather than a fallback.

Watching a few riders use early builds settled the rest: they scrubbed to an obvious landmark (a sprint, a stop) and matched the dial there. So the sync UI is built around a big scrubbable preview and a live readout, not a settings form.

AlternativesDesktop apps, paid plugins, AE
Riskiest bitTrack-to-footage sync
PrototypeAuto-align by speed vs motion
FindingAuto-align fails on real footage
DecisionDesign sync-by-eye as the core
ThenWatched riders match on a landmark

Three problems
between the files.

01
The track and the clip don't line up
GPS logs about once a second on its own clock; footage runs at 25, 30 or 60fps from a different start. The gauges only feel right after a sub-second offset and a playback-rate nudge.
02
Rendering in the browser, phone-first
No server, no ffmpeg. Every frame is drawn on a canvas at 1080×1920 and packed into a ZIP that drops straight onto a 9:16 timeline for Reels, Shorts and TikTok.
03
Telemetry is sparse and messy
Altitude drops out, some tracks have no heart rate, a few have no timestamps at all. Every widget has to degrade gracefully and the exporter falls back to one frame per GPS point.

Load, sync, lay out,
composite, export.

01
Parse everything once
GPX or TCX is read into typed track points: coordinates, altitude, timestamp, heart rate, speed, cadence. The route is drawn on a Leaflet map, waypoints are listed, and the elevation profile is built.
GPX / TCX parserLeafletElevation profile
02
Sync by eye
An offset field and a 0.25×–4× speed slider. Scrub the video and watch the dial until it matches the ride, with a live readout of exactly what each control is doing to the timing.
OffsetPlayback rateLive readout
03
Lay out the 9:16 frame
Drag and size each widget (speed, cadence, heart rate, elevation, inset map) on a preview canvas. The customizer exposes padding, spacing, font size, route and segment width, arrow size and card opacity.
Drag & resizePreview canvasCustomizer
04
Composite every frame
For each output frame the renderer draws the video still, the inset map with the travelled segment and a next-turn arrow, then the HUD cards, all onto one 1080×1920 canvas.
Canvas 2DMap insetNext-turn arrow
05
Mark A→B and export
Set point A and point B on the timeline, see the exact frame count at 25fps and an estimated ZIP size, then stream PNGs into a JSZip download. Nothing is uploaded.
A→B range25fpsJSZip

Four decisions,
each with a cost.

Every one of these traded something real away. They’re the reason the tool is a browser tab and not a desktop app.

01
Browser-only, nothing uploaded
A server could run ffmpeg and encode far faster. But a server means accounts, an upload wait, storage, and a privacy promise I'd have to keep. Keeping everything local removes all of that. The cost is a performance ceiling and no heavy processing: for a two-minute clip that trade is invisible; for a twenty-minute one it isn't, and that's a known limit.
Local-firstPrivacyPerformance ceiling
02
Export a PNG sequence, not an MP4
In-browser video encoding exists, but it means codec choices, quality settings and a black box. A numbered PNG ZIP is deterministic, drops into any editor at any quality, and never argues about colour. The cost is a ~180 MB download and the user assembling frames in an NLE, which is acceptable because they're already in one to cut the clip.
DeterminismEditor fitFile size
03
Sync by eye, not auto-align
I prototyped correlating GPS speed to optical flow. It worked on clean footage and fell apart on everything else: mounts that shake, stop-start riding, tunnels. A manual offset plus a rate slider with a live readout always works, on any footage, and the rider trusts a result they dialled in themselves. The cost is one manual step I have to make feel quick.
ReliabilityTrustManual step
04
One output format, no settings
1080×1920, 25fps, 9:16, full stop. No resolution picker, no fps menu. That rules out landscape YouTube, which is a real segment. But the target user wants a Reel out the door, and every setting I don't add is a decision they don't have to make. Opinionated beats configurable here.
Opinionated defaultsScopeShort-form

Six things
on every frame.

🚴
Garmin GPX / TCX import
Drop a Garmin track and RideMetri reads every point (position, altitude, timestamp, heart rate, speed and cadence) and maps the route.
🗺️
Leaflet map inset
An inset map that follows your position, with the travelled segment highlighted and outdoor, satellite or topo tiles.
⏱️
Video sync
A sub-second offset field and a 0.25×–4× rate slider, so the gauges move exactly with the footage.
🎛️
HUD customizer
Drag and size the speed, cadence, HR, elevation and map widgets on a 9:16 preview, with sliders for padding, type size and line weights.
🫀
Heart-rate zones
A beat-synced pulse and HR-zone colouring (recovery through VO2 max) straight off the strap data.
📦
A→B PNG export
Mark the stretch you want, see the frame count at 25fps and an estimated size, then export a zipped, numbered PNG sequence.

A closer look.

One tab,
start to render.

The mockups on this page are rebuilt from the real front end; figures are illustrative, not exported from a specific ride.

1080×1920
Export frame
Vertical 9:16, drops onto a Reels / Shorts timeline
25fps
Frame cadence
Even 25p, ready for the edit
5
HUD widgets
Speed, cadence, heart rate, elevation, map
3
Map layers
Outdoor, satellite, topo
0
Uploads
Parsing, compositing and export all run local
A→B
Export range
Render only the stretch you marked

What I’d change. The sync step is the whole product and it’s still the part first-time users stall on. It needs a guided first-run, not just a live readout. And a long export at 25fps can walk a browser tab into a memory wall; the honest fix is a smaller default range with a clear “render in chunks” path, which I’d rather ship than pretend the ceiling isn’t there.

Next Project
StockFlow
A smart B2B logistics platform for order management, shipment tracking, returns, and inventory.
View project →