This preliminary Javascript library following the same declarative grammar as the Python traffic library.
In Node.js:
npm install traffic.jsIn Observable:
https://observablehq.com/@xoolive/traffic-js
import * as traffic from 'traffic.js';
const { Flight, Traffic } = traffic.core;When loading from a local dev server, call setEnv once to register Observable
globals so that table() and view() work:
t = require("http://localhost:4000/traffic.js").then(t => {
t.env.setEnv({ Inputs, html, d3, Plot });
return t;
})
// Render an Inputs.table for a Traffic object:
quickstart = await t.data.samples.quickstart();
quickstart.table();
// Render a map + metadata card (returns a Promise; Observable awaits it).
// Works with viewof so the cell's value is the Flight itself:
viewof belevingsvlucht = (await t.data.samples.belevingsvlucht()).view();Field 15 is the ICAO route string in a flight plan (the part between origin and
destination). Resolver parses it and expands airways into sequences of
geographic waypoints using whichever navigation databases you attach.
Step 1 — configure thrust-wasm (once, in a preamble cell):
// Production: no config needed — the library auto-loads from jsDelivr CDN.
// Development (local static server serving pkg/web/):
traffic.env.setThrustWasm({
thrustModuleUrl: 'http://localhost:8002/web/thrust_wasm.js',
});setThrustWasm affects every resolver and parseField15 call in the notebook.
Call it before creating any resolver.
Step 2 — load data sources (can be done in parallel):
// EUROCONTROL DDR — European airways, navaids, airports
ddr = await traffic.data.eurocontrol.createEurocontrolDdrResolver({
archive: await FileAttachment('airac_2111.zip').arrayBuffer(),
});
// FAA NASR — US airways, navaids, airports
nasr = await traffic.data.faa.createNasrResolver({
archiveUrl: 'https://your-server/nasr.zip',
});
// FAA ArcGIS — alternative US source (no archive needed, fetches lazily)
arcgis = await traffic.data.faa.createFaaArcgisResolver();Step 3 — build a Resolver and attach sources in priority order:
// European-first: DDR wins when both DDR and NASR know a waypoint
resolver = new traffic.data.Resolver().withDdr(ddr).withNasr(nasr);
// US-only:
resolver = new traffic.data.Resolver().withNasr(nasr);
// ArcGIS as secondary US fallback:
resolver = new traffic.data.Resolver()
.withDdr(ddr)
.withNasr(nasr)
.withArcgis(arcgis);Step 4 — resolve a route:
route =
'N0490F360 ELCOB6B ELCOB UT300 SENLO UN502 JSY DCT LIZAD DCT MOPAT ' +
'DCT LUNIG DCT MOMIN DCT PIKIL/M084F380 NATD HOIST/N0490F380 N756C ' +
'ANATI/N0441F340 DCT MIVAX DCT OBTEK DCT XORLO ROCKT2';
// Array of { start, end, name? } segment objects:
segments = resolver.enrichRoute(route);
// GeoJSON FeatureCollection of LineString features:
fc = await resolver.enrichRouteAsGeoJSON(route);
// Raw tokenisation (no nav-db needed):
tokens = await resolver.parseField15(route);Step 5 — visualise with Observable Plot:
Plot.plot({
projection: { type: 'mercator', domain: fc },
marks: [
Plot.geo(fc, {
stroke: (d) => d.properties.name ?? 'DCT',
strokeWidth: 2,
tip: true,
title: (d) =>
`${d.properties.start_name} → ${d.properties.end_name}` +
(d.properties.name ? ` via ${d.properties.name}` : ' DCT'),
}),
Plot.dot(
segments.flatMap((s) => [s.start, s.end]),
{ x: 'longitude', y: 'latitude', fill: 'kind', r: 3, tip: true }
),
],
});Each resolver also exposes its underlying navigation database as queryable collections (Observable bracket-lookup pattern):
// List all airports/navaids/airways/airspaces:
await ddr.airports.data();
await ddr.airways.data();
// Bracket lookup by code:
eham = await ddr.airports['EHAM'];
ut300 = await ddr.airways['UT300'];
lfbb = await ddr.airspaces['LFBB'];
// Search by partial name:
await ddr.navaids.search('JSY');arcgis = await traffic.data.faa.createFaaArcgisResolver();
klax = await arcgis.airports['KLAX'];
jfk = await arcgis.airports['KJFK'];The web/ build of thrust_wasm.js must be used for dynamic import() in a
browser (Observable, Vite dev, plain <script type="module">). The bundler
target (pkg/thrust_wasm.js) uses a static .wasm import and will not work
when loaded via URL.
To serve locally:
# from the thrust-wasm pkg directory:
npx serve --cors -p 8002 /path/to/thrust/crates/thrust-wasm/pkg
# then in Observable: traffic.env.setThrustWasm({ thrustModuleUrl: "http://localhost:8002/web/thrust_wasm.js" })Country flag, registration, and type are looked up via
rs1090-wasm if available. Loads lazily
and degrades silently to the raw icao24 hex code when unavailable.
import * as traffic from 'traffic.js';
const info = await traffic.data.aircraftInfo('484506');
// → { icao24: '484506', flag: '🇳🇱', registration: 'PH-TFK', typecode: 'B738', ... }import * as traffic from 'traffic.js';
const resolver = await traffic.data.faa.createFaaArcgisResolver();
const airports = await resolver.airports.data();
const klax = await resolver.airports['KLAX'];traffic.data.faa.createFaaArcgisResolver() auto-loads thrust-wasm:
- Node / bundlers: from the installed npm dependency.
- Browser / Observable: from jsDelivr CDN, then unpkg fallback.
npm run build # build dist/
npm run docs # generate TypeDoc site in docs/
npm run docs:serve # generate + serve docs locally on http://localhost:3000
npm run serve # build and serve dist/ on http://localhost:4000
npm test # run test suite
npm publish # publish to npm registry