← Journal Guide

How to Build a Live Flight Map (Real-Time Aircraft Positions via API)

Build a real-time flight map that plots live aircraft and animates them smoothly — the endpoints, the render loop, and the dead-reckoning trick, with code.

July 22, 2026 · · 5 min read

A live flight map is one of those features that looks like magic and turns out to be surprisingly tractable: a scatter of aircraft over a map, each one gliding along its heading, updating as the world moves. Whether you're building an operations dashboard, an arrivals board, or a "what's flying overhead" widget, the recipe is the same. This guide walks through it end to end, with the one trick that makes it feel smooth.

Fetch aircraft /airspace?bbox=… Dead-reckon advance by heading × speed Draw & rotate canvas, every frame every ~60s — refetch & snap to truth
Fetch occasionally, interpolate every frame — planes glide instead of jumping.

The two queries you need

A live map is driven by just two endpoints:

Grab the aircraft inside the map's current view in one call:

# every aircraft in the visible box
curl "https://api.flightnerve.com/airspace/KEY?bbox=34,-10,60,20"
 [ { "callsign":"BAW117","latitude":51.3,"longitude":-0.2,
      "altitude":9000,"groundSpeed":260,"heading":78 }, … ]

Drawing them

Render on a <canvas> overlay, not with one DOM marker per plane — a busy sky is thousands of aircraft. For each one, project its lat/lon to screen pixels, translate there, rotate by heading, and draw a small triangle. That's the whole renderer.

The trick: dead-reckoning

If you only redraw when new data arrives, planes teleport every refresh. The fix is to fetch occasionally but animate every frame: between refreshes, advance each aircraft along its own heading at its own ground speed. Over a few seconds a jet flies a straight line, so a simple extrapolation looks perfectly real:

// each animation frame, dt = seconds since last frame
const degPerSec = plane.speedKt * 1.852 / 3600 / 111;   // kt → deg/s
const r = plane.heading * Math.PI/180;
plane.lat += Math.cos(r) * degPerSec * dt;
plane.lon += Math.sin(r) * degPerSec * dt / Math.cos(plane.lat*Math.PI/180);

Refetch /airspace every ~60 seconds and snap positions back to truth. Between fetches, dead-reckoning carries the motion. This is exactly how the map on our own homepage works.

Polling, cost & coverage

Bonus: a live arrivals board

The same endpoint powers an arrivals board in one call — every airborne aircraft routed to an airport, with distance and a rough ETA:

curl "https://api.flightnerve.com/airspace/KEY?inbound=JFK"
 { "aircraft":[ { "callsign":"AAL1185","distanceKm":9.6,"etaMinutes":2 }, … ] }

From snapshot to motion: interpolating between updates

A live map feels alive only if the aircraft move between data refreshes. Since position updates arrive every so often rather than every frame, the trick is dead reckoning: between updates, advance each aircraft along its last known heading at its last known ground speed, then snap it to the true position when a fresh update lands. Done well, planes glide smoothly instead of teleporting.

# advance a marker between updates (per animation frame)
dt      = (now - lastFrame) / 1000
distKm  = groundSpeedKt * 1.852 * (dt / 3600)
lat    += Math.cos(headingRad) * distKm / 111
lon    += Math.sin(headingRad) * distKm / (111 * Math.cos(latRad))

Rendering thousands of aircraft smoothly

A busy sky is thousands of moving objects. DOM markers buckle at that scale, so render to a single <canvas> and redraw each frame, it comfortably handles tens of thousands of points. Colour aircraft by altitude for instant depth, size them subtly by phase, and cull anything outside the current viewport before you draw. Batch your data refresh (one call for the whole visible area) rather than one call per aircraft.

Filtering the sky: show only what matters

Raw "everything airborne" is rarely the product. Give users meaningful slices: aircraft near a point, inside a bounding box, around a specific airport, or inbound to an airport with a live ETA, an instant arrivals board on a map. Server-side filtering keeps the payload small and the map fast.

# everything inbound to JFK, with live ETA
curl "https://api.flightnerve.com/airspace/KEY?inbound=JFK"

Keeping it honest: freshness and coverage

A live map is only as trustworthy as its freshness. Stamp every aircraft with the age of its last fix and fade or drop markers that go stale, so you never show an aircraft frozen in place as if it were live. Coverage is strongest over busy, populated airspace; be transparent about it rather than implying a plane is missing when it is simply out of range.

Frequently asked questions

How do I make aircraft move between updates?

Dead-reckon between refreshes: advance each marker along its heading at its ground speed every frame, then correct to the true position when a new update arrives.

Canvas or DOM markers?

Canvas. A single canvas redrawn per frame scales to tens of thousands of aircraft; individual DOM markers do not.

How do I avoid downloading the whole sky?

Filter server-side, by point and radius, bounding box, airport, or inbound-to-airport, so you only fetch the aircraft actually in view.

How do I show an arrivals board on the map?

Query the airspace endpoint for aircraft inbound to an airport; each comes with a live ETA you can list alongside the map.

Getting started

FlightNerve puts all of this behind a single API key: flight status, live position, airports, routes and live airspace, in one clean JSON shape. It is free to start, no card required. Create a key, make your first call in a minute, and browse the documentation or the airlines and airports we track on the Airlines and Airports pages.

Build it in minutes — free to start.Get an API key →