Die meisten GPX-Video-Overlay-Tools sind Desktop-Apps. Datei importieren, auf eine Video-Timeline ausrichten, exportieren. Funktioniert. Ist aber bei 2015 stehengeblieben — manuelles Syncing, klobige UIs, halb die Zeit nur Windows.
TelemetryKit geht anders vor: GPX- oder FIT-Datei in eine Web-App hochladen, vier Telemetrie-Widgets im Browser in der Vorschau sehen, H.265-MP4-Clips serverless auf AWS Lambda rendern und das Ergebnis herunterladen. Kein Desktop-Install. Kein Video-Editor. Kein After-Effects-Template.
Die interessante technische Entscheidung dahinter: Die Render-Engine ist Remotion — ein React-Framework für programmatische Videoproduktion. Normalerweise für Marketing-Clips und Social-Media-Animationen genutzt. Es für datengetriebene Cycling-Telemetrie-Overlays einzusetzen, ist ein ungewöhnlicher Use Case. Hier ist, warum es funktioniert, wie die Architektur zusammenhängt und wo die Trade-offs liegen.
Das Problem mit bestehenden GPX-Video-Overlay-Tools
Cycling-Content-Creator — YouTube-Kanäle, Rennbericht-Editoren, Coaching-Plattformen — brauchen Metrik-Overlays auf ihrem Videomaterial. Geschwindigkeit, Leistung, Herzfrequenz, Steigung. Die Daten liegen in GPX- oder FIT-Dateien, exportiert von Garmin-, Wahoo- oder Hammerhead-Geräten.
Die aktuellen Optionen:
- Desktop-Apps wie Dashware oder Garmin VIRB Edit. Mächtig, aber eingestellt oder kaum gepflegt. Garmin hat VIRB Edit seit Jahren nicht mehr aktualisiert. Dashware läuft noch, fühlt sich aber nach 2012 an.
- After-Effects-Templates. Flexibel, aber erfordern AE-Skills, manuellen Datenimport und ein Creative-Cloud-Abo. Overkill für ein 30-Sekunden-Power-Meter-Overlay.
- FFmpeg-Skripte. Die Programmierer-Antwort auf alles. Skript schreiben, das GPX liest, SVG-Frames generiert, sie auf Video composited. Funktioniert — aber eine eigene FFmpeg-Pipeline pro Widget-Stil zu pflegen, ist mühsam. Keine Vorschau. Kein Iterations-Loop.
Keine dieser Lösungen ist „Datei hochladen und Overlay-Clip bekommen". Das ist die Lücke.
Was TelemetryKit macht
Der Ablauf ist unkompliziert:
- Eine
.gpx- oder.fit-Datei hochladen. - Der Server parst die Datei, baut eine 15-fps-Timeline und extrahiert Per-Frame-Datenserien — Geschwindigkeit, Leistung, Herzfrequenz, Steigung, Kadenz (wenn vorhanden).
- Der Browser zeigt vier Live-Widget-Vorschauen: SpeedGauge, PowerMeter, GradeMeter, HeartRate. Alle angetrieben von denselben Timeline-Daten.
- Render-Dauer wählen (gesamte Fahrt oder ein kürzeres Fenster — 30s, 1m, 5m, bis 90m), Pixel-Scale (1× oder 2×) und Hintergrundmodus (Vollfarbe oder Green Screen).
- Render auslösen. AWS Lambda übernimmt den Job, rendert ein H.265-MP4 in der nativen Widget-Auflösung und erzeugt eine presigned Download-URL.
- Clip herunterladen. Im eigenen Video-Editor auf das Cycling-Footage compositen.
Das Ergebnis ist ein eigenständiges MP4 pro Widget — kein Vollbild-Overlay, das direkt auf das Video composited wird. Man bekommt einen kleinen Clip (400×400 für SpeedGauge bei 1×, zum Beispiel) mit entweder einem Vollfarb- oder Green-Screen-Hintergrund. In Premiere, Final Cut, DaVinci Resolve oder CapCut per Chroma Key oder Blending-Modus einsetzen. So bleibt TelemetryKits Aufgabe einfach: saubere, akkurate Metrik-Widgets rendern. Den Rest macht der Video-Editor.
Warum Remotion statt FFmpeg oder Canvas
Das ist die architektonische Entscheidung, die es wert ist, erklärt zu werden.
Der reine FFmpeg-Ansatz
Man könnte GPX-Daten parsen, SVG- oder PNG-Frames mit Node.js generieren (sharp, canvas oder eine Template-Engine), dann diese Frames in FFmpeg pipen und ein MP4 erzeugen. Funktioniert. Ich hab das für einfachere Overlay-Aufgaben gemacht.
Das Problem: Es gibt keine Vorschau. Um zu sehen, wie das Widget bei Frame 847 aussieht, muss man Frame 847 rendern. Um das Design zu iterieren — Font ändern, Nadelwinkel anpassen, Easing-Kurve tweaken — rendert man die ganze Sequenz neu. Entwicklungszyklen werden in Minuten pro Designänderung gemessen.
Der Canvas-Ansatz
Widgets in einem Browser-Canvas rendern, den Canvas als MediaStream aufnehmen, an MediaRecorder pipen. Client-seitig, kein Server nötig.
Das Problem: Die Ausgabequalität von MediaRecorder ist browserübergreifend inkonsistent. H.265 bekommt man nicht zuverlässig. Frame-Timing ist nicht deterministisch — wenn der Browser stottert, stottert die Ausgabe. Okay für Screen Recordings. Nicht okay für framegenau Telemetriedaten, bei denen die Geschwindigkeitsanzeige exakt zum Frame passen muss.
Der Remotion-Ansatz
Remotion (v4, aktuell bei v4.0.448) behandelt Video als React-Komponentenbaum. Jeder Frame ist eine Funktion von frame-Nummer und fps. Man schreibt React-Komponenten, die einen Frame-Index entgegennehmen und JSX zurückgeben. Remotion rendert jeden Frame als Browser-Screenshot und encodiert sie in eine Videodatei.
Was das TelemetryKit bringt:
- React-Komponenten als Widgets. SpeedGauge ist eine React-Komponente. Empfängt
speedfür den aktuellen Frame, rendert eine SVG-Anzeige. Standard-React-Entwicklung — Hot Reload, Component Composition, TypeScript. - Deterministischer Frame-Output. Frame 847 bei 15 fps rendert immer identisch. Kein MediaRecorder-Timing-Jitter.
- Browser-Vorschau über Remotion Player. Dieselbe React-Composition, die Remotion Lambda rendern wird, treibt auch die Live-Vorschau auf der Upload-Seite an. Eine Codebase, zwei Kontexte.
- Serverloses Rendering über Remotion Lambda. Remotion hat eine First-Party-Lambda-Integration. Lambda-Funktion deployen, Composition-ID und Input Props (die Datenserien) schicken, es rendert und lädt auf S3 hoch. Kein persistenter Render-Server. Bezahlung pro Aufruf.
- H.265-Ausgabe. Remotion nutzt FFmpeg unter der Haube für die Encodierung. Man gibt
codec: 'h265',pixelFormat: 'yuv420p', einen CRF-Wert an, und es erledigt den Rest.
Der Trade-off: Remotion ist schwerer als eine rohe FFmpeg-Pipeline. In Lambda läuft eine Chromium-Instanz, die React-Komponenten als Screenshots rendert. Nicht der ressourceneffizienteste Weg, einen 400×400-Geschwindigkeitsmesser zu produzieren. Aber die Developer Experience — Vorschau, Iteration, Deploy — ist dramatisch besser. Für ein POC, in dem sich das Widget-Design wöchentlich ändert, zählt das mehr als Lambda-Kostenoptimierung.
Architektur: GPX zu MP4
So hängen die Teile zusammen:
┌─────────────┐
│ Browser UI │
│ (Next.js) │
└──────┬───────┘
│ POST /api/upload
▼
┌──────────────┐ Direkter Upload
│ Upload API │───────────────────► S3 (rohe GPX/FIT)
└──────┬───────┘
│ POST /api/upload/complete
▼
┌──────────────────┐
│ Parse & Timeline │ ← gpx-parser / fit-parser
│ 15-fps-Serien │ ← speedMps[], power[], hr[], gradePct[]
└──────┬───────────┘
│ JSON-Response
▼
┌──────────────────┐
│ Remotion Player │ ← Client-seitige Vorschau (gleiche React-Komponenten)
│ 4 Widget-Previews│
└──────┬───────────┘
│ POST /api/lambda/render
▼
┌──────────────────┐
│ Remotion Lambda │ ← Rendert einzelne Composition
│ (AWS Lambda) │ ← H.265 MP4, CRF 23, 15 fps
└──────┬───────────┘
│ S3-Upload (privat, 3-Tage-TTL)
▼
┌──────────────────┐
│ Presigned URL │ ← Download-Link zurück zum Browser
└──────────────────┘
Datei-Parsing
GPX-1.1-Dateien enthalten Trackpoints mit Lat, Lon, Time und Elevation. Extensions können Power, Herzfrequenz und Kadenz enthalten — abhängig vom Gerät und der Aufnahme-App. FIT-Dateien (Garmins Binärformat) sind für Sensordaten zuverlässiger, brauchen aber einen dedizierten Parser.
Der Server liest die hochgeladene Datei, extrahiert alle verfügbaren Kanäle, interpoliert sie auf eine feste 15-fps-Timeline und gibt die Daten als Arrays zurück. Geschwindigkeit wird aus GPS-Positionen und Zeitstempeln abgeleitet. Steigung wird aus Höhenänderungen über Distanz berechnet. Power und Herzfrequenz kommen direkt aus der Datei, wenn vorhanden.
Vorschau
Der Browser empfängt die Daten-Arrays und speist sie in Remotion-Player-Instanzen — eine pro Widget. Jeder Player führt dieselbe React-Composition aus, die Remotion Lambda auch rendern wird. Der Nutzer sieht die Nadelanzeigen bewegen, die Power-Zahlen ticken, den Herzfrequenzpuls — alles angetrieben von echten Fahrtdaten.
Das ist der architektonische Kernvorteil von Remotion: Vorschau und Produktions-Render nutzen identischen Code. Was man im Browser sieht, bekommt man im MP4. Kein „Vorschau sieht anders aus als Export"-Problem.
Rendering
Wenn der Nutzer einen Render auslöst, ruft die Next.js-API-Route renderMediaOnLambda() aus dem @remotion/lambda-Paket auf. Übergeben werden:
- Die Composition-ID (z.B.
SpeedGauge) - Input Props mit den Datenserien für das gewählte Zeitfenster
- Codec-Einstellungen:
h265,yuv420p, CRF 23 - Auflösung: die native Widget-Größe × den gewählten Pixel-Scale (1× oder 2×)
- Hintergrundfarbe oder Green-Screen-Modus
Lambda startet, rendert jeden Frame, encodiert das Video, lädt das MP4 auf S3 mit privater ACL und einer 3-Tage-Auto-Delete-Policy hoch. Der Client pollt einen Progress-Endpoint, bis der Job abgeschlossen ist, und erhält dann eine presigned Download-URL.
Kostenschätzung
Der Server schätzt die AWS-Renderkosten für jeden Job — basierend auf Lambda-Laufzeit, Speicherallokation und S3-Storage. Im POC wird diese Schätzung im UI für interne Validierung angezeigt. Die Zahlen helfen, ein zukünftiges Preismodell zu kalibrieren. Ein kurzer Widget-Render (30 Sekunden Fahrtdaten bei 15 fps = 450 Frames) kostet Bruchteile eines Cents an Lambda-Rechenzeit.
Die vier Widgets
Jedes Widget ist eine eigenständige Remotion-Composition mit eigener nativer Auflösung:
| Widget | Datenkanal | Größe (1×) | Größe (2×) |
|---|---|---|---|
| SpeedGauge | speedMps → km/h | 400×400 | 800×800 |
| PowerMeter | power (Watt) | 320×200 | 640×400 |
| GradeMeter | gradePct | 320×160 | 640×320 |
| HeartRate | hr (bpm) | 240×120 | 480×240 |
Die 2×-Option existiert für hochauflösende Video-Workflows (4K-Footage). Bei 1× sind die Widgets für 1080p-Compositing dimensioniert — klein genug, um in einer Ecke zu sitzen, ohne das Bild zu dominieren.
Green-Screen-Modus setzt den Hintergrund auf reines Grün (#00ff00) und erzwingt als Widget-Zeichenfarbe entweder Schwarz oder Weiß für sauberes Chroma Keying. Vollfarb-Modus lässt den Nutzer Schwarz, Weiß oder eine eigene Farbe wählen — nützlich, wenn der Hintergrund zur Farbgebung des Videos passen soll.
Warum 15 fps und nicht 30 oder 60
Cycling-Telemetriedaten zeichnen typischerweise mit 1 Hz auf (eine Messung pro Sekunde). Einige Geräte zeichnen für bestimmte Kanäle mit 2–4 Hz auf. 1-Hz-Daten auf 60 fps zu interpolieren erzeugt 60 Frames, von denen 59 fabriziert sind — die Anzeigennadel bewegt sich smooth, aber die Datenpräzision ist eine Illusion.
15 fps ist ein pragmatischer Mittelweg. Smooth genug, damit das Overlay nicht wie eine Slideshow wirkt, wenn es über 30-fps-Footage composited wird. Ehrlich genug, damit die Frame-zu-Frame-Änderungen reale Datenintervalle widerspiegeln. Und es halbiert die Frame-Anzahl im Vergleich zu 30 fps, was Lambda-Renderzeit und -Kosten direkt halbiert.
Ob 30 fps als Option in einer künftigen Version angeboten werden, ist eine offene Frage. Aktuell hält 15 fps den Scope eng und die Renderkosten niedrig.
Aktueller Status
TelemetryKit befindet sich in der Validierung. Der End-to-End-Flow funktioniert: Upload → Vorschau → Render → Download. Die Landing Page sammelt Waitlist-Anmeldungen. Es gibt keine Nutzerkonten und kein Billing. Das POC existiert, um zu validieren, ob dieser Workflow ein echtes Problem für Cycling-Content-Creator löst, bevor die Business-Schicht gebaut wird.
Wenn die Validierungssignale positiv sind, sind die nächsten Schritte klar: Nutzerkonten, ein credits-basiertes Billing-Modell gekoppelt an Renderkosten, Batch-Rendering (alle vier Widgets in einem Job) und Erweiterung der Widget-Bibliothek über die initialen vier hinaus.
Was ich beim Bauen gelernt habe
Einige Punkte, die für jeden relevant sind, der Remotion für datengetriebenes Video in Betracht zieht:
Remotion-Lambda-Cold-Starts zählen. Die Lambda-Funktion enthält eine Chromium-Binary. Cold Starts sind spürbar — mehrere Sekunden, bevor das Rendering beginnt. Für einen Batch-Render, den ein Nutzer auslöst und 30 Sekunden warten kann, ist das akzeptabel. Für Echtzeit- oder Near-Realtime-Use-Cases nicht.
Datenserialisierung ist der Engpass, nicht das Rendering. Eine 90-Minuten-Fahrt bei 15 fps produziert 81.000 Frames. Die Daten-Arrays (Geschwindigkeit, Leistung, HR, Steigung pro Frame) sind große JSON-Payloads. Sie als Input Props an Lambda zu übergeben, erfordert sorgfältiges Größenmanagement. Für das POC funktioniert das. Für lange Fahrten bei höheren fps wäre ein anderer Datentransport nötig — S3-Referenz, Chunking.
Green Screen bei kleinen Auflösungen ist knifflig. Ein 240×120-HeartRate-Widget mit Green-Screen-Hintergrund hat sehr wenige Randpixel. Chroma Keying im Video-Editor kann in die Widget-Grafiken eingreifen, wenn die Key-Toleranz zu weit ist. Die Vollfarb-Hintergrund-Option existiert teils, weil Green Screen bei diesen Größen nicht immer sauber genug ist.
React ist eine brauchbare Video-Authoring-Sprache. Klingt seltsam, stimmt aber. Component Composition, Props-getriebenes Rendering, TypeScript-Typsicherheit auf dem Datenmodell — das sind echte Vorteile beim Bau visueller Komponenten, die über tausende Frames identisch rendern müssen. Der Remotion Player für Vorschauen besiegelt das. Kein anderes Video-Framework bietet „Komponente bearbeiten, Ergebnis sofort im Browser sehen."
Der Stack
Zur Referenz, TelemetryKit läuft auf:
- Frontend: Next.js (App Router), TypeScript, Tailwind CSS
- Video-Framework: Remotion v4 (Compositions + Lambda-Rendering)
- Rendering: AWS Lambda via
@remotion/lambda, H.265-Encoding - Storage: S3 (Upload + gerenderte Ausgabe, private ACL, 3-Tage-TTL)
- Parsing: GPX-1.1- und FIT-Datei-Parser (server-seitig)
- Hosting: Vercel (Frontend), AWS (Lambda + S3)
Keine Datenbank im POC. Keine Auth. Kein Billing. Der gesamte State lebt im Upload-Render-Download-Zyklus. Das ist Absicht — der schnellste Weg zu validieren, ob der Core-Workflow Wert hat, bevor Infrastrukturkomplexität hinzukommt.