Develop · Build
How it came together.
- 01
Every world is a real reconstruction
Fly close and it holds up; a skybox would have fallen apart three seconds earlier.
3D & Spatial - 02
One pipeline, nine bodies
One pipeline runs for every body rather than being hand-tuned per planet - which is what made a full solar system tractable solo.
Data Pipelines - 03
A flythrough that behaves like an app
The date scrubber means the system isn't a static diorama: positions move against real orbital mechanics, not a looping animation.
Frontend Engineering - 04
Lighting worlds you cannot photograph in the round
3D Gaussian Splatting assumes full camera coverage; a planet has partial imagery and no photos of its dark side. Reading correctly from any angle meant applying 3DGS past its usual capture-based use case: emission-baked splats, lit per body.
Research
Develop · Forks
Decisions on the record.
The few calls worth defending. Each one is a fork; the other branch would have been a different project.
Decision · 01
Known-pose Blender renders, not structure-from-motion
Ordinary 3DGS training solves for camera pose from a set of photos with unknown positions - structure-from-motion. Satellite imagery and elevation data aren't a photoset like that: the pipeline displaces a UV sphere by the real elevation model where one exists (Earth and Mars) and renders 150 to 300 views from cameras whose position is already known by construction, skipping structure-from-motion entirely. That's what makes training tractable on one GPU instead of a cluster.
Decision · 02
One splat renderer, even for worlds with no photograph
The gas giants have no solid surface to survey and no ground-truth photo the way Earth has. Rather than fall back to a textured sphere for them, they're built as emission-baked splats from open CC-BY colour maps, rendered through the same real-time pipeline as the captured worlds - one renderer, one performance profile, no separate textured-sphere path bolted on for the rest.
Develop · System
The design system.
Tokens and treatments as they exist in the repo - values verbatim, nothing aspirational.
"Deep Field" - a bespoke system built for this repo alone, not shared with any other project. Structurally it's Resend-inspired (near-black canvas, barely-there glass, hairline borders with a top light-edge, a shiny gradient button, subtle grain, Inter type), recoloured to space: void background, starlight ink, and accents pulled from the planets themselves rather than an arbitrary brand hue.
Colour
--sl-void / --sl-void-2#060608 / #0B0C10- Near-black canvas the scene sits on.
--sl-glass / --sl-glass-2rgba(255,255,255,.035) / rgba(255,255,255,.06)- Barely-there white lift over the void - the Resend signature glass fill.
--sl-line / --sl-line-top / --sl-line-strongrgba(255,255,255,.09) / rgba(255,255,255,.16) / rgba(255,255,255,.22)- Hairline borders, brighter at the top edge to fake a light-from-above highlight.
--sl-ink / -dim / -faint#F4F5F7 / #A7ADBA / #6F7585- Starlight text ramp - primary, secondary, tertiary.
--sl-mars -> --sl-mars-2#D24A12 -> #B23C0C- CTA gradient ("Land here") - Mars rust, the one warm high-contrast accent.
--sl-gold -> --sl-gold-2#D8B271 -> #B8945A- Active-state gradient - Saturn gold.
--sl-cosmic / --sl-ice#6B8CFF / #9FD3E0- Cool/tertiary accents - links, ice-giant tint.
Type
- UI text
AaBb Handgloves 0123
Inter (self-hosted via next/font, --font-inter)The Resend face; headings at -0.02em tracking, 600 weight.
- Data / labels / HUD
AaBb Handgloves 0123
ui-monospace, "JetBrains Mono", "SF Mono", Menlo, monospaceThe "instrument" voice - planet labels, panel sub-headers, readouts: mono, 0.08em tracking, uppercase. Type scale: label 11px, body 12px, ui 13px, title 15px.
Implementation
Two surfaces, one theme, documented in the repo's own DESIGN.md. The DOM reads CSS custom properties in app/app/tokens.css (--sl-* names); the three.js scene reads a parallel THEME hex object in lib/theme.ts (SolarSystem.tsx, surface-scene.ts). Per-planet colours are kept separate in lib/solar-system.ts as physical data, not chrome. The rule stated in DESIGN.md: never hardcode a hex, add a token - and keep the two files in sync by hand since there's no shared build step generating one from the other.
Elements
- Glass surface
Glass fill + 1px hairline border + backdrop blur + an inset top highlight/soft drop shadow (--sl-elev) + a ::after top light-edge gradient + a faint ::before grain (inline SVG fractal noise, ~3% opacity).
Principle from DESIGN.md: "glass, not cards" - hairline + blur + top light-edge + grain, never an opaque box, so panels stay honest about sitting over the scene rather than blocking it.
- Shiny gradient button
Vertical gradient fill + 1px white-alpha border + an inset top highlight and glow shadow, in three registers: Mars-rust gradient for the CTA, Saturn-gold gradient for an active chip, plain glass for secondary actions.
- Vignette
A fixed radial darken over the scene edges to sell depth against the void.
Process · 01
Planetary accents instead of a brand colour
The system's only two accents are Mars rust for the CTA and Saturn gold for an active state, both lifted from bodies already on screen. DESIGN.md states the rule directly: colour comes from the planets. It means the palette can never drift somewhere the subject matter cannot justify.
Process · 02
"Instrument, not app"
Data surfaces (planet labels, panel sub-headers, the HUD) are set in mono type, letter-spaced and upper-case, deliberately reading like an instrument readout rather than typical app UI. The alternative (clean sans everywhere) would look more like a product and less like something you're navigating a real system through.
Process · 03
One palette, two source files
A three.js scene and a DOM UI can't share a CSS custom property, so the same token set is declared twice: once as --sl-* CSS variables, once as a hex-valued THEME object. Keeping them as two hand-synced files (rather than e.g. generating the CSS from the JS object at build time) was the simpler choice for a solo project at this scale, at the cost of needing to remember to update both when a colour changes.
