Grok Zephyr (also referred to as Colossus Fleet) is a WebGPU-powered orbital simulation featuring 1,048,576 simulated satellites. The project visualizes a massive satellite constellation in Earth orbit at 550 km altitude, inspired by the Grok, SpaceX, and Colossus project concepts.
The simulation renders a real-time light show with RGB beam projections from satellites, viewable from multiple camera perspectives including a 720 km horizon vantage point, free-floating "God View", first-person "Fleet POV", immersive "Ground View" with environmental overlays, and a distant "Moon View". It also supports interactive beam patterns (CHAOS, GROK, 𝕏 LOGO), constellation animation patterns (SMILE, DIGITAL RAIN, HEARTBEAT), and selectable physics propagation modes.
| Component | Technology |
|---|---|
| Graphics API | WebGPU |
| Shading Language | WGSL (WebGPU Shading Language) |
| Frontend | TypeScript 5.9+ |
| Build Tool | Vite 5.0+ |
| Math Utilities | Custom column-major matrix implementation |
| Physics | satellite.js 5.0+ (fallback) + Vallado SGP4 WASM (native/ → public/sgp4.wasm) |
| Package Manager | npm |
| Deployment | Python 3 + Paramiko (SFTP) |
| Testing | Vitest (npm run test) with initial coverage for math + TLE parsing |
grok_zephyr/
├── index.html # Main HTML entry point with UI controls
├── package.json # npm dependencies and scripts
├── tsconfig.json # TypeScript configuration (strict mode)
├── vite.config.ts # Vite build configuration with custom plugins
├── git.sh # Git helper script
├── README.md # Human-readable project description
├── AGENTS.md # This file
├── ARCHITECTURE.md # Architecture documentation
├── initial_plan.md # Design documentation and planning
├── update_plan.md # Recent updates and roadmap
├── SWARM_PROMPT.md # AI prompt context
├── demo-ground-observer.html # Ground observer demo page
├── scripts/
│ └── build-standalone.ts # Standalone HTML build script
├── public/
│ └── tle/
│ └── starlink_sample.txt # Sample Starlink TLE data
├── dist/ # Build output (generated)
└── src/
├── main.ts # Application entry point (GrokZephyrApp class)
├── styles.css # Global styles and UI theming
├── styles/
│ └── ground-observer.css # Ground view overlay styles
├── types/
│ ├── index.ts # Core TypeScript interfaces and types
│ ├── constants.ts # Simulation and rendering constants
│ ├── shaders.ts # Shader-related types
│ └── animation.ts # Animation, LOD, TAA, post-process types
├── core/
│ ├── WebGPUContext.ts # WebGPU adapter/device initialization
│ ├── SatelliteGPUBuffer.ts # GPU buffer management for 1M satellites
│ ├── SatelliteColorBuffer.ts # Per-satellite color buffer management
│ └── BlinkTimingModel.ts # Coherent ground-image blink timing
├── render/
│ ├── RenderPipeline.ts # Main rendering pipeline orchestration
│ ├── PostProcessStack.ts # Post-processing configuration stack
│ ├── RenderTargets.ts # HDR, depth, and bloom target management
│ ├── TrailRenderer.ts # Satellite trail/ribbon rendering
│ ├── SmileV2Pipeline.ts # Smile V2 animation compute pipeline
│ ├── SmileV2Controller.ts # Smile V2 animation state controller
│ ├── passes/
│ │ └── index.ts # Render pass helpers
│ └── pipelines/
│ ├── index.ts # Pipeline exports
│ ├── types.ts # Pipeline type definitions
│ ├── ComputePipeline.ts # Compute pipeline creation
│ ├── ScenePipelines.ts # Scene render pipelines
│ ├── EffectPipelines.ts # Effect render pipelines
│ └── PostProcessPipelines.ts # Bloom/composite pipelines
├── camera/
│ ├── CameraController.ts # View modes and camera math
│ └── GroundObserverCamera.ts # Ground view presets and parallax
├── ui/
│ └── UIManager.ts # HUD updates, control buttons, animation UI
├── utils/
│ ├── math.ts # 3D math utilities (vectors, matrices)
│ └── PerformanceProfiler.ts # FPS and timing metrics
├── physics/
│ ├── TlePropagator.ts # SGP4 CPU anchor (WASM Vallado + satellite.js fallback)
│ ├── Sgp4WasmEngine.ts # Emscripten batch API wrapper (public/sgp4.wasm)
│ ├── keplerianFromState.ts # ECI → Keplerian for GPU extended elements
│ └── index.ts
├── data/
│ ├── ConstellationLoader.ts # Walker constellation generation
│ └── TLELoader.ts # TLE data parsing and loading
├── matrix/
│ ├── ColorMatrix.ts # RGB projection patterns
│ └── AnimationEngine.ts # Animation engine for patterns
├── patterns/
│ └── PatternSequencer.ts # Pattern sequencing logic
├── animations/
│ ├── SmileV2Controller.ts # Smile V2 animation controller
│ ├── SmileV2IntegrationExample.ts # Integration example
│ └── index.ts
└── shaders/
├── index.ts # Central shader exports (canonical runtime WGSL)
├── uniforms.ts # Shared uniform struct (TypeScript)
├── uniforms.wgsl # Legacy include stub (not used at runtime)
├── compute/
│ ├── index.ts # Compute shader exports
│ ├── orbital.ts # Orbital mechanics compute shader
│ └── beam.ts # Beam compute shader
├── render/
│ ├── index.ts # Render shader exports
│ ├── stars.ts # Starfield background
│ ├── earth.ts # Earth sphere rendering
│ ├── atmosphere.ts # Atmospheric limb glow
│ ├── satellites.ts # Satellite billboards (canonical sharp kernel)
│ ├── beam.ts # Laser beam rendering
│ ├── ground.ts # Ground terrain rendering
│ ├── volumetricBeams.ts
│ └── postProcess/
│ ├── index.ts # Post-process shader exports
│ ├── bloomThreshold.ts # Bloom extraction (canonical)
│ ├── bloomBlur.ts
│ └── composite.ts # Final tonemapping
└── animations/
├── index.ts # Animation shader exports
├── smileV2.ts # Smile V2 compute shader (canonical)
├── skyStrips.ts
└── *.wgsl # Archival animation shaders (not imported at runtime)
# Install dependencies
npm install
# Start development server (port 5173)
npm run dev
# Build for production (outputs to dist/)
npm run build
# Build standalone single-file version (grok-zephyr.standalone.html)
npm run build:standalone
# Build Vallado SGP4 WASM module (public/sgp4.wasm + public/sgp4.js)
npm run build:wasm
# Preview production build locally
npm run preview
# Type check without emitting
npm run type-check
# Lint (ESLint + Knip)
npm run lintThe frame is rendered through the following passes:
- Compute Pass: Update 1,048,576 satellite positions via compute shader (16,384 workgroups × 64 threads)
- Beam Compute Pass: Compute laser beam start/end positions based on beam pattern mode
- Smile V2 Compute (optional): Run constellation animation pattern compute if an animation is active
- Scene Pass: Render to HDR texture
- Ground View uses
encodeGroundScenePasswith terrain rendering - Other views use
encodeScenePass(stars → Earth → atmosphere → satellites → beams)
- Ground View uses
- Bloom Threshold: Extract bright pixels to bloom texture
- Bloom Horizontal Blur: Gaussian blur pass
- Bloom Vertical Blur: Gaussian blur pass
- Composite Pass: Tonemap HDR + bloom to swapchain with ACES approximation
const NUM_SATELLITES = 1048576; // 2^20 satellites
const EARTH_RADIUS_KM = 6371.0; // km - Earth radius
const ORBIT_RADIUS_KM = 6921.0; // km - 550km altitude orbit
const CAMERA_RADIUS_KM = 7091.0; // km - 720km altitude camera
const MOON_DISTANCE_KM = 384400.0; // km - average Earth-Moon distance
const MEAN_MOTION = 0.001097; // rad/s - orbital angular velocity
const NUM_PLANES = 1024; // orbital planes
const SATELLITES_PER_PLANE = 1024; // satellites per plane[0-63] view_proj: mat4x4f // View-projection matrix
[64-79] camera_pos: vec4f // Camera position
[80-95] camera_right: vec4f // Camera right vector
[96-111] camera_up: vec4f // Camera up vector
[112-115] time: f32 // Simulation time
[116-119] delta_time: f32 // Frame delta time
[120-123] view_flags: u32 // Packed: view_mode (bits 0-15), is_ground_view (bit 16), physics_mode (bits 17-19)
[124-127] sim_time: f32 // Scaled simulation time
[128-223] frustum: array<vec4f,6> // Frustum planes
[224-231] screen_size: vec2f // Screen dimensions
[232-235] time_scale: f32 // Simulation time multiplier (1x - 100000x)
[236-239] pad0: u32 // Padding
[240-255] sun_position: vec4f // Sun position in ECI frame
| Mode | ID | Description |
|---|---|---|
| 720km Horizon | 0 | Camera at 720km altitude on +X axis, looking along constellation |
| God View | 1 | Orbiting free camera with mouse controls (drag to rotate, scroll to zoom) |
| Fleet POV | 2 | Camera follows satellite #0 in first-person; WASD for micro-drift |
| Ground View | 3 | Surface observer looking up at the constellation; includes environmental overlays |
| Moon View | 4 | Camera positioned at Earth-Moon distance viewing the near-side constellation |
When in Ground View, the following presets are available via UI buttons:
- House (
houseWindow) — View from a house window - Car (
carWindshield) — View from a car windshield - Beach (
beachNight) — Night beach perspective - Rooftop (
rooftop) — Urban rooftop view - Airplane (
airplaneWindow) — View from an airplane window
Each preset applies a different CSS overlay class to #ground-observer-overlay.
Controlled by the "BEAM PATTERN" UI buttons:
- CHAOS (
0) — Random/unstructured beam pattern - GROK (
1) — Grok-branded structured pattern (default) - 𝕏 LOGO (
2) — X logo projection pattern
Controlled by the "CONSTELLATION PATTERNS" UI buttons:
- SMILE (
3) — Smile face constellation animation - DIGITAL RAIN (
4) — Matrix-style digital rain effect - HEARTBEAT (
5) — Pulsing heartbeat pattern
Controlled by the "PHYSICS MODE" UI buttons:
- Simple (
0) — Basic circular orbits (implemented) - Keplerian (
1) — Elliptical orbits with mean anomaly (implemented) - J2 Perturbed (
2) — Oblateness corrections (UI placeholder; not fully implemented in compute shader)
The default procedural mode uses a Walker constellation pattern with multiple inclination shells:
- 53° — main Starlink-like shell
- 70° — polar coverage
- 97.6° — sun-synchronous
- 30° — equatorial
src/main.ts: Main application class GrokZephyrApp that orchestrates:
- WebGPU initialization
- Buffer management
- Render loop
- Camera and UI coordination
- TLE data loading from query parameters
- Pattern/physics/animation mode switching
- Time scale control
src/core/WebGPUContext.ts: WebGPU abstraction layer handling:
- Adapter and device creation
- Canvas context configuration
- Buffer creation helpers
- Error handling with
WebGPUErrorclass
src/core/SatelliteGPUBuffer.ts: GPU memory manager for:
- 16MB orbital elements buffer (read-only)
- 32MB extended elements buffer (for J2 propagation)
- 16MB position buffer (read-write storage)
- 256-byte uniform buffer
- 4MB per-satellite color buffer (rgba8unorm packed)
- 16MB pattern buffer (Sky Strips)
- 2MB beam data buffer (64k beams)
- 32MB trail buffer (2 frames)
- Various uniform buffers for bloom, beams, patterns, Smile V2
- Double-buffered staging uploads (zero CPU stall)
- Total ~118 MB (under Pascal 128 MB safe limit)
src/render/RenderPipeline.ts: Complete rendering system with:
- Pipeline creation for all shader stages
- Render target management (HDR, depth, bloom)
- Bind group setup
- Pass encoding methods (compute, scene, ground, bloom, composite)
src/render/SmileV2Pipeline.ts: Smile V2 animation compute pipeline.
src/render/TrailRenderer.ts: Satellite trail ribbon rendering.
src/camera/CameraController.ts: Camera management:
- Five view modes with smooth transitions
- God view mouse controls (orbit + zoom)
- Fleet POV satellite tracking with WASD micro-movement
- Ground and Moon view camera math
- View-projection matrix calculation
src/camera/GroundObserverCamera.ts: Ground view presets and parallax updates.
Shaders are organized into three domains under src/shaders/:
- compute/ — Compute shaders (orbital mechanics, beams)
- render/ — Render shaders (stars, Earth, atmosphere, satellites, ground, post-process)
- animations/ — Animation shaders (Smile V2, Sky Strips, digital rain, heartbeat, etc.)
The central export is src/shaders/index.ts which exposes SHADERS.compute, SHADERS.render, and SHADERS.animations. Legacy flat exports are deprecated but remain for backward compatibility.
The vite.config.ts includes two custom plugins:
- wgslPlugin: Handles
.wgslfile imports as strings with#importpreprocessing - standalonePlugin: Generates
grok-zephyr.standalone.html(single-file build with inlined JS/CSS) whenmode === 'standalone'(triggered bynpm run build:standalone)
Path aliases configured:
@/*→src/*@/shaders/*→src/shaders/*@/core/*→src/core/*@/render/*→src/render/*@/camera/*→src/camera/*@/matrix/*→src/matrix/*@/ui/*→src/ui/*@/utils/*→src/utils/*@/types/*→src/types/*@/physics/*→src/physics/*
- Strict mode enabled with full type checking (
strict: true) noUnusedLocalsandnoUnusedParametersare enforcednoFallthroughCasesInSwitchis enforced- ES2022 target with ESNext modules
- Module resolution:
bundler - Column-major matrix convention (consistent with WebGPU)
- Custom math utilities (no external 3D math libraries)
- Private methods prefixed with
_(in some files) - Explicit return types on public methods
- Import paths use
.jsextensions (e.g.,@/core/WebGPUContext.js) — Vite resolves these to.tssource files
- Classes:
PascalCase - Methods/Variables:
camelCase - Constants:
UPPER_SNAKE_CASE - Types/Interfaces:
PascalCase - File names:
PascalCase.tsfor classes,camelCase.tsfor utilities
- Uniform struct shared across shaders via string concatenation
- Workgroup size of 64 for compute shaders
- Explicit binding layouts with proper visibility flags
- Shaders are organized in domain subdirectories (
compute/,render/,animations/) - The Vite WGSL plugin supports
#import "relative/path.wgsl"for shader includes
- Chrome 113+ (recommended)
- Edge 113+
- Firefox Nightly with WebGPU flag enabled
- Safari Technology Preview
WebGPU requires a secure context (HTTPS or localhost).
The app is WebGPU-first but ships a toggleable WebGL2 fallback renderer. Use
it whenever you need to see what the simulation renders — WebGPU output is not
readable in headless Chromium, but the WebGL2 canvas is (gl.readPixels /
canvas.toDataURL work; preserveDrawingBuffer is on).
- Activate:
?renderer=webgl(persists inlocalStorage['zephyr.renderer']). - Reduce load:
?renderer=webgl&sats=100000(default = full 1,048,576). - Debug flags:
?renderer=webgl&debug=wireframe,lod,points,noearth,nostars,nobloom,nosats. - Scripting:
window.zephyrGL.{getDebug,setDebug,capture}()for Playwright/agents.
The WebGL path shares simulation state with WebGPU — orbital data + Keplerian
math live in src/core/OrbitalElements.ts (used by both SatelliteGPUBuffer and
the WebGL renderer), and the CameraController drives both. Satellite propagation
runs in the GLSL vertex shader (the "simplified compute fallback" for
orbital_compute.wgsl). Not ported to WebGL: volumetric beams, trails, TAA,
motion blur, DoF, and J2/RK4 physics. Full details, the WGSL→GLSL uniform mapping,
and WebGL→WebGPU porting notes are in docs/WEBGL_FALLBACK.md.
WebGL module layout: src/webgl/{rendererSelection,glUtils,shaders,WebGLRenderer,WebGLDebug}.ts.
Integration points in src/main.ts: initializeWebGL(), renderWebGL, and the
backend branches in initialize() / handleResize() / destroy().
Vallado reference SGP4 is compiled to public/sgp4.wasm via Emscripten (npm run build:wasm).
Prebuilt artifacts are committed; CI rebuilds on native/** changes (.github/workflows/build-wasm.yml).
- Runtime:
TlePropagatorloads WASM when available; falls back tosatellite.json failure. - Batch API:
propagateBatchEci()/applyKeplerianBatch()feed GPU extended-element re-anchoring. - Benchmark: Performance dashboard shows WASM vs JS speedup after TLE catalog load.
- Tests:
Sgp4WasmEngine.test.tschecks 1e-3 km agreement over 24h;Sgp4Benchmark.test.tschecks speedup.
The project uses Vitest (Node environment) with colocated *.test.ts files.
Current covered modules:
src/utils/math.ts— matrix/vector operations and frustum extractionsrc/data/TLELoader.ts— parsing, line2 orbital extraction, and fetch handlingsrc/core/OrbitalElements.ts— Keplerian propagation invariants (shell radius, determinism)src/webgl/rendererSelection.ts— backend +?sats+?debugresolutionsrc/visualHarness.ts— Playwright harness URL param parsing- Visual regression —
npm run test:visual(Playwright + SwiftShader, golden PNGs undertests/visual/baselines/; seedocs/WEBGL_FALLBACK.md)
Recommended next targets:
src/camera/CameraController.ts— camera state calculations for each view mode
Production hosting uses GitHub Pages via .github/workflows/deploy.yml.
- Automatic: merge to
main→ Test runs → on success, Deploy buildsdist/and publishes. - Manual: GitHub Actions → Deploy → Run workflow.
- Prerequisite: repo Settings → Pages → Source must be GitHub Actions.
Local production verification:
npm run build && npm run preview- Compute Shader: Dispatches 16,384 workgroups for 1M satellites
- Frustum Culling: Done in vertex shader to degenerate invisible satellites
- Distance Culling: Satellites >150,000km from camera are not rendered (increased for Ground/Moon views)
- HDR Rendering: Uses
rgba16floatformat for intermediate buffers - Texture Views: Cached to avoid
createView()calls every frame - Pascal Safety: Total GPU buffer footprint is kept under ~118 MB (128 MB safe limit) through tight packing (rgba8unorm colors, compact extended elements, reduced trail frames)
The simulation supports loading real Two-Line Element (TLE) data as an alternative to the default procedural Walker constellation.
Add a ?tle= query parameter to the URL:
# CelesTrak shorthand names:
https://your-host/?tle=starlink # ~6,000 Starlink satellites
https://your-host/?tle=oneweb # OneWeb constellation
https://your-host/?tle=gps # GPS operational satellites
https://your-host/?tle=active # All active satellites (~8,000+)
# Direct URL to any 3-line TLE text file:
https://your-host/?tle=https://example.com/my-satellites.tle
Without ?tle=, the default procedural Walker constellation is used.
starlink, oneweb, iridium, iridium-next, gps, galileo, stations, active
URL ?tle=starlink
→ TLELoader.fromFile(sourceUrl) [src/data/TLELoader.ts]
→ fetch() 3-line TLE text
→ TLELoader.parse() → TLEData[]
→ SatelliteGPUBuffer.loadFromTLEData(tles) [src/core/SatelliteGPUBuffer.ts]
→ For each TLE: parse line2 fixed-width columns
→ Extract: inclination, RAAN, mean anomaly (deg → rad)
→ Derive altitude from mean motion: a = (μ/n²)^(1/3)
→ Classify into shell 0/1/2 by altitude bracket
→ Pack into vec4f: [raan, inc, M, (shell<<8)|colorIdx]
→ Fill remaining slots (up to 1,048,576) with procedural Walker data
→ uploadOrbitalElements() → GPU read-only storage buffer
→ Compute shader propagates all 1M positions per frame (same as procedural)
Real TLE counts (~6K) are much smaller than the 1,048,576 buffer. Remaining slots are filled deterministically with Walker satellites. The HUD displays the data source, e.g. "Source: TLE (6,142 real)".
If TLE fetch/parse fails (network error, CORS, invalid format), the app logs a warning and falls back to procedural generation. Startup is never blocked.
- Scale: Real constellations have ~6K sats vs 1M procedural. Padded sats use the standard Walker pattern.
- Accuracy: TLEs are propagated with the same simplified circular Keplerian model. Full SGP4 in compute shader is not yet implemented.
- Epoch: Simulation uses wall-clock elapsed time, not UTC. Positions drift from reality over time.
- CORS: CelesTrak allows cross-origin. Custom URLs need CORS headers.
- SGP4 Propagation: Currently using simplified Keplerian mechanics in the compute shader; full GPU SGP4 implementation is stubbed
- J2 Perturbations: UI exists but compute shader implementation is incomplete
- GPU Timing: Only works if the browser supports
timestamp-queryfeature - Standalone Build: Creates a single HTML file but requires manual deployment
- No Automated Tests: The project has no unit tests, integration tests, or end-to-end tests
- No sensitive credentials in frontend code or deploy scripts
- WebGPU requires secure context (HTTPS or localhost)
- Add entry to
VIEW_MODESarray insrc/types/constants.ts - Add button to
#controlsdiv inindex.html - Update
setViewMode()method inCameraController.ts - Add camera logic in
calculateCamera()method - Update
estimateVisibleSatellites()inmain.tsif needed
- Orbital elements generated in
SatelliteGPUBuffer.generateOrbitalElements() - Compute shader in
src/shaders/compute/orbital.ts - CPU-side position calculation in
calculateSatellitePosition()for camera tracking
- Shaders are defined in domain subdirectories under
src/shaders/ - Entry point is
src/shaders/index.tswhich exportsSHADERS.compute,SHADERS.render, andSHADERS.animations - Check browser console for shader compilation errors
- The Vite WGSL plugin supports
#import "relative/path.wgsl"for shader includes
- Update the beam compute shader in
src/shaders/compute/beam.ts - Add UI button in
index.htmlwithdata-patternattribute - Update
setPatternMode()inmain.tsif pattern param semantics change
- Create WGSL shader in
src/shaders/animations/ - Export from
src/shaders/animations/index.ts - Add UI button in
index.htmlunder#animation-controls - Wire up in
UIManager.tsandmain.ts
- Presets are defined in
GroundObserverCamera.ts - Overlay CSS classes are in
src/styles/ground-observer.css - Preset buttons are in
index.htmlinside#ground-preset-selector
main.ts
├── WebGPUContext
├── SatelliteGPUBuffer
├── RenderPipeline
│ ├── shaders/index.ts
│ ├── SmileV2Pipeline
│ ├── PostProcessStack
│ └── RenderTargets
├── CameraController
│ └── utils/math.ts
├── GroundObserverCamera
├── UIManager
├── PerformanceProfiler
├── TLELoader
└── types/constants.ts