@ridewolf/track-playback
v0.1.1
Published
GPS track replay engine: wall-clock interpolation, parked-drift de-jitter, stop/speeding timeline markers, and a speed-coloured scrubber gradient. Pure TS core + optional Vue 3 adapter.
Readme
Everything above is the engine driving examples/demo.html — ~200 dependency-free lines of canvas.
Everyone who builds trip replay writes "interpolate between GPS points" in an afternoon — and then spends weeks discovering what real GPS data does to it: parked vehicles teleporting a few metres at random, heading arrows snapping north at every stop, a swarm of speeding pins from one GPS dip, traffic lights flagged as parking. This engine is the version that already went through those weeks, extracted from the Ridewolf fleet dashboard.
Quickstart
bun add @ridewolf/track-playback # vue is an optional peer, only for ./vueFramework-free core — feed it points, get a playhead:
import { buildTrackComputed, interpolateTrackAtClock } from '@ridewolf/track-playback';
const tc = buildTrackComputed(points); // once per track
const at = interpolateTrackAtClock(points, tc, Date.parse('2026-07-31T10:30:00Z'));
// → { point: { lat, lng, speed, bearing, progress }, index, distance }Vue adapter — the full player state machine:
import { useTrackPlayback } from '@ridewolf/track-playback/vue';
const tracks = ref<ReplayTrack[]>([{ id: 'veh-1', label: 'AB 123', points }]);
const player = useTrackPlayback(tracks);
// player.currentPoint → render the marker on your map
// player.speedBar → CSS gradient for the scrubber
// player.timelineMarkers → parked/started/speeding pins
// player.play() / pause() / seekTo(0.5) / jumpBy(-30_000) / setSpeed(4)Runnable examples: examples/headless.ts (engine only, no framework) and examples/demo.html — a complete canvas player with a speed-coloured track, bearing-rotated marker, gradient scrubber and event pins, in ~200 dependency-free lines.
What it gets right
- De-jittered parking — while the vehicle reads stopped and fixes drift within 8 m of the anchor, the playhead holds still instead of teleporting. Real movement snaps to the fresh fix immediately.
- Stable heading — bearing is carried across frozen runs, so a parked arrow keeps its last real direction instead of snapping north.
- Wall-clock interpolation — the playhead runs on absolute time with binary-search segment lookup: O(log n) per frame, day-long tracks included. Multi-track ready.
- Honest stop detection — a halt counts as parked only after 3 minutes (traffic lights don't), and the same single source of truth drives both the timeline pin and the bar colouring, so they can never disagree.
- One pin per speeding episode — a breach pins where it starts, upgrades warn → alert within the episode, and brief dips below the limit don't spawn a swarm of markers (60 s cooldown ends an episode).
- Compact speed gradient — the scrubber samples 140 evenly-spaced stops instead of one per GPS point, so a day-long track doesn't produce a megabyte of CSS.
- Tunable — every threshold above is a
PlaybackTuningfield; defaults are the car-fleet values, scooter profiles just overridespeedWarnKmh/speedAlertKmh.
API surface
| Export | From | Purpose |
| --- | --- | --- |
| buildTrackComputed, interpolateTrackAtClock | . | Precompute + query a track at any clock time. |
| stabilizeCoordinates, buildSegmentBearings | . | The de-jitter primitives, usable standalone. |
| buildSpeedBar, buildTimelineMarkers | . | Scrubber gradient + event pins from raw points. |
| calculateBearing, haversineDistance | . | The geo math underneath. |
| getColorFromSpeed | . | Speed → hex colour (blue→cyan→lime→green→yellow→orange→red). |
| DEFAULT_TUNING, types | . | ReplayPoint, ReplayTrack, PlaybackTuning, ... |
| useTrackPlayback | ./vue | The reactive player: state, scrubber, transport controls. |
Documentation
- The replay engine — interpolation, de-jitter, marker semantics, and why each threshold exists.
- Vue adapter — composable API, playable-window bounds, lifecycle, rendering recipes.
Why we built this
At Ridewolf operators replay vehicle trips to investigate incidents and complaints — "was the scooter really parked there?", "when did it speed?". The naive replayer answered those questions wrong in subtle ways (phantom movement while parked, marker swarms), and each fix encoded a lesson about real-world GPS. This package is those lessons, made portable.
Contributing
Contributions welcome — see the contributing guide.
Run bun test, bun run lint, bun run typecheck before a PR. Security issues:
SECURITY.md — never in a public issue.
