What it is
ATS Orbit Watch tracks satellites live, entirely in the browser. CelesTrak GP (OMM) orbital elements are propagated client-side with satellite.js — the standard SGP4/SDP4 implementation — and rendered on the ArcGIS Maps SDK, with every position updating on a two-second tick. Five constellations are a click apart: Space Stations, Brightest, GPS, Weather, and Geostationary. Select any satellite and it gains a ground track spanning ±50 minutes, a visibility footprint sized from its live altitude, and an optional follow mode that keeps the camera on it as it moves.
The architecture is the distinctive part: there is no server, no websocket, and no stream — just published orbital elements and mathematics. The browser does the orbital mechanics itself, which means the app costs nothing to run, scales to any number of viewers as static files, and keeps working from bundled element snapshots even if CelesTrak is briefly unreachable (live fetches are cached for fifteen minutes).
It suits satellite operators' outreach pages, ground-station and antenna-pass planning conversations, STEM classrooms and museum installations — and, for ATS prospects, it is the cleanest possible demonstration of real-time computation delivered with zero infrastructure.
The experience
Within seconds of load, satellites are drifting across the dark map — the Space Stations group loads first, with ISS (ZARYA) leading the list. The shell is mission-control dark with a glass detail card, and the interaction model is deliberately simple:
- Pick a constellation. Space Stations, Brightest, GPS, Weather, or Geostationary — the geostationary belt snapping into its arc over the equator is the crowd-pleaser. The list caps at 400 rendered items, the portfolio convention.
- Select a satellite. From the list or by clicking its marker: the detail card opens with live-updating position data, the ±50-minute ground track draws (split cleanly at the antimeridian), and the visibility footprint circle scales with the satellite's actual altitude.
- Follow mode. Locks the view to the selected satellite as it moves — watching the ISS chase the terminator is the demo moment.
- Two-second heartbeat. Every satellite position in the active group re-propagates and redraws every two seconds; the math could run at 60 fps, and is throttled to stay cool.
The data — where it comes from
One live API, one bundled fallback, one anonymous basemap — all free.
| Source | What it provides | Refresh | Cost | Attribution |
|---|---|---|---|---|
| CelesTrak GP API (gp.php, FORMAT=json) — groups: stations, visual, gps-ops, weather, geo | Current OMM orbital elements for the five constellations | Elements refreshed by CelesTrak through the day; app caches each group 15 min | Free (public) | Data courtesy of CelesTrak / Dr. T.S. Kelso |
| Bundled element snapshots (tle_stations.json, tle_visual.json) | Offline fallback for the Stations and Brightest groups if CelesTrak is unreachable | Static, refreshed at build time | Free | CelesTrak |
| Esri dark-gray vector basemap (anonymous) | Dark world basemap under the constellation | Vendor-managed | Free (no API key) | Esri attribution renders in-map automatically |
How it was assembled
The application is a fully static single-page app — no backend, no database, no server-side rendering. The stack: TypeScript, ArcGIS Maps SDK for JavaScript 5.0.x, Calcite Design System 5.0.x, and Vite 8.
Module map — the codebase is small and deliberate; each file owns one concern:
Key engineering decisions
- Client-side SGP4 as the architecture. satellite.js v6's json2satrec() accepts CelesTrak OMM records directly — no TLE string wrangling — and positionAt() wraps propagate() + eciToGeodetic() + gstime(). One trap, found at build time: the propagate() types claim a non-null position, but the runtime returns false when an orbit diverges. Every propagation is guarded and wrapped in try/catch, and bad records are skipped rather than allowed to kill the tick.
- Render-loop economics. Each two-second tick clears and repopulates the satellite GraphicsLayer with a single removeAll + addMany — measured faster at this scale than mutating per-graphic geometry. The 400-item list cap exists because Starlink-scale groups would tank the DOM long before the math became the bottleneck.
- Honest geometry. Ground tracks are computed ±50 minutes around now and split cleanly at the antimeridian instead of smearing across the map; the visibility footprint is a geodesic circle whose radius is derived from the satellite's live altitude, so a GPS bird's footprint dwarfs the ISS's, as it should.
- Resilience without a server. Live group fetches are cached in-app for 15 minutes (matching CelesTrak's guidance for polite use), and bundled element snapshots for Stations and Brightest keep the demo alive if the network or the API is briefly down.
- SDK 5.x specifics. ui.components no longer accepts "attribution" (it renders regardless) — empty components list plus explicit Zoom/Home widgets; strict TypeScript requires an explicit { x, y } type on the click handler used for hit-testing.