A browser-based geospatial intelligence platform that lets you observe any place on Earth through the lens of a surveillance analyst β night vision, FLIR thermal, CRT scan lines, live air traffic with detailed aircraft silhouettes and 3D models, current on-demand satellite imagery, and real satellite tracking with orbits draped directly onto a photorealistic 3D globe.
All of it running in a browser tab. No classified clearances required.
NVG Filter during 3D Flight Model Tracking

FLIR Filter during 3D Flight Model Tracking

- Photorealistic 3D Globe β powered by your choice of Google 3D Tiles, Cesium ion (terrain + OSM buildings + Bing satellite), or MapTiler (switchable via a single env variable)
- Live Air Traffic β viewport-aware aircraft from your chosen flight data provider, polled every 10s; aircraft rendered as distinct top-down silhouettes (heavy, widebody, jet, turboprop, helicopter, light) color-coded by classification (commercial/military/other)
- Military Flight Detection β classifies aircraft using ADS-B
dbFlags, known military ICAO hex ranges, and callsign pattern matching - Satellite Orbital Tracking β 200 satellites rendered on actual orbital paths using real TLE data via SGP4 propagation; click any to inspect
- Visual Shader Modes β NVG (night vision), FLIR thermal, CRT scan lines, and anime cel-shading via WebGL post-process stages
- IP Geolocation Startup β camera opens at your approximate location on launch (falls back to configurable env defaults)
- Tactical HUD β corner brackets, UTC clock, live entity counter, coordinate readout, place search, and layer/shader controls
- Street-Level Traffic β live Google traffic-aware flow (Routes API) with automatic OpenStreetMap particle fallback, terrain-aligned to roads
- CCTV Integration β tile-streamed global public camera markers with live snapshot/video inspection panels
- Airspace + Connectivity Overlays β FAA TFR polygons, GPSJam interference zones, and IODA internet blackout polygons with click-to-inspect metadata
- 4D Timeline / Replay β scrub through archived snapshots of all data layers (Phase 11 β stub, not yet implemented)
| Layer | Technology |
|---|---|
| 3D Globe & Rendering | CesiumJS |
| Photorealistic City Models | Google Photorealistic 3D Tiles / Cesium ion / MapTiler (switchable) |
| Visual Shaders | WebGL PostProcessStage (inline GLSL) + CSS overlays |
| Live Flight Data | airplanes.live / adsb.lol / OpenSky Network (switchable) |
| Flight Data Proxy | Node.js HTTP server (server/proxy.mjs) β viewport-aware hub fetching from opendata.adsb.fi |
| Aircraft Classification | ADS-B dbFlags + ICAO hex ranges + callsign pattern matching |
| Satellite Orbital Math | satellite.js (SGP4 propagation) |
| Satellite TLE Data | CelesTrak / Space-Track / N2YO (switchable) |
| Street Traffic Data | Google Routes API traffic-aware polylines / OpenStreetMap Overpass API (auto fallback) |
| Airspace + Outage Overlays | FAA TFR WFS + GPSJam + IODA + SafeAirspace map links |
| Geospatial Overlay Utilities | h3-js + topojson-client |
| CCTV Playback | hls.js for browser HLS playback |
| CCTV Data Pipeline | server/collectors/collectCameras.mjs + tiled camera manifests in public/camera-data |
| IP Geolocation | ipapi.co (free, no key) |
| Build Tool | Vite with vite-plugin-static-copy |
Set VITE_MAP_PROVIDER in your .env to switch instantly β no code changes required.
| Provider | Visual Quality | Cost | Credit Card? | Notes |
|---|---|---|---|---|
cesium |
ββ Terrain + Bing satellite + OSM buildings | 100% free | β No | Recommended default |
google |
βββ Photogrammetric city models | Free tier ($200/mo credit) | β Required | Best possible visuals; falls back to cesium if key is missing |
maptiler |
ββ Quantized-mesh terrain + satellite | 100% free tier | β No | Falls back to cesium if key is missing |
Note: Setting a Cesium ion token is recommended for all setups β it unlocks Bing satellite imagery and suppresses CesiumJS console warnings, even when using Google or MapTiler as the primary provider.
Set VITE_FLIGHT_PROVIDER in your .env to switch.
| Provider | Coverage | Cost | Account / Key? | Notes |
|---|---|---|---|---|
airplaneslive |
Global, unfiltered ADS-B + MLAT | Free | β None required | Great fallback when OpenSky is limited or unavailable |
adsbool |
Global, unfiltered | Free | β None required | ADS-B Exchange drop-in replacement; includes military and untracked flights; ODbL licensed |
(default) opensky |
Global, ~10k aircraft | Free (non-commercial) | β OAuth2 client credentials | Recommended default; strongest live density in test runs; 4,000 credits/day authenticated |
proxy |
Viewport-aware global | Free | β None required | Uses server/proxy.mjs β fetches from opendata.adsb.fi hub grid; best for local dev and server-heavy mode |
Proxy server: The
proxyprovider requires the Node.js proxy to be running separately. See Running the Proxy below.
Set VITE_SATELLITE_PROVIDER in your .env to switch.
| Provider | Objects | Cost | Account / Key? | Notes |
|---|---|---|---|---|
celestrak |
20,000+ | Free | β None required | Recommended default when site is working; uses GP TLE endpoint; transitioning to OMM format for catalog numbers > 69,999 (~July 2026) |
spacetrack |
Full catalog | Free | β Free account (login) | Authoritative US Space Force data (18th Space Defense Squadron) |
n2yo |
Targeted queries | Free tier (1k req/hr) | β Free API key | Better for per-satellite lookups |
Set VITE_TRAFFIC_PROVIDER in your .env to switch.
| Provider | Source | Cost | Key Required? | Notes |
|---|---|---|---|---|
auto |
Google Routes API or OSM | Mixed | β If Google path used | Recommended default; uses Google live traffic when VITE_GOOGLE_MAPS_API_KEY is present, otherwise falls back to OSM simulation |
google |
Google Routes API (TRAFFIC_ON_POLYLINE) |
Paid usage tier | β Yes | Real-time traffic speed intervals (NORMAL / SLOW / TRAFFIC_JAM) along sampled city routes |
osm |
OpenStreetMap Overpass road geometry | Free | β No | Simulated vehicle flow with local-time density profile + terrain-aligned particles |
Optional env vars you can add to .env for tuning behavior:
VITE_SERVER_HEAVY_MODEβ force browser layers to request server-built snapshots (true/false)VITE_DEFAULT_LON,VITE_DEFAULT_LAT,VITE_DEFAULT_ALTβ fallback startup camera positionVITE_DEFAULT_USE_IP_LOCATIONβ use IP geolocation on boot (trueby default)VITE_DEVELOPER_MODEorVITE_DEV_MODEβ enables extra diagnostics widgets in HUD (most useful with heavy mode)VITE_TRAFFIC_MAX_PARTICLESβ caps client-side OSM particle countVITE_SATELLITE_MAX_OBJECTS,VITE_SATELLITE_MAX_PER_CATEGORYβ cap client satellite render countsVITE_SERVER_CAMERA_MAX_OBJECTSβ cap client camera snapshot objects in heavy mode
Proxy/server-side tuning knobs:
SHADOWGRID_SERVER_HEAVY/SHADOWGRID_SERVER_MODEβ enables server heavy mode behavior inserver/proxy.mjsSHADOWGRID_CAMERA_MAX_POINTSβ caps server camera points returned per snapshot
Cesium ion (recommended for all setups)
- Create a free account at ion.cesium.com
- Go to Access Tokens β Create token (default scopes are fine)
- Paste into
VITE_CESIUM_ION_TOKEN
Google Maps (only for VITE_MAP_PROVIDER=google)
- Go to Google Cloud Console and create or select a project
- Enable the Map Tiles API
- Enable the Routes API (required for live street traffic)
- Go to Credentials β Create API Key, then restrict it to "Map Tiles API" and "Routes API"
- Enable billing β the $200/mo free credit covers typical development usage
- Paste into
VITE_GOOGLE_MAPS_API_KEY
MapTiler (only for VITE_MAP_PROVIDER=maptiler)
- Create a free account at cloud.maptiler.com
- Go to Account β API Keys and copy your default key
- Paste into
VITE_MAPTILER_API_KEY
airplanes.live and adsb.lol β no setup required. Just set VITE_FLIGHT_PROVIDER=airplaneslive or adsbool and go.
OpenSky Network (for VITE_FLIGHT_PROVIDER=opensky)
β οΈ OpenSky migrated to OAuth2 in March 2025. The old username/password method no longer works. You now need API client credentials.
- Create a free account at opensky-network.org
- Go to your Account page β "API Client" section
- Click Create API Client β a
credentials.jsonfile will download - Open it and copy
client_idβVITE_OPENSKY_CLIENT_ID - Copy
client_secretβVITE_OPENSKY_CLIENT_SECRET
Rate limits: 4,000 credits/day authenticated; anonymous access is heavily throttled.
CelesTrak β no setup required. Set VITE_SATELLITE_PROVIDER=celestrak and go.
Space-Track (for VITE_SATELLITE_PROVIDER=spacetrack)
- Register for a free account at space-track.org
- Add your credentials to
VITE_SPACETRACK_USERNAMEandVITE_SPACETRACK_PASSWORD
N2YO (for VITE_SATELLITE_PROVIDER=n2yo)
- Request a free API key at n2yo.com/api
- Paste into
VITE_N2YO_API_KEY - Free tier: 1,000 requests/hour
Set VITE_TRAFFIC_PROVIDER to one of:
autoβ use Google live traffic when key is available, else OSM fallbackgoogleβ force Google live traffic pathosmβ force OSM simulation only
The HUD Imagery panel now pulls preview imagery through the local proxy route /api/localproxy/api/satellite-imagery/preview.
- Requests are now validated server-side for allowed
collection + bands + sourcecombinations. - The viewer enforces backend policy by collection authority, while still allowing configured credential-backed backends when available:
| Authority | Preferred Backend | Credentials Required? |
|---|---|---|
| ESA Copernicus | Copernicus Data Space | β Yes |
| ESA Copernicus / Sentinel-5P | Copernicus Data Space | β Yes |
| NASA / USGS (Landsat) | Copernicus Data Space | β Yes |
| NASA (MODIS) | NASA GIBS (default) | β No |
| NOAA VIIRS (Night Lights) | NASA GIBS (default) | β No |
| NASA VIIRS | NASA GIBS (default) | β No |
| NOAA GOES | NASA GIBS (default) | β No |
| NASA / METI ASTER | Copernicus Data Space | β Yes |
| NASA ASTER | Copernicus Data Space | β Yes |
- Copernicus Data Space credentials are required for Copernicus-backed collections. Add these to
.env:
COPERNICUS_DATASPACE_INSTANCE_ID=your_instance_id_here
COPERNICUS_DATASPACE_TRUE_COLOR_LAYER=TRUE_COLOR
COPERNICUS_DATASPACE_FALSE_COLOR_LAYER=FALSE_COLOR- Sentinel Hub remains supported as an optional credential-backed fallback for compatible collections:
SENTINEL_HUB_INSTANCE_ID=your_instance_id_here
SENTINEL_HUB_TRUE_COLOR_LAYER=TRUE_COLOR
SENTINEL_HUB_FALSE_COLOR_LAYER=FALSE_COLOR- Optional imagery backend preference for
Source=Auto:
VITE_SATELLITE_IMAGERY_PROVIDER=autoAllowed values: auto, copernicus-dataspace, sentinel-hub, nasa-gibs.
-
VITE_SATELLITE_IMAGERY_PROVIDERcontrols imagery backend preference only, and is separate fromVITE_SATELLITE_PROVIDER(satellite object/TLE provider). -
Autosource now prioritizes the configured imagery provider (VITE_SATELLITE_IMAGERY_PROVIDER; legacy fallback toVITE_SATELLITE_PROVIDERis still supported), then falls back through policy-backed providers and finally basemap. -
For Copernicus-backed collections, missing credentials now return a clear API error with setup guidance.
-
NASA GIBS-backed collections run credential-free and fall back to basemap if remote imagery is unavailable.
- Node.js v18+
- At minimum, a free Cesium ion token is recommended (no credit card required)
git clone https://github.com/JerichoJack/ShadowGrid.git
cd ShadowGrid
npm installcp .env.example .envThen edit .env and set:
VITE_MAP_PROVIDER=cesium
VITE_CESIUM_ION_TOKEN=your_cesium_ion_token_here
VITE_FLIGHT_PROVIDER=opensky
VITE_SATELLITE_PROVIDER=celestrak
VITE_SATELLITE_IMAGERY_PROVIDER=autoThen generate camera database and tiles (for CCTV layer):
node server/collectors/collectCameras.mjsBy default this pulls OSM man_made=surveillance objects via Overpass and keeps entries matching manufacturer=Flock Safety.
Other modes:
node server/collectors/collectCameras.mjs --mode=trafficvision # Legacy feed mode
node server/collectors/collectCameras.mjs --mode=both # Both trafficvision + OSM (deduplicated)
node server/collectors/collectCameras.mjs --osm-manufacturer="Axis" # Custom manufacturer filter
node server/collectors/collectCameras.mjs --osm-all=true # All OSM surveillance (no filter)npm startor:
npm run dev -- --hostOpen http://localhost:5173 or your local network IP (e.g. http://192.168.1.100:5173) to view on network devices.
Available scripts:
npm run devβ launcher script (scripts/dev.mjs)npm run dev:viteβ Vite only (no proxy)npm run proxyβ proxy server onlynpm run buildβ production buildnpm run previewβ preview production build
If VITE_FLIGHT_PROVIDER=proxy, the app fetches flight data through a local Node.js proxy server.
The proxy runs on port 3001 and handles viewport-aware hub fetching from opendata.adsb.fi, with per-hub caching (12s TTL) and a stale aircraft cleanup (2 min). It is not required if you use the airplaneslive, adsbool, or opensky providers directly.
Run it with:
npm run proxyHeavy mode pushes the expensive aggregation and preprocessing work to the Node proxy so the browser mostly renders already-built snapshots.
Run with:
npm run dev -- --host --serverThis launcher automatically sets VITE_SERVER_HEAVY_MODE=true for the client and enables heavy behavior in the proxy process.
In this mode the proxy now serves cached snapshots for:
- flights (
/api/flights) with short-lived viewport/global cache keys - satellites (
/api/satellites/snapshot) with propagation snapshot caching - traffic (
/api/traffic/google) with viewport-bucket cache keys - cameras (
/api/cameras/snapshot) from server-side tile cache - combined world snapshots (
/api/world/snapshot) for multi-layer fetches
Heavy mode now supports the env-selected flight, satellite, and traffic providers server-side, and also proxies Google/MapTiler tile requests through the local server with on-disk HTTP tile caching (server/cache/tile-http). Cesium frame rendering still happens on the client GPU.
The cache is persisted to server/cache/world-snapshot-cache.json so warm data can survive a restart.
If heavy mode fails with ERR_MODULE_NOT_FOUND (for example, Cannot find package 'h3-js' imported from server/proxy.mjs), the server install is incomplete. Reinstall dependencies from the project root:
npm installThen rerun:
npm run dev -- --host --serverShadowGrid/
βββ server/
β βββ proxy.mjs # Node.js data proxy + snapshot hub
β βββ collectors/
β βββ collectCameras.mjs # Pulls/normalizes public camera feeds + builds camera tiles/manifest
βββ src/
β βββ main.js # Boot sequence β wires globe, layers, and UI
β βββ core/
β β βββ globe.js # CesiumJS viewer + map provider switcher
β β βββ camera.js # IP geolocation startup + fly-to navigation
β β βββ serverSnapshot.js # Heavy-mode world snapshot polling + client-side snapshot apply
β βββ layers/
β β βββ flights.js # Flight provider switcher + aircraft silhouette rendering
β β βββ satellites.js # Satellite provider switcher + SGP4 orbital propagation
β β βββ traffic.js # Google live traffic flow + OSM fallback particle system
β β βββ cctv.js # Global tiled CCTV markers + live snapshot/video inspect panel
β β βββ intrenet.js # Internet blackout polygons from IODA-backed server snapshots
β βββ ui/
β β βββ HUD.js # Coordinate readout + click-to-inspect panel
β β βββ Controls.js # Layer toggles + shader mode buttons + GLSL shaders
β β βββ clock.js # UTC clock
β βββ archive/
β βββ collector.js # Node.js cron: polls APIs, writes snapshots (Phase 7 β stub)
βββ public/
β βββ camera-data/ # Tiled CCTV datasets + manifest (served as static assets)
β βββ favicon.svg
βββ index.html # App shell + HUD markup + CSS (self-contained)
βββ .env.example
βββ vite.config.js
βββ README.md
- β Phase 1 β CesiumJS globe with switchable map provider (Google / Cesium ion / MapTiler)
- β Phase 1 β Switchable flight data providers (airplanes.live / adsb.lol / OpenSky / local proxy)
- β Phase 1 β Switchable satellite TLE providers (CelesTrak / Space-Track / N2YO)
- β Phase 2 β Aircraft silhouette rendering (7 distinct shapes by type code + ADS-B category)
- β Phase 2 β Military/commercial/other classification with color coding
- β Phase 3 β Satellite orbital tracking with SGP4 propagation + click-to-inspect
- β Phase 4 β Street traffic system (Google live traffic + OSM fallback)
- β Phase 5 β CCTV tiled camera layer + live snapshot/video inspection panel
- β Phase 6 β FAA TFR "Safe Flight" polygons, GPSJam medium/high interference hexagons, and IODA blackout polygons tied to Flight/Internet layers with click-to-inspect detail panels.
- β Phase 7 β Visual shaders (Normal, NVG, FLIR, CRT, Anime (don't know if it'll stay, likely not...)) via WebGL PostProcessStage + CSS overlays
- β¬ Phase 8 - Add Satellite Imagery Viewer panel with backend routing and collection-specific policy enforcement for Copernicus Data Space, Sentinel Hub, and NASA GIBS imagery sources (Working, some bugs to work out still)
- β¬ Phase 9 - Add Maritime layer with live ship traffic (AIS) and coastal radar coverage areas (Phase 9 β stub, not yet fully implemented)
- β¬ Phase 10 β Historical storage + replay backend (replace transient snapshot cache with durable spatial/time-indexed storage)
- β¬ Phase 11 β 4D timeline + data archival / replay with archived snapshots and a real time slider
- β¬ Phase 12 β Performance optimizations, mobile support, and UI polish
| Mode | Description |
|---|---|
| Normal | Default photorealistic view |
| NVG | Green-channel night vision with noise grain and vignette (WebGL + CSS radial overlay) |
| FLIR | Thermal false-color (iron palette) simulating infrared sensors |
| CRT | Retro scanline overlay with barrel distortion and phosphor bloom |
| Anime | Cel-shading via Sobel edge detection + quantized color bands |
Aircraft are rendered as distinct top-down silhouettes using inline SVG icons, chosen by type in this priority order:
- ICAO type code (
tfield from ADS-B data) β e.g.B738,A320,H60 - ADS-B category byte β fallback when type code is absent
- Altitude proxy β last resort for completely unidentified aircraft
Classification (commercial / military / other) uses:
dbFlagsbit 0 from ADS-B database (most reliable)- Known military ICAO hex ranges (US, UK, France, Russia, China, and others)
- Callsign pattern matching against known airline and military prefixes
This project is a direct replication and exploration of Bilawal Sidhu's spy satellite simulator concept β a browser-based system that fuses open-source intelligence feeds onto a photorealistic 3D globe. Bilawal's original repo has not been made public; this is my attempt to reverse-engineer and build the same system from the ground up using the same publicly documented tools and data sources.
The core thesis: the data was never the moat. Surveillance-grade views of the world are built entirely from open, public feeds. ShadowGrid makes that visible.
MIT License β see LICENSE for details.
- Bilawal Sidhu β original spy satellite simulator concept and thesis
- CesiumJS β open-source 3D geospatial engine
- Google Maps Platform β Photorealistic 3D Tiles
- Cesium ion β hosted terrain, imagery, and OSM buildings
- MapTiler β terrain and satellite tile services
- airplanes.live β free community ADS-B + MLAT flight data
- adsb.lol β free open ADS-B flight data (ODbL)
- tar1090 β open-source ADS-B receiver software powering many community flight data feeds
- 3D Aircraft Models β used for 3D flight model tracking mode (Follow mode)
- OpenSky Network β open flight data research network
- CelesTrak β free satellite TLE data
- Space-Track.org β US Space Force satellite catalog
- N2YO β satellite tracking API
- satellite.js β SGP4 orbital propagation
- ipapi.co β IP geolocation for startup camera placement
- TrafficVision β aggregated global public traffic camera feed data used to build parts of the CCTV layer; built by Noah Eisenbruch (NERKTEK)
- Surveillance under Surveillance β project tracking global surveillance cameras, inspired parts of the CCTV layer