Point your head at the sky and Sky Finder names what you're looking at — stars, constellations, planets, the Moon, and a handful of deep-sky objects. Built for Meta Ray-Ban Display glasses.
Everything is computed on-device. No API, no key, no network call after the page loads, so it works with the Wi-Fi off and there is no CORS or secret-storage problem to solve.
- Identifies bright stars, constellations, planets, the Moon, and selected deep-sky objects in the wearer's current field of view.
- Supports device orientation when available, with D-pad panning as a seamless desktop, simulator, and sensor-fallback control scheme.
- Guides the wearer to a selected object using its true off-screen bearing.
- Draws a compact, high-contrast sky view designed for a 600 × 600 waveguide display.
- Keeps location, orientation, and sky calculations on the device.
Sky Finder has no backend, analytics, account system, or API keys. It requests only the browser capabilities needed for orientation and (where enabled) device location; those values are used locally to calculate the sky and are never sent to a server by this app.
| File | Purpose |
|---|---|
index.html |
Five screens: home, sky, detail, find, settings |
styles.css |
Dark theme sized to the 600×600 display |
app.js |
Screens, D-pad routing, orientation, canvas renderer |
astro.js |
Pure positional astronomy — no DOM, no globals |
catalog.js |
172 stars, 28 constellation figures, 14 deep-sky objects |
dev-harness.html |
Test harness (development only — safe to delete before deploying) |
| Screen | D-pad | Pinch (Enter) | Back (Escape) |
|---|---|---|---|
| Home / Find / Settings / Detail | Move focus | Activate | Previous screen |
| Sky | Pan the view | Identify centred object | Home |
The sky screen is the one place arrow keys don't move focus — there, panning is the interaction.
Orientation sits behind a provider so the app works with or without a working compass:
- Head tracking —
deviceorientationdrives the view. The view basis comes from the full W3C rotation matrix rather than a hand-rolled angle mapping, so head roll is handled implicitly and there's no degenerate case at the zenith. - Manual pan — arrow keys pan. Used automatically when no orientation event arrives within 2 seconds, which is what happens in the simulator and any desktop browser.
The mode is not shown on the sky screen. A permanent badge reading "D-PAD" told
the wearer something they already knew and spent display area doing it; the
state is published as data-mode on the sky screen instead, which is what the
test harness asserts against.
Because the exact axis convention can't be confirmed without hardware, Settings exposes heading offset, pitch offset, and invert heading. Calibration on-device is a few pinches, not a code change.
cd skyfinder
python3 -m http.server 8777
# then open http://localhost:8777/ in Chrome with the
# "Meta Ray-Ban Display Web App Simulator" extension enabledUse the arrow keys and Enter — the whole app is reachable without a mouse.
For a real device, serve the same static files over HTTPS, then allow the browser's location and orientation permissions when prompted. The app also remains usable when either permission is unavailable: location has a configurable fallback and orientation automatically falls back to manual pan.
The app intentionally has no build step or framework dependency. Clone it, serve the directory, and it runs as a static web app.
Astronomy correctness is verified against known values rather than eyeballed.
Node suites (in the scratchpad during development) check Sun positions at all
four equinoxes and solstices, Polaris altitude tracking observer latitude,
Orion's Belt collinearity and spacing, hemisphere visibility (Crux from Sydney
but not London), and the orientation basis against an independently composed
Rz·Rx·Ry rotation.
dev-harness.html drives the real app in an iframe and scripts D-pad sequences:
http://localhost:8777/dev-harness.html?seq=sky-pan
Sequences: home, sky, sky-pan, sky-wrap, sky-zenith, sky-horizon,
sky-flash, find, settings, guide, guide-hold, sensor,
sensor-fallback, errors.
sky-flash is the odd one: the pan chevron lives for 420ms, too short to catch
in a screenshot under virtual time, so that sequence counts lit canvas pixels in
the box the chevron occupies — once while it should be visible and once after it
should be gone.
| Metric | Limit | Actual |
|---|---|---|
| JS bundle | 500 KB gzipped | ~32 KB |
| Requests on load | < 10 | 7 |
| Idle CPU, sky screen | — | ~18 fps animation tick |
| Idle CPU, every other screen | — | zero; repaints only on input |
Rendering is throttled on a timer rather than requestAnimationFrame, because
rAF is suspended in non-visible contexts and a silently frozen sky is a worse
failure than one drawn a few milliseconds late. Catalogue positions recompute
every 10s (the sky turns 0.25°/minute); redraws only re-project the cached values.
The animation tick starts when the sky screen opens and stops when it closes, so the cost is paid only while the wearer is actually looking at the sky. It feeds the same throttle, which means an animation frame and a burst of orientation events collapse into one paint rather than two.
The waveguide is additive: black emits nothing, so light is the ink and anything large and bright both washes out the real world and costs battery. Everything is built from thin luminous strokes — a compass tape and altitude ladder at the edges, corner brackets that pull inward when the reticle locks, diffraction spikes on the first-magnitude stars, and the constellation figure lighting up around whatever star is currently identified.
Guidance points with three chevrons marching along the object's true screen bearing, computed from the view basis rather than by picking the nearest of four arrow glyphs — which matters most when the object is off-screen or behind the wearer, exactly where a four-way glyph is least useful.
Home and detail share a sky dome: the whole sky as one disc, zenith at the centre, horizon at the rim, north up. It is azimuthal-equidistant, the same projection a paper planisphere uses, and it is drawn once per screen entry rather than animated.