The map uses Esri's World Topo by default — free, no key, with relief, national
parks and contours in a palette that stays out of the way of the markers. CARTO stamps
API KEY REQUIRED across its tiles without a key; theirs is free, takes a minute and needs
no account at carto.com/basemaps/apikey (5 million tiles a month). Paste
one here only if you prefer their look; blank keeps Esri.
Every enabled source is polled together and merged by aircraft. Your receiver sees
low traffic overhead that the wide networks miss; the networks see everything beyond your horizon.
The union beats any one of them, and any source can drop without taking the layer down — positions
stay on the map for two minutes after their last report, greyed out and labelled with their age.
adsb.lol and adsb.fi are independent community networks with different feeder bases, so
running both is real redundancy rather than the same data twice. Both need the relay below, because
neither sends a CORS header — and adsb.one, airplanes.live and OpenSky all refuse anonymous
browser calls outright.
A PiAware, tar1090 or dump1090 box on your network serves live JSON directly, with no
relay and no rate limits. Usually http://<box>:8080/data/aircraft.json
on PiAware, or /data/aircraft.json on tar1090. Reachable only while you
are on the same network as it.
Note: if you open this dashboard over https
from a web host, the browser blocks calls to a plain-http address on your
own network. The receiver source only works from a local copy of the file, or over http. It shows as
blocked rather than failing quietly.
If you uploaded proxy.php next to this page, you can
leave this blank — a same-origin proxy is detected and used automatically. Otherwise
deploy this free Cloudflare Worker (workers.cloudflare.com → Create Worker → paste →
Deploy) and paste its URL below. Either way the page appends
?url=<encoded feed>.
export default {
async fetch(req) {
const u = new URL(req.url).searchParams.get("url");
if (!u) return new Response("missing ?url=", { status: 400 });
const r = await fetch(u, { headers: { "User-Agent": "firewatch" } });
return new Response(r.body, {
status: r.status,
headers: {
"content-type": r.headers.get("content-type") || "application/json",
"access-control-allow-origin": "*",
"cache-control": "no-store"
}
});
}
}
Search area follows the map — the centre
of your current view, with a radius sized to cover it, capped at the networks' 250 nm limit. Zoom in
for a tighter, faster query; zoom out to sweep wider.
Aircraft identity is filled in automatically from adsbdb.com — free, keyless
and CORS-open, so it needs no setup. It returns the registered owner for each Mode-S hex, which is
how a contracted machine gets recognised as firefighting even when it squawks its registration
instead of a HELITAK callsign. Records are cached in this browser permanently.
Anything that is a direct stream address plays inside this page,
with the same Stop button and no new tab: a community station's Icecast or Shoutcast URL, or your
own scanner — an RTL-SDR box running rtl_airband or SDRTrunk on your network serves one directly.
It has to be the audio itself (ending in .mp3,
.aac, /stream, .m3u8),
not a web page with a player on it.
What will not work, and why it is not worth trying:
Broadcastify strips credentials from a browser audio request, refuses cross-origin fetches and
sends X-Frame-Options: DENY; LiveATC and the commercial networks
(LiSTNR, iHeart) gate theirs the same way, so pasting one of their addresses here will not work.
Same caveat as the receiver above: a page served over
https cannot load a plain-http stream, so a box
on your own network needs https, or open this page over http from a local copy.
Puts a synthetic incident on the map at your current view so you can check the
flashing marker, the flashing list row and the top banner without waiting for a real warning.
It is clearly labelled TEST DRILL everywhere it appears, a striped bar
sits over the map while it is running, and it survives feed refreshes until you end it. Also
reachable as ?drill=emergency in the address bar.