07 · Night Atlas: one particle cloud that becomes a planetarium
Night Atlas is a fictional 48-seat planetarium and half-metre observatory on "Harrow Hill" in Connecticut's Quiet Corner. A single canvas of about 160,000 particles reshapes itself as you scroll: nebula, barred spiral galaxy, Saturn, a live star chart, and finally the venue's name sitting over a dome on a hill. What makes people stop is that the star chart is real. It is turned to the visitor's own clock, next to the actual moon phase and tonight's sunset.
Inspiration
The ideas come from the free tool list, rebuilt by hand in vanilla JS and raw WebGL2, with no copied code.
- Magic UI: Floating 3D Particles (the persistent particle field), Hyper Text (headings that scramble in), Meteors (the occasional shooting star), Dotted Map (the chart grid built from points).
- Aceternity UI: Tracing Beam style SVG path draw-in (the constellation lines draw themselves), Following Pointer (the reticle that trails the cursor), Encrypted Text (the scramble effect again).
- Cult UI and the trends list: glass panels, a HUD frame, a full-screen shader hero.
What was added on top: a morph system that moves the same particles between five shapes, and real astronomy computed from the visitor's clock. Neither of those comes from a component library.
Visual direction
- Palette: true black
#000, starlight#eef3ff, rose#ff4f8f, teal#2ee6d6, amber#ffb454. One CSS variable,--accent, swaps per chapter: rose, teal, amber, teal, amber. - Fonts: Michroma for wide technical display, IBM Plex Sans 300 for body, IBM Plex Mono for data and labels.
- Motion: each particle morphs on its own delay with a quintic ease, and gets pushed around by a turbulence field while in transit. The camera pulls back during a "slew". The pointer tilts the camera and swirls nearby particles. Glass panels rise in as they enter view, headings scramble in.
- Layout: the object sits on one side, a glass instrument panel on the other. Corner ticks, a chapter rail on the right, and live RA/Dec, clock, sidereal time, point count and frame time readouts frame the viewport. The readouts hide when a panel would overlap them.
- Signature moments: the "Slewing to Messier 109" transit, the eyepiece buttons that change Saturn's zoom, the Big Dipper pointer line that draws toward Polaris, the per-pixel moon disc, and the closing wordmark made of particles with a dome, hill and pines drawn in amber line work.
References
- Magic UI, https://magicui.design: Floating 3D Particles, Hyper Text, Meteors, Dotted Map.
- Aceternity UI, https://ui.aceternity.com: Tracing Beam / SVG path draw-in, Following Pointer, Encrypted Text.
- Cult UI, https://www.cult-ui.com: glass panel and HUD framing.
- 21st.dev, https://21st.dev: general pattern source for scroll-driven sections.
Why these choices
One persistent canvas using the same particles gives continuity, so the page reads as one instrument instead of stacked sections. That suits an observatory, where the product is an experience of looking up.
The real sky numbers carry actual content. Sunset and astronomical dusk come from the sunrise equation, the moon phase from the synodic month, and the chart rotation from local sidereal time. A visitor who returns next week sees different numbers.
Trade-offs are visible in the code. Particle count drops to 70,000 on small screens, and a quality governor sheds resolution, then bloom, then particles if frames run slow. A static 2D starfield replaces the canvas if WebGL fails or the context is lost. The scroll content passes under a fixed nav behind a blur scrim, which is a small legibility cost. Reduced motion lowers turbulence, stops the parallax and meteors, and skips the scramble.
Who this kind of site is for
Planetariums, museums, science centres, observatories, tourism attractions, festivals, and product launches that want a journey narrative. It wins when the subject is itself spectacular and the owner wants the site to feel like a visit.
It is the wrong call for a site that must load fast on old phones, for content-heavy or search-driven sites, or for anyone who needs dependable frame rates without testing. The one-canvas approach also makes ordinary content updates harder than a normal page.
How to make it
Stack and key techniques
- Single HTML file, about 1,015 lines, raw WebGL2, no libraries, no build step.
- Particle shader of about 75 lines of GLSL. It draws 160k points (70k on small screens, 180k with
?q=high), binds two shape buffers per chapter pair, uses a quintic staggered morph, and adds ABC-flow turbulence as a divergence-free stand-in for curl noise. A screen-space cursor swirl sits on top. - Saturn in the vertex shader: Lambert shading, back-face culling, a ray-sphere test so the globe hides rings behind it, the planet's shadow on the rings, and ring density from real radii (C, B, Cassini, A, Encke, F).
- Shape generators for a rift-carved nebula, a log-spiral barred galaxy, a chart, and a canvas-sampled wordmark with dome and pines.
- Star chart: polar projection from J2000 coordinates for Ursa Major, Ursa Minor and Cassiopeia, a Milky Way band via galactic-to-equatorial conversion, rotated by sidereal time. SVG lines and labels are projected through the same camera matrix.
- Half-float HDR render target, quarter-resolution bloom, exponential tone map with vignette and dither.
- Working ticket counter and eyepiece zoom.
The prompt
Build a single-file website, index.html, for "Night Atlas", a fictional 48-seat planetarium and half-metre observatory on Harrow Hill in Connecticut's Quiet Corner (41.90 N, 72.00 W). No libraries, no build step, raw WebGL2 and vanilla JS. Everything visual is procedural.
Concept: one persistent full-screen canvas of about 160,000 particles (70,000 on phones) morphs between five shapes as the user scrolls: a rose-and-teal nebula with dark rifts, a barred spiral galaxy (Messier 109), Saturn with real ring structure, a polar star chart, and finally the words NIGHT ATLAS sampled from a canvas, with a dome, hill and pines drawn as amber line work. Sections: hero, Shows, Telescope, Tonight, Visit with a ticket counter. Between them, short "transit" captions such as "Slewing to Messier 109".
Visual direction: true black #000, starlight #eef3ff, rose #ff4f8f, teal #2ee6d6, amber #ffb454. The accent colour changes per chapter. Fonts: Michroma for display, IBM Plex Sans 300 for body, IBM Plex Mono for data. The object sits on one side, a glass instrument panel (blur, hairline border, corner ticks) on the other. Add a HUD: corner ticks, a chapter rail, and live readouts for target, RA, Dec, local clock, sidereal time, point count and frame time. Add a reticle cursor, CSS meteors every 5 to 14 seconds, and headings that scramble in.
Signature moments: staggered per-particle morphs with turbulence in transit and a camera pull-back while slewing; pointer parallax and a cursor swirl; Saturn lit by Lambert shading with the globe hiding the rings behind it, the planet's shadow on the rings, and three eyepiece buttons that zoom the camera; a live star chart for Ursa Major, Ursa Minor and Cassiopeia using real J2000 coordinates, rotated by local sidereal time, with SVG constellation lines that draw in and a dashed pointer line from the Dipper to Polaris, projected through the same camera as the particles.
Real content: compute moon phase (draw a shaded moon disc per pixel), sunset and astronomical dusk by the sunrise equation, and a line saying where the Big Dipper is right now. Show doors, first show and full dark times. Write warm, specific copy for four shows, telescope specs, and visiting info. Build a working ticket counter (pick night, show, quantities, total, a demo ticket stub), labelled as a demo.
Rendering: half-float HDR target, quarter-resolution bloom, tone map. Add a quality governor that lowers resolution, then bloom, then particle count when frames are slow. If WebGL fails or the context is lost, show a static 2D starfield and keep all content working. Respect prefers-reduced-motion. Make it work at 390px wide with no horizontal scroll, and keep text legible over the canvas.
Verification loop: open the page in a browser, screenshot desktop (1440 wide) at about 11 scroll stops and mobile (390) at about 7, read each one, check the console for errors, check for horizontal overflow, force the no-WebGL path, exercise the ticket counter by script. Fix what you find and repeat. Report honestly what you could not test, such as real-GPU frame rate.
Build notes
- Fonts load late, so the wordmark shape is regenerated once Michroma is ready, and again if the aspect ratio flips between landscape and portrait.
- The camera, chart labels and SVG lines share one matrix, so the labels stay on the stars. Label placement pushes each label away from its neighbours' average position.
- The last chapter's vertical shift is recomputed from scroll position so the wordmark settles between the ticket panel and the footer.
- The builder only ran it on software WebGL, about 300 ms per frame. Real-GPU frame rate and motion feel are unconfirmed, and the quality governor never stepped down in testing.
- The reduced-motion path was not exercised, and chart labels were checked at one sidereal orientation only.
- Saturn's RA/Dec is approximate, and the footer says so.
Stepping down: what you would likely lose
This is an informed estimate, not a measurement. No public benchmark compares these exact models on one-shot frontend work, so the rows below rest on Anthropic's tiering, earlier-generation reviews and extrapolation. Fable 5.1 built this with a screenshot loop, which narrows the gap if the other models get the same loop.
| Feature | Opus 5.5 (likely outcome) | Sonnet 5.5 (likely outcome) |
|---|---|---|
| Five-shape particle morph with staggered ease and turbulence | Likely does fine | Likely does fine, probably with simpler turbulence (plain noise instead of a flow field) |
| Saturn shader: Lambert, ring shadow, globe occluding rings | Attempts it with likely small errors, such as rings drawn through the planet or a shadow on the wrong side | Likely simplifies it away: flat rings with no occlusion or shadow |
| Real star chart rotated by sidereal time, SVG lines on the same camera | Likely does fine on the maths, with a risk of a mirrored sky or mismatched label offsets | Attempts it with likely bugs: chart and labels drift apart, or the sky is mirrored |
| Sunset, dusk and moon phase from real formulas | Likely does fine | Likely does fine on the moon, with a risk of wrong time zone handling for sunset |
| Canvas-sampled wordmark with dome and pines | Likely does fine, though it may miss the font-load regeneration, leaving a fallback font | Attempts it with likely bugs: wordmark sampled in the wrong font or misaligned on mobile |
| HDR bloom, tone map and quality governor | Likely does fine | Likely simplifies it: a single blur, or no governor |
| Layout polish: HUD, glass panels, mobile at 390px | Likely does fine, with minor alignment slips | Likely does fine on structure, with more overlap between panels, HUD and canvas on phones |
Rough overall estimate, as a judgment call and not a measurement: Opus 5.5 keeps roughly 80 to 90 percent of the visual impact from the same prompt in one pass, and Sonnet 5.5 roughly 60 to 75 percent. The gap between Fable 5.1 and Opus 5.5 is probably small here, since the hard parts are well-known graphics maths. Sonnet's losses are likely to land in the shader-heavy and cross-system parts, where many coordinate frames must agree.
What closes the gap: a screenshot feedback loop, a few iterations on Saturn and the chart, and splitting the build into shaders first, then chart, then content.