Chris Collins

Tools · aviation

Flight tracker

Live aircraft near you or any airport — on a map or an offline radar scope, in a sortable table, and as an "overhead now" list — from public ADS-B feeds or your own receiver's aircraft.json, with trails, altitude colours, emergency-squawk alerts and a watchlist.

Added

Works with

How to use it

Pick a Source, say Where (an airport code such as KSFO or LHR, lat, lon, a town — or tap 📍 My location), choose a radius and press ▶ Start. The tool polls the source every few seconds, and only while the tab is visible.

  • Map — aircraft drawn at their reported track, coloured by altitude (orange low → green → blue → magenta at 40 000 ft; grey on the ground; a red outline for 7500/7600/7700), with range rings around the centre. Trails are built from successive polls (the selected aircraft, all, or none). The map is OpenStreetMap through Leaflet and loads only when this view is open; Radar (offline) draws the same picture with no map tiles, and is used automatically if the map cannot load.

  • Tap an aircraft for its callsign, ICAO address, registration and type (when the feed has them), barometric and geometric altitude, ground speed, vertical rate, track, squawk and what it means, distance and bearing from you, how high it is above your horizon, and when it was last seen. Links go to globe.adsb.lol (and your own tar1090 when you use a receiver); Look up asks adsbdb.com for registration, owner and the route on record — only when you tap it.

  • Table — every aircraft, sortable by any column, downloadable as CSV.

  • Overhead — aircraft more than 20° (or your choice) above your horizon, highest first, with the compass direction to look. Plane spotter does the same with your phone’s compass and camera.

  • Filters — altitude band, type, callsign/hex/registration/squawk search, military only (adsb.lol and adsb.fi flag known military airframes), emergencies only, hide aircraft on the ground; units in feet/knots/nautical miles, metric, or statute miles.

  • Watchlist — callsigns (UAL1), prefixes (BAW*), registrations (N123AB) or hex addresses. A match in range shows a toast and, if you allow it, a system notification; emergency squawks alert too. The list stays in this browser (JSON export/import) and never goes into a link.

The first visit shows a recorded sample: a real snapshot of traffic around San Francisco from adsb.lol (15 October 2026, ODbL), moved forward by dead reckoning so it animates. It needs no network.

⛶ Full screen (or F) gives the map or radar the whole screen — the browser’s full screen where it is allowed, otherwise a full-window layer (iPhone). The source panel and the aircraft details start hidden; ◫ Panels brings them back. Escape leaves.

Sources

Source Terms From this page

adsb.lol (/v2/point/lat/lon/nm, up to 250 nm)

Community feeders; data under the Open Database License (ODbL). Free API — the operator asks for modest use, and plans keys for non-feeders.

Needs the relay

adsb.fi open data (/api/v2/lat/…/lon/…/dist/…)

Personal, non-commercial use; at most one request per second.

Needs the relay

OpenSky Network (/api/states/all with a bounding box)

Non-commercial; anonymous users get 400 API credits a day and 10 s resolution, so it is polled at most every 30 s and is a fallback. Cite "The OpenSky Network, https://opensky-network.org".

Needs the relay

Your receiver (aircraft.json)

Yours.

Directly if it sends a CORS header and is reachable without mixed content (below), otherwise through the relay

Why a relay? A web page may only read another site’s data if that site answers with an Access-Control-Allow-Origin header (CORS). On 15 October 2026 none of the public ADS-B APIs did — not for https://geekychris.github.io, not for http://localhost, not for any origin (OpenSky answers only for its own site), and airplanes.live and adsb.one now refuse API use without contacting them first. The tool still tries the source directly first, in case that changes; when it is blocked it says so and offers the recorded sample. If the chosen source fails it falls back to the others and shows which one answered and its terms.

With the gitwebsite app

If the gitwebsite app (or gw serve) is running on your computer, nothing else is needed: its companion relays adsb.lol, adsb.fi, OpenSky and aviationweather.gov, and your receiver too once you set its address in the app’s Companion settings. The tool asks the app first and keeps the relay script and the direct paths below as fallbacks. In the app’s own preview it connects by itself; from this site press ⚡ Use the gitwebsite app (under Receiver & relay) and approve the 6-digit code in the app. While it is in use a ⚡ Companion button in the header shows what goes through it, and disconnects.

