Real-time isn't optional in serious space GIS. This week you build the WebSocket pipeline that streams live satellite positions to a browser, throttling and reconnecting.
Real-time is not optional in serious space GIS. A launch detection that takes 5 minutes to appear in a user's browser is much less valuable than one that appears in 5 seconds. This week you build the WebSocket pipeline that makes this possible.
Three options for getting server data to a browser in near-real-time:
FastAPI (fastapi.tiangolo.com) has first-class WebSocket support — declarative, async, type-checked. The skeleton:
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
import asyncio, json
from skyfield.api import EarthSatellite, load, wgs84
app = FastAPI()
ts = load.timescale()
sats = [EarthSatellite(l1, l2, name, ts) for name, l1, l2 in tle_catalog]
@app.websocket("/ws/positions")
async def positions(ws: WebSocket):
await ws.accept()
try:
while True:
t = ts.now()
payload = []
for sat in sats[:100]:
sp = wgs84.subpoint(sat.at(t))
payload.append({
"name": sat.name,
"lat": sp.latitude.degrees,
"lon": sp.longitude.degrees,
"alt_km": sp.elevation.km
})
await ws.send_text(json.dumps(payload))
await asyncio.sleep(1.0) # 1 Hz throttle
except WebSocketDisconnect:
pass # clean disconnect — nothing to do
The browser's WebSocket constructor opens the connection. On each message, update marker positions. Throttle DOM updates if needed (1 Hz to the server is fine; rendering at 60 fps in the browser is overkill — animate-tween between samples for smoothness):
const ws = new WebSocket('wss://launchdetect.com/ws/positions');
ws.onmessage = (ev) => {
const positions = JSON.parse(ev.data);
for (const p of positions) {
const marker = markers.get(p.name);
marker?.setLngLat([p.lon, p.lat]);
}
};
ws.onclose = () => setTimeout(reconnect, 1000 * Math.min(retryCount++, 30));
10,000 satellites × 60 Hz = 600,000 updates/second. No browser will keep up; no server should send that. Realistic limits:
WebSockets disconnect for many reasons: WiFi flap, server restart, mobile network switch, load balancer hiccup. A production client must reconnect with exponential backoff (1 s, 2 s, 4 s, 8 s, ..., capped at 30 s) and detect close vs error events. Socket.IO (socket.io) bundles reconnection, namespaces, rooms, and HTTP-polling fallback — at the cost of a heavier wire protocol. For pure server-to-client streaming, raw WebSockets are usually enough.
You'll build a FastAPI WebSocket endpoint that streams positions of 100 satellites at 1 Hz, and a MapLibre client that receives the stream and updates marker positions in real time. Handle disconnects gracefully with exponential backoff. This is the architecture LaunchDetect uses to stream live detection events to the browser — minus authentication and selective subscription, which are Track 5 topics.
Build a FastAPI WebSocket endpoint that streams positions of 100 satellites at 1 Hz. Build a MapLibre client that receives the stream and updates marker positions. Handle disconnects.
Test yourself. Answer key on the certificate-track page (Gold-tier feature: progress tracking and auto-grading).