@kinesisjs/core · TypeScript-first · framework-agnostic

Smooth real-time interpolation for .

A TypeScript-first interpolation engine that drops straight into your map. Smooth motion between position updates — WebSocket, polling, or anything else — without writing your own animation loop.

Quick startView on GitHub
MIT<5 KB coreZero runtime deps~60fps @ 1000+ points
See the difference · live

Without it, teleport. With it, .

One position stream, 2.2 s apart. Left snaps; right runs the tick loop.

RAW · setCoordinates()no interpolation
server tick → snap
kinesis.js · interpolated~60fps
tick(hl) → interpolate
Points7
Server period2.2s
Tick @10000.15ms
Dropped0
What's in the box

A narrow problem, solved to production depth.

Framework-agnostic core

Plain TypeScript. The engine has zero runtime deps — bring Angular, React, or no framework at all.

Fixed memory profile

Ring-slot buffers keep two points per object. Allocation-free hot path, bounded memory across a full day.

Adaptive interpolation

Reads the feed period per point and switches between snap, lerp and fade zones — no manual tuning.

Multi-state lifecycle

Points flow through active → warning → stale → completed, with events at every transition.

Built-in sanity checks

Anomalous jumps and sharp turns are caught automatically — fade behavior and cubic fallback, no opt-out.

Error-as-event

Adapter and interpolation failures surface as typed error events — the tick loop never dies.

Reactive binding

The Angular wrapper accepts a Signal or Observable of positions and wires lifecycle to DestroyRef.

Web Worker mode

Move the tick loop off the main thread with one flag — keep map pan and zoom buttery under tens of thousands of points.

Quick start

Three lines to smooth motion.

Pick an adapter, hand the tracker your position stream, and start the loop. Everything else has production-grade defaults.

Tip: set renderLagMs ≈ feed period so the engine always has two points to interpolate between. The default is 1000.

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

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

tracker.start();

ws.onmessage = (e) => tracker.ingest(JSON.parse(e.data));
live-map.component.tsts
import { Component, inject } from '@angular/core';
import { KinesisMapDirective } from '@kinesisjs/angular';

@Component({
  standalone: true,
  imports: [KinesisMapDirective],
  template: `
    <div kinesisMap
         [positions]="positions"
         [interpolation]="'adaptive'"></div>`,
})
export class LiveMapComponent {
  positions = inject(FeedService).positions; // Signal<Position[]>
}
custom-interpolator.tsts
import { Tracker, type TrailPoint } from '@kinesisjs/core';

const tracker = new Tracker({
  adapter,
  interpolation: {
    compute(from: TrailPoint, to: TrailPoint, ratio: number): TrailPoint {
      // snap to your own road graph, predict, dead-reckon…
      return { ...to, lng: from.lng + (to.lng - from.lng) * ratio,
                       lat: from.lat + (to.lat - from.lat) * ratio };
    },
  },
});

tracker.start();
Interpolation modes

Pick a curve — or let it pick for you.

Four curves, plus an adaptive mode that picks per point at runtime.

linear

Straight-line lerp between the two latest points. The default — right for most feeds.

cubic

Smoothstep easing — softer acceleration into and out of each segment.

geodesic

Great-circle arc across the globe. For ships, aircraft and long distances.

smooth

3-point centripetal Catmull-Rom. Rounds the path through turns once a third ping lands.

autoadaptive

Reads feed period per point → snap, lerp, or fade. Zero manual tuning.

Architecture

Three layers, one responsibility each.

Layer 3 · Wrapper
Framework bindings

Reactive inputs (Signal / Observable / Hook) and lifecycle wired to the framework's teardown.

@kinesisjs/angular · react* · vue*
Layer 2 · Adapter
Map feature lifecycle

Create, update and remove features; styling, trails and viewport. One small adapter per map library.

@kinesisjs/openlayers · @kinesisjs/leaflet
Layer 1 · Core
The interpolation engine