Relay

Not needed if the gitwebsite app is running (above) — otherwise: aviation-relay.py is a single Python 3 file with no dependencies (download it, or ⬇ aviation-relay.py under Receiver & relay). It runs on your computer, listens on http://127.0.0.1:8099, and adds the CORS headers. Browsers treat localhost/127.0.0.1 as a secure origin, so this https page may call it (Chrome, Edge and Firefox; Safari may block plain-http localhost — use the HTTPS options below on an iPhone).

curl -O https://geekychris.github.io/tools/aviation-relay.py
python3 aviation-relay.py                                     # public feeds + aviation weather
python3 aviation-relay.py --receiver http://piaware.local:8080 --beast piaware.local:30005 --raw piaware.local:30002

Then set Relay address to http://127.0.0.1:8099 and press Test relay. Routes: /adsblol/…, /adsbfi/…, /opensky/…, /awc/… (aviationweather.gov, for the aviation weather tool), /receiver/… (your --receiver), and two WebSockets for the Mode S decoder: ws://127.0.0.1:8099/beast (Beast binary from port 30005) and ws://127.0.0.1:8099/raw (AVR text from port 30002). It only forwards to those addresses (it is not an open proxy), caches each answer for 2 s so several tabs don’t multiply the load, and binds to 127.0.0.1 unless you pass --host. Only this site and local previews (http://127.0.0.1:…, http://localhost:…, such as the gitwebsite app) may read through it, so other web pages you have open can’t see your receiver; add another origin with --allow-origin https://example.org. Chrome may ask whether this site may "access devices on your local network" the first time — allow it. On a Mac with the python.org installer, the relay finds root certificates from certifi or /etc/ssl/cert.pem.

Your own receiver

You don’t need one — but a receiver shows every aircraft your antenna hears, with signal strength and message counts, no rate limits, and no one else’s terms. What to buy and run:

  1. SDR dongle: an RTL-SDR Blog V4 (general-purpose, ≈ US$40) or a FlightAware Pro Stick Plus (built-in 1090 MHz filter and amplifier — the easiest good result).

  2. Antenna: a 1090 MHz antenna — the FlightAware 66 cm antenna, or a home-made quarter-wave ground plane or collinear. Height and a clear view of the horizon matter far more than anything else; keep the coax short (or use a filtered LNA at the antenna).

  3. Computer: a Raspberry Pi (3B+, 4, 5 or Zero 2 W) with a good power supply.

  4. Software, either:

  5. Check it works in the receiver’s own map, then type the aircraft.json address under Receiver & relay (or just the Pi’s host name to get the usual addresses as buttons) and press Test receiver.

Getting this https page to read it

Two rules apply to a receiver on your network:

  • Mixed content: an https page may not fetch a plain-http address, except localhost/127.0.0.1. http://piaware.local/… from https://geekychris.github.io is blocked by the browser before the Pi is even asked — the tool spots this and says so.

  • CORS: the receiver must answer with Access-Control-Allow-Origin. lighttpd (PiAware, tar1090) and nginx don’t by default.

Pick one:

  1. The relay on the computer you browse from (above): --receiver http://<pi>; and source address http://127.0.0.1:8099/receiver/data/aircraft.json (add tar1090/ or skyaware/ as in the Pi’s own path). Solves both rules; nothing to change on the Pi.

  2. HTTPS for the Pi: sudo tailscale serve --bg 80 on the Pi gives it https://<pi>.<tailnet>.ts.net/ with a real certificate (any device on your tailnet can use it, including your phone away from home); or a reverse proxy with a trusted certificate (Caddy, nginx + Let’s Encrypt DNS challenge). Still add the CORS header below.

  3. The gitwebsite desktop app preview: the app serves this site on http://127.0.0.1, so the page isn’t https and only the CORS header is needed.

CORS header for lighttpd (PiAware, dump1090-fa, tar1090) — create /etc/lighttpd/conf-enabled/89-cors.conf, then sudo systemctl restart lighttpd:

