FRONTIER

build notes

Thirteen open data feeds. Twelve are live on the deployed site and the thirteenth stays local because of its licence. Not one needs an API key, an account, or a card on file. Here is every source, what it costs, and the four places this got hard.

Every feed

LayerSourceHow it reaches the page
SatellitesCelesTrakOrbital element sets downloaded once, then propagated in your browser. 16,021 objects cached, 1,400 drawn.
AircraftOpenSky NetworkPushed from a machine at home. See below for why.
EarthquakesUSGSStraight from the source, browser to USGS.
Fires and stormsNASA EONETStraight from the source.
VolcanoesNASA EONET + Smithsonian1,214 volcanoes with a live eruption overlay.
Road camerasTransport for London, NZ Transport Agency1,090 positions vendored, the pictures load from the authority into your browser.
Internet outagesIODA, Georgia TechProxied, cached 10 minutes.
Submarine cablesTeleGeographyLocal only. See the licence section.
SanctionsOpenSanctions291,857 entities across 200 countries, reduced to 22KB at build time.
Geomagnetic KpNOAA SWPCStraight from the source.
Exploited vulnerabilitiesCISA KEV1.7MB reduced server side, cached 30 minutes.
BitcoinBlockstreamProxied, cached 30 minutes.
EthereumBlockscoutProxied, cached 30 minutes.

1. Satellites without a satellite API

The paid version of this buys satellite positions from an API, per request, per satellite. You can skip that entirely.

CelesTrak publishes two-line element sets for free. An element set describes an orbit, and the maths for turning an orbit plus a timestamp into a position has been public since 1980. It is called SGP4, and there is a JavaScript implementation of it. So the browser downloads the element sets once and works out every position itself, every second, forever, with no rate limit and nothing to pay.

16,021 objects are cached. 1,400 of them go on the globe, sampled evenly across the whole set so every orbital plane appears rather than one dense cluster.

CelesTrak answers 403 with "GP data has not updated since your last successful download" if you ask again inside its two-hour window. That is a courtesy, not a block. Cache to disk and the 403 never reaches the page.

2. The licence traps

Free and public are not the same as free to publish. Three of these carry conditions that change what you are allowed to build.

TeleGeography, submarine cables, CC BY-NC-SA 3.0

Non-commercial and share-alike. Share-alike means anything built on it inherits the same licence, which is a decision about your whole project rather than one layer. The cable data sits in a local folder outside the deployed bundle and never goes to a visitor. OpenStreetMap was the obvious substitute and it carries 194 cables against TeleGeography's 728, so it is not one yet.

OpenSanctions, CC BY-NC 4.0

Non-commercial. Fine on a page carrying nobody's price list, and a problem the moment the same page sells something.

Transport for London, TfL Open Data

Reuse permitted with the credit "Powered by TfL Open Data", which has to be visible on the page rather than in a readme. It is in the map attribution.

NZ Transport Agency

Non-commercial reuse of the camera images.

The general rule: read the licence before the API docs. The licence decides what the thing can become, and the API docs only decide how hard it is to get.

3. The two feeds that refuse to talk to a cloud server

Aircraft was the one layer that would not work, and the reason had nothing to do with credentials.

OpenSky answers a laptop at home in about three seconds. It returns 522 to a Cloudflare Worker, every single time, including its OAuth2 token endpoint, which timed out after 19.5 seconds on three consecutive tries while answering the same request from home in about one second. adsb.lol returns 429 to a single sequential request from the same place, and still returns nothing to a paced sweep with 1.2 seconds between requests.

Neither service is throttling this app. They are throttling the whole shared cloud address range, because that range is where the scrapers live. No key fixes that, because the block is not about who you are.

The fix is to turn the arrow around

Stop asking the cloud to fetch it. A small job on a machine that can reach the feed pulls the data every few minutes and pushes it into the cloud app's own storage. The hosted service only ever reads its own store, so it never makes the call that gets blocked.

Two details that make this safe to ship rather than a trap for later:

4. The camera test that was measuring the wrong thing

1,090 public road cameras, and a good share of them serve a grey holding plate instead of a road. The obvious test is whether the picture changes: take two samples, compare, call it dead if they match.

That test condemned most of London. Transport for London rewrites those JPEGs every few minutes, and the samples were 25 seconds apart, so the test was measuring its own impatience.

Colour is the signal that works. A holding plate is grey, mean saturation 0.00. A real street scene sits around 37. One number, no false positives.

Then it failed again, on all 250 New Zealand cameras at once, because it ran at New Zealand midnight and a night scene is also flat and grey. So it now works out whether the sun is up at each camera's coordinates and refuses to judge the ones in the dark. Current scores: 782 usable, 58 holding plates, 250 unknown because it is night there.

What it runs on

One HTML file, one Cloudflare Worker, and a key-value store. MapLibre GL JS for the globe, satellite.js for the orbital maths, both vendored into the page, so there are no CDN calls and the whole thing works on a plane. No build step, no dependencies, no framework.

One trap that cost a day: MapLibre only fires its load event after it paints a frame, and a hidden browser tab never runs requestAnimationFrame. Drive the page from an automation tool that keeps the tab in the background and it sits on the boot screen forever, with nothing in the console. The page is fine. The tab is asleep.