The idea

There are only a few dozen black holes well-characterized enough to be worth showing — famous X-ray binaries, the supermassive black holes at the centers of nearby galaxies, a handful of Gaia astrometric detections, and the LIGO/Virgo gravitational-wave mergers. That's small enough to hardcode, which means the whole "point your phone at the sky" feature can work entirely offline, with no backend and no rate limits.

Point your phone up, and the app tells you something like:

Cygnus X-1 — 40° up, toward NE — 7,000 light-years away, ~21 solar masses

How it works

  1. The catalogsrc/data/blackholes.json holds 60 entries: RA/Dec (J2000), mass, distance, discovery year, and a short discovery note for each object, researched from SIMBAD, NASA HEASARC, NED, and arXiv/ADS papers.
  2. Coordinate mathastronomy-engine converts each object's fixed RA/Dec into altitude/azimuth for the user's current location and time. This has to account for precession (RA/Dec is anchored to J2000, but the sky has moved since then), which the app handles by rotating into equator-of-date coordinates before computing horizon position.
  3. Compass + tiltDeviceOrientationEvent gives the phone's raw alpha/beta/gamma angles. The tricky part: you can't just use alpha as compass heading, because when the phone is held upright (pointing at the sky, not lying flat), alpha becomes unstable. The app instead builds the full device-to-world rotation matrix and rotates the phone's "out the back of the camera" vector through it, which stays well-behaved at any tilt.
  4. Matching — every ~10 seconds the app recomputes alt/az for all 60 objects against the live clock and GPS location, then checks which one is within a few degrees of where the phone is currently pointing.
  5. Desktop fallback — no compass on a laptop, so there's a Three.js sky dome you drag to look around, plus a searchable/sortable list view. Both work identically to the phone view, just without the orientation sensor.

Honesty about the data

Not every black hole in the catalog has a real, precise sky position. LIGO/Virgo gravitational-wave detections are localized to sky regions ranging from tens to thousands of square degrees — for most of the 12 GW merger events in the catalog, there's no genuine point-source coordinate published, just a probability region.

Rather than quietly picking an arbitrary point and presenting it as fact, the app:

  • Uses the best available estimate (published skymap peak, EM-counterpart host galaxy when one exists, or an illustrative point within the credible region otherwise)
  • Flags 9 of the 12 GW events with a visible "approx. position" badge in the UI (list view, AR pointer, and sky dome), plus a dashed marker instead of a solid dot
  • Documents the actual localization uncertainty (e.g. "90% credible region ~8,284 sq deg") in each entry's note

The 3 GW events with real confidence are GW170817 (pinpointed via its electromagnetic counterpart, host galaxy NGC 4993), GW170814, and GW190521.

Stack

  • Vite + vanilla JS (no framework) — the whole app is ~10 small modules
  • astronomy-engine — RA/Dec → alt/az conversion
  • Three.js — desktop sky dome
  • DeviceOrientationEvent / getUserMedia — phone compass + camera passthrough
  • No backend, no API keys, no database — the catalog is the data layer

Catalog breakdown (60 objects)

Category Count Examples
Supermassive black holes 26 Sagittarius A*, M87*, TON 618, 3C 273, OJ 287
Stellar-mass / X-ray binaries 19 Cygnus X-1, V404 Cygni, GRS 1915+105
Gaia astrometric detections 3 Gaia BH1, BH2, BH3
LIGO/Virgo GW mergers 12 GW150914, GW170817, GW190521

Running it

cd blackhole-sky
npm install
npm run dev -- --host

Compass and camera access require a secure context — localhost works for local testing, but a phone on the same network needs HTTPS (e.g. deploy to Vercel/Netlify) for those permissions to be granted at all.

What's next

  • Real skymap files (not secondary-source approximations) for the GW merger positions
  • Constellation line overlay in the sky dome
  • Offline/PWA install support