I've been working on Flight-Viz, a real-time flight tracker that plots live aircraft on a 3D globe — entirely in the browser. No plugins, no Canvas2D, just Rust compiled to WebAssembly with raw WebGL2 shaders. I wanted to share some of the rendering and data challenges since they took me a while to figure out.
The problem
I wanted to show 10,000+ aircraft moving smoothly on a 3D globe at 60fps. Each plane has its own position, altitude, heading, and aircraft type. The data updates every 2 minutes from OpenSky Network, but planes need to look like they're moving continuously.
Instanced rendering with SDF icons
Instead of loading sprite textures for each aircraft, I draw all the icons purely in the fragment shader using signed distance fields. There are 5 different shapes — commercial jet, cargo plane, propeller plane, helicopter, and military fighter — each built from combinations of SDF primitives (boxes, circles, lines) using min/max operations.
A single float per instance (a_type) tells the shader which shape to draw. The nice thing about this approach is the icons scale perfectly at any zoom level, there's no texture memory used, and all 10K aircraft render in a single glDrawArraysInstanced call.
Each instance carries 12 floats (48 bytes): world position, altitude, heading, surface normal, tangent vector, and aircraft type. The vertex shader uses the normal and tangent to orient a billboard quad on the sphere surface so the plane always points in its heading direction.
Classifying aircraft without an API call
OpenSky doesn't tell you what kind of aircraft it is, so I classify them by callsign:
- Three-letter ICAO code + digits (like AAL100, DAL500) = commercial airline
- FDX, UPS, GTI = cargo
- N-numbers (like N80SL) = private/GA propeller plane
- RCH, EVAC, callsigns starting with NAVY/ARMY = military
- PHI, ERA, callsigns containing HELI = helicopter
It's not perfect, but it's surprisingly accurate and costs zero API calls.
Smooth movement between updates
Flight data only updates every 2 minutes, so between updates I linearly interpolate between the previous and current positions. Once interpolation reaches t=1.0, I switch to dead reckoning — extrapolating using the plane's last known velocity and heading so it doesn't freeze in place. When new data arrives, it snaps to the real position and starts interpolating again.
Supplemental data from FlightRadar24
OpenSky covers about 6,000-10,000 flights. For flights not in OpenSky (like regional carriers), I fetch live positions on-demand from FlightRadar24 through a Cloudflare Worker proxy. This keeps the requests off my server IP and uses Cloudflare's edge caching. The worker is about 50 lines of JavaScript and runs on the free tier (100K requests/day).
I also built a live airport departure/arrival board that looks like a real airport FIDS display — dark blue background, color-coded status, gate info — all from FR24's airport schedule API.
The stack
- Rust → WASM via Trunk (wasm-pack alternative)
- egui for the UI (side panel, windows, popups)
- glow for raw WebGL2 bindings
- Custom GLSL shaders for globe, atmosphere, stars, map tiles, weather overlay, flight icons, route lines
- OpenSky Network for live flight positions (OAuth2 + account rotation for credits)
- FlightRadar24 via Cloudflare Workers for supplemental data
- nginx on a $5 VPS for caching and serving
Total WASM binary is about 4MB gzipped. Loads fast, caches well on CDN.
Try it
Live at flight-viz.com — you can search any flight number (even ones not shown on the map), click airports for live departure boards, filter by airline or country, and toggle between globe and flat map mode.
Happy to answer questions about any of the rendering or data pipeline stuff. The SDF-based icon approach in particular has been really fun to work with and I think it's underused for this kind of real-time data viz.



Top comments (1)
good idea