An interactive dot matrix black hole simulation and real exoplanet atlas built with React, Three.js, Vinext, and Cloudflare Workers.
Event Horizon Laboratory is a browser based scientific visual experience with two connected instruments.
-
Field Mode renders an expressive particle simulation inspired by matter orbiting a rotating black hole.
-
Atlas Mode searches confirmed planetary systems, enriches them with Gaia astrometry, and renders their orbital architecture in three dimensions.
The goal is not to pretend every visual is a direct observation. The interface explicitly separates catalogued measurements, computed estimates, and artistic geometry.
-
Change black hole mass, spin, particle density, emission, time scale, lensing, and relativistic beaming.
-
Orbit, zoom, reset, and capture the Three.js scene.
-
Search real confirmed exoplanet systems through NASA PSCompPars.
-
Inspect Gaia DR3 position, parallax, proper motion, radial velocity, magnitude, and colour when a matching identifier is available.
-
Enter Mission Mode for a guided journey from Earth to Proxima Centauri, TRAPPIST 1, and TOI 700.
-
Follow a five step overlay walkthrough on the first visit, or replay it from the question mark control.
-
Keep exploring when NASA or Gaia is unavailable through the versioned local catalog snapshot.
Requirements:
-
Node.js 22.13 or newer
-
npm
-
Linux for the included bounded build scripts
git clone https://github.com/ruvnet/Event-Horizon.git
cd Event-Horizon
npm ci
npm run devThen open the local address printed by Vite.
Run the production checks:
npm run lint
npm testThe test command builds the Cloudflare Worker output and verifies the root route, catalog validation, offline fallback, Three.js runtime boundary, Mission Mode, and overlay walkthrough.
The browser never receives a general catalog proxy.
Browser search
↓
Fixed server route at /api/astro
↓
NASA Exoplanet Archive PSCompPars
↓
Optional Gaia DR3 enrichment
↓
Normalized StarSystem response
↓
Three.js Atlas or local snapshot fallback
The server accepts only a validated system name. It owns the endpoint, table, selected fields, ordering, row limit, redirect policy, timeout, and response size cap. Users cannot submit a URL, table name, column list, or ADQL fragment.
A value returned by NASA PSCompPars or Gaia DR3, or preserved in the versioned fallback snapshot.
Examples include right ascension, declination, distance, stellar temperature, orbital period, planet radius, and planet mass.
A value derived from catalogued inputs.
For example, when a semi major axis is unavailable but period and stellar mass exist, the interface can estimate orbital scale using Kepler's third law.
Geometry chosen to make an incomplete system understandable.
Most exoplanets do not have enough published orbital elements to reconstruct current three dimensional position. Missing orientation and instantaneous phase therefore use deterministic artistic values and remain labelled as such.
The application keeps rendering and external data isolated.
-
app/page.tsxowns Field Mode, shared laboratory controls, dialogs, administrative settings, and the walkthrough overlay. -
app/atlas-mode.tsxowns Atlas Mode, Mission Mode, system navigation, provenance presentation, and its browser only Three.js scene. -
app/api/astro/route.tsowns the bounded NASA and Gaia query templates, validation, normalization, timeouts, caching headers, and fallback behavior. -
app/astro/catalog.tsdefines the normalized catalog contract and versioned featured systems. -
worker/index.tsadapts Vinext output to the Cloudflare Worker runtime. -
tests/rendered-html.test.mjsexercises Worker compatibility and the critical security invariants.
Read the deeper architecture guide and architecture decision records.
Three.js and OrbitControls are loaded only inside browser effects through dynamic imports. Nothing creates a renderer, clock, control object, texture, or scene at Worker module scope.
This boundary prevents server rendering and Cloudflare Worker startup from evaluating browser only Three.js code. A source invariant test protects the rule.
-
Atlas background stars: 8,000 on mobile and 18,000 on desktop.
-
Device pixel ratio ceiling: 1.25 on mobile and 1.5 on desktop.
-
Catalog response cap: 900,000 bytes.
-
Upstream timeout: 4 seconds for each bounded catalog request.
-
Root route: independent from NASA, Gaia, and Three.js initialization.
-
Offline featured system load: local and immediate.
-
NASA Exoplanet Archive using the composite PSCompPars table
-
ESA Gaia Archive using Gaia DR3
PSCompPars is a composite table. Values for different fields may originate from different publications and are not guaranteed to form a single self consistent orbital solution.
app/
api/astro/route.ts Bounded catalog service
astro/catalog.ts Types and fallback systems
atlas-mode.tsx Atlas and Mission experiences
globals.css Complete instrument visual system
layout.tsx Metadata and document shell
page.tsx Field experience and shared controls
docs/
adr/ Architecture decisions
images/ Repository media
tests/
rendered-html.test.mjs Worker and security acceptance checks
worker/
index.ts Cloudflare Worker entry point
The main operational risk is turning the catalog route into an anonymous TAP relay. The implementation prevents that by fixing both upstream hosts and query templates, validating all names, limiting rows, rejecting redirects, enforcing timeouts, capping response bodies, and never accepting arbitrary ADQL.
The application contains no catalog credentials. Local preferences and walkthrough completion stay in browser storage. See SECURITY.md for the full boundary and reporting guidance.
-
Field Mode is an expressive parametric model, not a numerical general relativity solver.
-
Exoplanet orientation and current phase are often unknown.
-
The local snapshot contains selected showcase systems rather than the entire NASA or Gaia catalog.
-
Live system detail depends on public archive availability and quota policy.
-
The repository currently has no software license. Public visibility does not itself grant reuse rights.
The production application runs as a Vinext application compiled to a Cloudflare Worker compatible bundle.
npm run build
npm run startThe checked build produces dist/server/index.js with a default Worker object exposing fetch(request, env, ctx).
The repository includes .openai/hosting.example.json rather than the live Site identity. Copy it to .openai/hosting.json only when attaching your own Sites project.
Keep changes scientifically honest and operationally bounded.
-
Preserve the browser only Three.js import invariant.
-
Do not add a general upstream proxy or user supplied ADQL.
-
Preserve null values as unknown rather than converting them to zero.
-
Label every derived or illustrative value.
-
Add or update an acceptance check for every new trust boundary.
-
Run
npm run lintandnpm testbefore proposing a change.
