Architecture & concepts
Kinesis.js is organised into three layers with one responsibility each, connected by a single rAF-driven tick pipeline. Understanding that shape explains every option in the API.
Three layers, one responsibility each
Each layer is a separate, independently-versioned package. You only depend on the ones your stack needs.
| Layer | Owns | Examples |
|---|---|---|
| 1 · Core | Time, math, memory, events. The interpolation engine itself. | @kinesisjs/core |
| 2 · Adapter | Map feature lifecycle, styling, trails, viewport. | @kinesisjs/openlayers · @kinesisjs/leaflet |
| 3 · Wrapper | Reactive bindings and framework-managed teardown. | @kinesisjs/angular |
The core never imports a map library, and an adapter never imports a framework. That boundary is what makes Kinesis.js map-library agnostic and framework agnostic at the same time.
The data pipeline
Positions flow one way. Your feed calls ingest(); the clock drives interpolation; the adapter writes to the map.
The clock
The tick loop is built on requestAnimationFrame, not setInterval. That matters for two reasons:
- Frame-synchronous. Updates land exactly when the browser is about to paint, so there's no double-buffering jitter.
- Tab-aware. rAF pauses in background tabs, so the engine never builds up a backlog and never produces a catch-up jump when you return.
Memory management
Each vehicle owns a fixed ring slot holding only the points it needs — previous and current (plus previous2 for the smooth mode). Ingesting a new position shifts the slot rather than allocating, so the hot path is allocation-free and memory stays flat across an 8-hour shift regardless of how many updates arrive.
Stale vehicles are swept out automatically, and trail rendering uses a capped ring buffer (maxPoints). There is no unbounded history to leak.
Lifecycle
The Sweeper watches each vehicle's lastIngestAt and moves it through a small state machine, emitting an event at every transition.
| State | Enters when | Event |
|---|---|---|
active | Receiving updates normally. | vehicleadded |
warning | Idle past warningThreshold (60s). | vehiclewarning |
stale | Idle past staleThreshold (10min) → removed. | vehiclestale |
completed | You call markCompleted(id) (end of shift). | vehiclecompleted |
An adapter can optionally render these states (for example dimming a warning vehicle via warningOpacity) by implementing setVehicleState().
Design principles
- Narrow scope, deep quality. One job — interpolation — done to production depth, not a kitchen-sink mapping toolkit.
- Zero-cost abstractions. The tick loop is allocation-free; adapters add a thin styling layer, nothing more.
- Production defaults. Sanity checks, thresholds and throttling work out of the box.
- TypeScript-first, JavaScript-friendly. Full types, but no build step required to consume.
- Open to new adapters. The
TrackAdapterinterface is ~6 methods; writing one is an afternoon.