Clock (rAF-synced), Interpolator, Sweeper lifecycle, typed EventBus and math utils. Zero deps.

@kinesisjs/core
ingest(positions)SlotBufferClock · rAF tick(hl)interpolateadapter.updatePosition()
Packages

Install only what your map needs.

A tiny core plus opt-in adapters and wrappers — ESM + CJS, fully typed.

@kinesisjs/core

The framework-agnostic interpolation engine: clock, modes, lifecycle, events.

<5 KB min+gzip0 deps
$ npm i @kinesisjs/core
@kinesisjs/openlayers

OpenLayers adapter with marker styling, fading trails and gap visualization.

<3 KBpeer ol
$ npm i @kinesisjs/openlayers
@kinesisjs/angular

Standalone kinesisMap directive — Signals/RxJS in, auto-teardown out.

<2 KBpeer @angular/core
$ npm i @kinesisjs/angular
@kinesisjs/leaflet

Leaflet adapter with heading-aware markers that co-exist with your own layers.

~2 KBpeer leaflet
$ npm i @kinesisjs/leaflet
@kinesisjs/route-aware

OSRM-backed road-snapping CustomInterpolator with an LRU cache.

0 depsdrop-in
$ npm i @kinesisjs/route-aware
Benchmarked, not promised

Built for a screen that stays open all day.

0.15ms
Tick @ 1000 points
~60fps
rAF-synced loop
~1%
Of the frame budget
1000+
Points, one thread

Representative benchmarks — 1000 points, linear mode, desktop Chrome. Real-world figures shift with device, map, point count and browser; the ratios hold.

Read it all live with getStats() — tick p50/p95/p99, ingest rate, dropped ticks, memory. Need more? Web Worker mode (v0.2) moves the loop off-thread.

How it compares

One job, done where others stop.

How Kinesis.js compares to leaflet-realtime, MovingMarker, and hand-rolled setLatLng() calls, across seven capabilities rated full support, partial, or not supported.
CapabilityKinesis.jsThis libraryleaflet-realtimeLeaflet GeoJSON live layerMovingMarkerLeaflet marker tween pluginraw setCoordshand-rolled setLatLng()
~60fps interpolationKinesis.jsYesleaflet-realtimepartialMovingMarkerpartialraw setCoordsNo
Map-library agnosticKinesis.jsYesleaflet-realtimeLeaflet onlyMovingMarkerLeaflet onlyraw setCoordsYes
Adaptive period handlingKinesis.jsYesleaflet-realtimeNoMovingMarkerNoraw setCoordsNo
Bounded memory profileKinesis.jsYesleaflet-realtimepartialMovingMarkerNoraw setCoordsNo
Multi-state lifecycleKinesis.jsYesleaflet-realtimeNoMovingMarkerNoraw setCoordsNo
Error-as-eventKinesis.jsYesleaflet-realtimeNoMovingMarkerNoraw setCoordsNo
TypeScript-firstKinesis.jsYesleaflet-realtimetypesMovingMarkerNoraw setCoordsNo
Roadmap

Shipping in the open.

v0.1 – v0.3
● shipped
  • Core engine + adaptive interpolation
  • OpenLayers & Leaflet adapters
  • Angular wrapper
  • Web Worker mode + gap visualisation
v0.4 – v0.5
shipped
  • route-aware road-snapping (OSRM)
  • smooth interpolation (Catmull-Rom)
  • Playout buffer
v1.0+
planned
  • MapLibre, Mapbox GL, Google Maps adapters
  • React, Vue & Svelte wrappers
v1.0+
planned
  • Ecosystem: clustering, predict, geofence
  • eta, heatmap, devtools, webgl
  • server, replay, styles, labels…
Get started

Stop teleporting your markers.

Drop in the tracker, hand it your stream, and watch the jumps turn into motion. Five kilobytes, zero dependencies.