Packages & adapters

Kinesis.js ships a tiny core plus opt-in adapters and wrappers. This page covers each package, how to write your own adapter, Web Worker mode, custom styling and performance tuning.

OpenLayers

@kinesisjs/openlayers manages an OpenLayers Feature<Point> per vehicle, with optional fading trails and gap visualisation. It can attach to an existing layer and scope itself to a set of feature ids so it co-exists with the rest of your map.

openlayers.tsts
import { Tracker } from '@kinesisjs/core';
import { OpenLayersAdapter, createVehicleStyle } from '@kinesisjs/openlayers';

const adapter = new OpenLayersAdapter(map, {
style: createVehicleStyle({ icon: '/car.png', iconScale: 0.7 }),
trail: { enabled: true, maxPoints: 80, width: 4, opacity: 0.45 },
warningOpacity: 0.35,
});

const tracker = new Tracker({ adapter, interpolation: 'adaptive' });
tracker.start();

Leaflet

@kinesisjs/leaflet manages an L.Marker per vehicle with heading-aware rotation baked into the icon. Remember Leaflet's coordinate order is [lat, lng] — the adapter converts your { lng, lat } positions for you — and import its stylesheet.

SSR

Leaflet references window at import time. In an SSR setup, import the Leaflet map component only on the client.

Angular

@kinesisjs/angular exposes a standalone kinesisMap directive plus a kinesisTracker() factory. Inputs accept a Signal or Observable; teardown is automatic via DestroyRef.

live-map.component.tsts
@Component({
standalone: true,
imports: [KinesisMapDirective],
template: `<div kinesisMap [positions]="positions" [interpolation]="'adaptive'"
                [worker]="true" [trail]="trail" class="map"></div>`,
})
export class LiveMapComponent {
positions = inject(FeedService).positions; // Signal<Position[]>
trail = { enabled: true, maxPoints: 80, width: 4, opacity: 0.45 };
}

React, Vue & other frameworks

Because the core is framework-agnostic, you can use it directly from any framework today — construct a Tracker in an effect and dispose it on unmount. Dedicated @kinesisjs/react and @kinesisjs/vue wrappers are on the roadmap for v0.3.

route-aware (OSRM)

@kinesisjs/route-aware is a drop-in CustomInterpolator that snaps positions to the road graph using OSRM. It prefetches polylines, caches them in an LRU, guards against detours, and always falls back to linear — never blocking the tick loop.

route-aware.tsts
import { Tracker } from '@kinesisjs/core';
import { OpenLayersAdapter } from '@kinesisjs/openlayers';
import { OSRMInterpolator } from '@kinesisjs/route-aware';

const tracker = new Tracker({
adapter: new OpenLayersAdapter(map),
interpolation: new OSRMInterpolator({
  baseUrl: 'https://your-osrm.example.com', // self-host for production
  profile: 'driving',
  cacheSize: 500,
  maxDetourFactor: 2.5,
}),
});
tracker.start();

Web Worker mode

At thousands of vehicles, the tick loop competes with the map's own pan/zoom rendering. Move it off-thread with one flag. The real tracker runs in the worker; the adapter stays on the main thread for DOM/map writes.

worker.tsts
const tracker = new Tracker({ adapter, worker: true });
// or point at a hosted worker file:
const hosted = new Tracker({
adapter,
worker: { url: new URL('./kinesis.worker.js', import.meta.url) },
});
CaveatDetail
Custom interpolatorsNot supported — functions can't cross the worker boundary.
Fade animationsDegrade to snapping (no rAF on the worker).
getStats()Lags ~30 ticks behind.
markCompleted / removeVehicleOptimistic.

Custom styling

Pass a style provider — a function of the TrailPoint — to colour or size markers from live data such as speed bands or a per-vehicle meta.color.

styling.tsts
const adapter = new OpenLayersAdapter(map, {
style: createVehicleStyle({
  speedColorBands: [
    { max: 30,  color: '#22c55e' },
    { max: 80,  color: '#eab308' },
    { max: 130, color: '#ef4444' },
  ],
}),
});

Performance tuning

  • Match renderLagMs to your feed period. Too low and motion stutters; too high and it lags reality.
  • Raise ingestThrottle if a bursty feed sends duplicate positions.
  • Enable worker: true only for very large fleets — at typical sizes a 0.15 ms tick is already ~1% of the frame budget.
  • Cap trails with maxPoints to keep memory flat.
  • Watch getStats()droppedTicksLast60s and tickHistoryP99 tell you when to shed work.

Write your own adapter

The contract is small. Implement TrackAdapter — create, update and remove a feature per id, plus teardown — and you have a new map binding.

class MyAdapter implements TrackAdapter { addVehicle(id: string, p: TrailPoint): void { /* draw on your map */ } updatePosition(id: string, p: TrailPoint): void { /* feature.setCoordinates */ } removeVehicle(id: string): void { /* remove from layer */ } destroy(): void { /* cleanup */ } }