server.modules += ( "mod_setenv" )
$HTTP["url"] =~ "^/(tar1090|skyaware|dump1090-fa|dump1090)?/?data/" {
  setenv.set-response-header += ( "Access-Control-Allow-Origin" => "*" )
}

If lighttpd then refuses to start because mod_setenv is loaded twice (tar1090’s own config may load it), delete the server.modules line. Check with curl -sI http://<pi>/tar1090/data/aircraft.json | grep -i access-control.

For nginx, inside the server { } that serves the map, then sudo nginx -t && sudo systemctl reload nginx:

location ~ ^/(tar1090/|skyaware/)?data/ {
    add_header Access-Control-Allow-Origin "*" always;
    add_header Access-Control-Allow-Private-Network "true" always;
    # keep the root/alias from your existing location for the map
}

Access-Control-Allow-Origin: lets any web page you visit read your receiver’s aircraft list (which is public information anyway — it is what you could feed to an aggregator). Use https://geekychris.github.io instead of to allow only this site.

Raw and Beast feeds

dump1090 and readsb also serve raw messages: AVR text on TCP port 30002 and Beast binary on 30005. Browsers can’t open TCP sockets, so a WebSocket bridge is needed: the gitwebsite app’s TCP relay (add <pi>:30005 to its TCP allow-list), the relay’s --beast/--raw, or pip install websockify and websockify 8765 <pi>:30005. The Mode S decoder reads either, live, and decodes every message field by field.

Limits

  • Public feeds come from volunteers' receivers: coverage is patchy away from cities, MLAT positions wander, and some aircraft (blocked or private registrations, many military) are filtered or show only a hex address. Registration and type come from each aggregator’s database and can be wrong.

  • Polling is every 5–60 s (OpenSky at least 30 s); between polls aircraft don’t move on the map. Trails are only as old as this page’s session.

  • "Overhead" elevation uses the geometric altitude when the feed has it (barometric otherwise, which can be a few hundred feet off), a spherical-earth horizon with no refraction, and your location at ground level unless you use GPS.

  • Notifications only fire while this tab is open and polling; this is not an alerting service.

  • For curiosity and hobby use only — not for navigation, air traffic control, or safety decisions.

Linking to an example

Share link copies a link with your place, radius, source, view, zoom, units and filters. Your location (from 📍 My location), the receiver and relay addresses, and the watchlist are never put into a link; a link with where=gps asks the person who opens it for their own location. Parameters can also be written by hand, for example /tools/flight-tracker/?place=EGLL&r=40#map.

Parameter Meaning Example

place

airport code (ICAO or IATA), lat,lon, or a place name (looked up on OpenStreetMap’s Nominatim)

place=KSFO

lat, lon, label

the place’s coordinates and label, if you want to skip the lookup

lat=51.47&lon=-0.45

where

place, or gps to use the visitor’s own location (asked when they press 📍)

where=place

r

radius in nautical miles (5–250)

r=25

src

adsblol, adsbfi, opensky, receiver, demo

src=demo

view, z, trails, labels

map or radar; map zoom; sel, all or off; callsign labels true/false

view=radar

units

aviation (ft, kt, nm), metric, imperial

units=metric

minalt, maxalt

altitude band in feet

maxalt=5000

q, type

search text; type contains

q=BAW

mil, emerg, ground

military only; emergencies only; hide aircraft on the ground (1/0)

emerg=1

interval

seconds between polls

interval=15

example

a preset below (it picks the tab); other parameters override it

example=sfo-sample

Presets:

  • sfo-sample — the recorded sample around San Francisco (works offline, no relay needed)

  • heathrow — 40 nm around London Heathrow from adsb.lol (needs the relay)

  • jfk-table — a table of traffic within 30 nm of New York JFK

  • schiphol-metric — Amsterdam Schiphol from adsb.fi in metric units

  • my-receiver — your own receiver on the radar view

  • low-traffic — the sample, only aircraft below 5 000 ft

Runs entirely in your browser — network requests go only to the services named on this page.