Live flight tracking for anywhere on Earth, in the browser. No server, no API keys, no build-time secrets — just a static site that talks directly to public aviation APIs.
Named for the heading a controller gives an aircraft — and for what every fix really is: a direction and a magnitude. Point it at any coordinates, airport code, or place name and watch the traffic overhead: altitude-coloured aircraft, ground tracks, and the great-circle route of whichever flight you select.
- Go anywhere. Type
12.9613, 74.89,LHR, orMangaluru— coordinates and IATA codes resolve instantly from a local table, anything else through OpenStreetMap. Or press ⌖ to use your device's location. - Find a flight. Type a callsign (
BAW172), registration (G-VIIN) or ICAO hex and the map flies to that aircraft wherever it is on Earth, then selects it. Airport codes and place names are never mistaken for flights: a callsign always carries a digit, andDELnever does. - Live traffic. Aircraft positions refresh every 3 seconds from airplanes.live, coloured by altitude band, with a fading ground track and climb/descent markers.
- Route arcs. Select a flight and the map flies out to show its full great-circle route, with origin and destination pinned.
- Flight detail. Airline, aircraft type, altitude, speed, distance, time to go, and status — enriched from adsbdb.
- Local-first. Your location, radius, and enrichment cache live in
localStorage. Nothing is sent anywhere except the public APIs below.
Requires Node 20+ and pnpm 10+.
pnpm install
pnpm dev # http://localhost:5173Other scripts:
pnpm build # production bundle into web/dist
pnpm preview # serve the production build
pnpm test # unit tests
pnpm typecheck # tsc across both packagesPushing to main builds and publishes to GitHub Pages via
.github/workflows/deploy.yml. Enable it once under
Settings → Pages → Source → GitHub Actions.
For a project site served from https://<user>.github.io/<repo>/, the workflow
passes the repository name as BASE_PATH so asset URLs resolve correctly. No
other configuration is needed.
The build is entirely static, so any host works — Netlify, Cloudflare Pages, S3,
or npx serve web/dist.
browser
├── airplanes.live aircraft positions (CORS: *)
│ ├── /point/… everything in radius, polled every 3s
│ └── /callsign|reg|hex/… global flight search
├── adsbdb.com route + type lookup (CORS: *)
├── nominatim.osm.org place-name geocoding (CORS: *)
└── CARTO basemap dark raster tiles (no key)
Every upstream sends access-control-allow-origin: *, which is what makes a
serverless build possible. Enrichment results — including negative lookups —
are cached in localStorage for 12 hours so the free APIs aren't hammered.
Flight search takes a different path from the map feed, which only ever returns aircraft inside the current radius. A query shaped like a callsign, registration or hex goes to the matching lookup endpoint; on a hit the map re-centres on that aircraft and selects it, and its route lookup is allowed to jump the request queue so a searched flight doesn't wait behind a hundred others.
| Path | What's in it |
|---|---|
web/src/lib |
Data feed, flight search, config, place resolution |
web/src/display |
Map layer, HUD overlays, search box |
shared/src |
Config schema, guard rails, geo and formatting helpers |
Search radius is clamped to 5–200 km. Coordinates are clamped and wrapped,
and any config restored from localStorage is sanitised before use, so corrupt
storage can't produce an unusable view or a rejected API call.
Aircraft data from airplanes.live. Route and airframe data from adsbdb. Geocoding by Nominatim / OpenStreetMap contributors. Basemap tiles © CARTO, © OpenStreetMap contributors. Airport coordinates from OurAirports (CC0).
The Vector logo is the "plane-departure" icon from
Tabler Icons, used under the MIT licence and
recoloured to the app's palette. It is scaled and inset to sit with some air in
the tile, and its stroke is lightened from Tabler's 2 to 1.5.
web/public/favicon.svg is the source of truth, and
BrandMark in web/src/display/Icon.tsx mirrors
the same geometry so the browser tab and the app show the same mark. The PNG
sizes beside it are committed rather than generated at build time, which keeps
a native image dependency out of CI. To regenerate them after editing the SVG:
pnpm --dir web add -D sharp
node -e "const s=require('./web/node_modules/sharp'),f=require('fs'),v=f.readFileSync('web/public/favicon.svg');\
[[32,'favicon-32'],[192,'icon-192'],[512,'icon-512']].forEach(([n,o])=>s(v,{density:900}).resize(n,n).png().toFile('web/public/'+o+'.png'));\
s(v,{density:900}).resize(180,180).flatten({background:'#06070a'}).png().toFile('web/public/apple-touch-icon.png')"
pnpm --dir web remove sharpThe HUD's own symbols — the metric icons, the panel headers, the radar scope —
are drawn for this project rather than taken from a set. Every path is plain
geometry specified by coordinate. They live in
web/src/display/Icon.tsx.
Vector was rebuilt from cpaczek/skylight, an RTL-SDR ceiling projector. The
server, tracker, Pi provisioning and projector renderer are gone; the data
layer, map, HUD, search and branding are new. Some of the original code
remains, and NOTICE.md records file by file exactly which.
Copyright (C) 2026 Tejaswi C.
Released under the GNU AGPL v3. You may use, study, modify and share it freely. If you run a modified version and let others use it over a network, section 13 requires you to offer them your source as well — rehosting it unchanged is welcome, rehosting it changed and silent is not.