Interpolation modes

Five built-in curves plus an adaptive meta-mode that reads each vehicle's feed period and chooses at runtime. You can also drop in a custom interpolator for road-snapping or prediction.

How interpolation works

On every frame, the engine asks a simple question: where should this vehicle be right now? It computes a render time slightly behind the wall clock, finds the ratio between the two latest points, and interpolates.

renderTime = now - renderLagMs ratio = (renderTime - prev.receivedAt) / period point = interpolate(prev, curr, ratio) // clamped to [0, 1]

The render-lag buffer (renderLagMs, default 1000) is what keeps motion continuous: it guarantees there is always a next point to move toward. With renderLagMs: 0, the marker simply snaps on every ingest.

The modes

ModeBehaviorBest for
linearStraight-line lerp between two points.Most feeds — the default.
cubicSmoothstep easing (ease-in-out).Softer sub-segment motion.
geodesicGreat-circle arc.Ships, aircraft, long distances.
smooth3-point centripetal Catmull-Rom; falls back to linear until the third ping.Turn-heavy routes.
noneNo interpolation — render snaps to latest.Sub-second feeds, debugging.
adaptivePeriod-aware switcher (see below).Mixed-period feeds.

Adaptive zones

Pass interpolation: 'adaptive' and the engine classifies each vehicle's feed period into a zone, every tick. Thresholds are configurable via the adaptive option.

PeriodZoneBehavior
< 500msnoneSnap — too fast to benefit from interpolation.
500ms – 8slinearStandard linear lerp.
8s – 15sfadeFade out, snap, fade back in.
> 15ssnapDirect jump — gap too large to fake motion.
Since 0.1.2

The lower boundary minPeriodMs defaults to 500 so steady 1 Hz feeds don't sit exactly on the zone edge.

Real-time feeds & render lag

The single most important setting is renderLagMs, and the rule is short: set it to roughly your feed period. A 5-second polling feed wants renderLagMs: 5000.

For jittery feeds — where the gap between updates wobbles — enable a playout buffer. It absorbs the jitter at the cost of a little extra latency, paying out segments at a steady pace.

jitter.tsts
const tracker = new Tracker({
adapter,
interpolation: 'smooth',
playout: 'auto', // measures ~10 recent periods and equalizes the render pace
});

// or tune it manually:
const manual = new Tracker({
adapter,
interpolation: 'smooth',
playout: { pace: 5000, bufferMs: 12000, maxQueue: 20 },
});

Custom interpolators

Anywhere a mode string is accepted you may pass an object implementing CustomInterpolator. The required method is compute(); prepare() (async prefetch) and dispose() are optional.

interface CustomInterpolator { compute(from: TrailPoint, to: TrailPoint, ratio: number, opts?: InterpolationOptions): TrailPoint | Promise<TrailPoint>; prepare?(from: TrailPoint, to: TrailPoint): void | Promise<void>; // prefetch hook dispose?(): void; }

The @kinesisjs/route-aware package is a ready-made custom interpolator: it queries OSRM, snaps positions to the road graph, caches polylines in an LRU, and falls back to linear — all synchronously, never blocking the tick loop.

Where linear stops working

Honest boundaries. Plain linear interpolation is great on highways and short periods; it struggles with urban routes, traffic stops, sharp turns and GPS noise. Adaptive mode covers the period-related cases. The rest are addressed by future packages.

ProblemSolutionVersion
Jump between updatesLinear / smooth interpolationv0.1
Period too short / too longAdaptive modev0.1
Anomalous GPS jumpBuilt-in sanity check + fadev0.1
Routes cut through buildingsRoute-aware (OSRM map-matching)v0.1 · route-aware
Traffic stops, GPS noisePredict / Kalman filteringv0.4+