Rendering a Real Solar System in the Browser
Make it real first. Then make it cheap enough to show everyone.
For engineering and product leaders weighing 3D, WebGL or data-heavy visuals against performance budgets.
Updated , by A Few Good Coders
Published 8 min read10 sectionsEngineering note
Key takeaways
- Every fact in the scene cites a primary source such as NASA NSSDC, JPL Solar System Dynamics or the NASA Exoplanet Archive, carries a review date, and anything illustrative is labelled.
- Light is handled physically where people look: linear HDR, a bloom threshold that only catches true emitters, ACES tone mapping and ray-marched atmospheres.
- Nothing 3D sits on the load path: a static preview is the LCP element, WebGL mounts after load and idle, and quality tiers plus a frame-rate monitor keep each device inside its budget.
Why a studio website renders planets
The background of this website is a live WebGL scene. Earth sits behind the homepage, a different body is framed behind each section, and the space explorer lets you travel to planets, moons, nebulae, galaxies and a black hole. It is the most visible piece of engineering on the site, so it has to keep two promises at once.
The first promise is that it is real. If we show Saturn's rings, the planet's shadow should fall across them where physics puts it. If a panel says Europa's ice shell is 15 to 25 km thick, a reader should be able to click through to the source.
The second promise is that it costs the visitor nothing. A company site that takes four seconds to show its headline has failed, however beautiful the planet behind it. Our working target is a Lighthouse performance score of 95 or more on every marketing page, with the scene present.
This note explains how the scene keeps both promises. The same trade-offs show up in any product that ships heavy visuals: maps, 3D product configurators, medical imaging, games and data-dense dashboards.
Start from sourced data, not a shader
We started with a data layer. Every celestial object in the scene, about 120 in all, is a typed record in the codebase: the Sun, the planets, 21 major moons, dwarf planets, small bodies, exoplanets, galaxies and phenomena such as pulsars and gravitational waves. Each fact is stored as a label, a value and a source link, and each record carries the date it was last reviewed.
Three rules keep the data honest:
- Volatile numbers carry an as-of date. Moon counts and mission schedules change, so those facts show when they were checked.
- Illustrations are labelled. No telescope has resolved the surface of an exoplanet, so every exoplanet view is flagged as an artist's concept in the data, and the detail panel says so.
- Uncertain science stays uncertain. Where a result is disputed, the copy says it is disputed instead of picking the more exciting reading.
Because facts are data, the same records feed the 3D scene, the detail panels, the server-rendered pages that search engines and AI assistants read, and the test suite. A unit test fails if any fact loses its https source.
Real maps, honest gaps
Surfaces come from published global maps, not painted textures.
Two details matter more than resolution. Venus in visible light is a cloud deck, so we never download a radar map of its ground; you would not see it from orbit. And spacecraft have not imaged every hemisphere of every moon. Voyager 2 saw only the southern halves of the Uranian moons and Triton, so those regions are filled procedurally and the credits say which areas are fill rather than data. Deimos has no published global colour mosaic, so its texture is procedural and labelled as such.
Stars get the same treatment. Each catalogue star sits at its true J2000 position. Its colour is computed from its B−V index through a blackbody curve, and its size from its visual magnitude. The 54 KB catalogue loads when the main thread is idle, after the first frame.
Light that behaves like light
Most space scenes look fake because of the light, not the geometry. Three choices did most of the work.
Linear HDR with a real threshold. On the full tier in the explorer, the scene renders linear high-dynamic-range colour into a half-float buffer. Bloom only picks up values above 1.0, which in this scene means true emitters: the solar photosphere and corona, city lights, aurora and accretion disks. Lit planet discs and stars stay below the threshold, so nothing glows that should not. ACES filmic tone mapping runs after bloom, so highlights roll off the way they do in a camera. Every other route applies the same ACES curve in the renderer, so the colour grade does not jump when the post-processing chain mounts.
Ray-marched atmospheres. Earth, Mars, Venus and the giant planets get a ray-marched scattering shell. Earth's coefficients come from Sébastien Hillaire's 2020 sky and atmosphere model; the other bodies use their own gas and haze mixes on the same method. That is what turns Earth's limb blue and its terminator orange, and keeps Mars dusty. The march takes 16, 8 or 6 steps per pixel depending on the quality tier.
Small physics where people look. Earth's ocean glint comes from a real land and water mask instead of a guess from colour. City lights fade in through civil and nautical twilight. The aurora sits on ovals around the geomagnetic pole (80.65° N, 72.68° W) and uses real emission lines: 557.7 nm green, 630.0 nm red at the top and a faint 427.8 nm blue along the lower edge. Saturn's rings are shaded from their optical depth, with different responses for the lit and unlit faces and the globe's shadow falling across them.
We also write down where we chose art over physics. The Sun's real photosphere is close to white; ours carries a warm grade, and its sunspots are illustrative. Saying so is part of being accurate.
A budget for every device
Realism is worthless if it drops frames on a mid-range phone. The scene picks one of three quality tiers at runtime.
A visitor who asks for reduced motion in the explorer gets a still scene that renders only when they interact with it. On top of the tiers, a frame-rate monitor watches the live scene. After sustained low frame rates it caps the render scale at 1, and if that is not enough it switches post-processing off.
The render loop is lazy too. It stops while the tab is hidden, while shaders warm up and once a company page has scrolled the planet out of view. A still scene that has settled renders only on demand. The cheapest frame is the one you never draw.
Never block the first paint
The headline has to be readable before any 3D code runs. On every page, the first thing painted behind the text is a static image: an 11.6 KB Earth preview on the homepage and, for other bodies, a 512 × 512 disc rendered offline from the real scene, each under 30 KB. That image is the Largest Contentful Paint element. The WebGL scene fades in over it only after it has presented its first real frame, so there is no flash.
The scene itself is deferred:
- Explorer: mounts straight away, one task after the page hydrates.
- Desktop company pages: after the
loadevent, a 250 ms pause and an idle callback. - Phones and touch screens: after
loadand the visitor's first touch, scroll or key press, then an idle callback. A phone that never interacts keeps the static image and never downloads three.js. - Save-Data or 2G connections: company pages keep the static preview and never load the scene.
Even after it mounts, the first frame stays light. Every tier starts from 2K surface maps and a 1K cloud layer; Earth's day map, clouds and night lights add up to about 371 KB. The 4K surfaces, finer clouds and the ocean mask stream in after a few presented frames. They are decoded off the main thread with createImageBitmap (falling back to img.decode() on older browsers), uploaded to the GPU before use and swapped in without recompiling a shader. Shader compilation is warmed asynchronously and capped at 250 ms, so a slow driver cannot hold the page hostage. The roughly 190 KB of compressed three.js never sits on the critical path of a company page.
What carries over to client work
None of this is specific to astronomy. The same habits apply to a 3D product configurator, a medical visualisation or a data-heavy dashboard:
- Source the data and test it. If a number can be wrong, store where it came from and when it was checked.
- Label the illustrations. People forgive an artist's concept. They do not forgive being misled.
- Build the static version first. It is your loading state, your reduced-motion experience and your fallback when the GPU says no.
- Tier by capability, then adapt at runtime. A viewport and memory check gets you most of the way; a frame-rate monitor covers the rest.
- Keep heavy work off the load path. Defer, stream and warm up after the visitor can already read and act.
You can see the result in the space explorer. If you are weighing heavy visuals against performance in your own product, talk to the team.
Sources
- NASA NSSDC Planetary Fact Sheet (opens in a new tab)
- JPL Solar System Dynamics: planetary physical parameters (opens in a new tab)
- JPL Solar System Dynamics: satellite physical parameters (opens in a new tab)
- NASA Exoplanet Archive (IPAC/Caltech) (opens in a new tab)
- NASA Scientific Visualization Studio: CGI Moon Kit (LROC colour, LOLA elevation) (opens in a new tab)
- Solar System Scope planet textures, CC BY 4.0 (opens in a new tab)
- CDS hips2fits service (moon mosaics) (opens in a new tab)
- Yale Bright Star Catalogue, 5th revised edition (CDS V/50) (opens in a new tab)
- ESO/S. Brunier, The Milky Way panorama (eso0932a) (opens in a new tab)
- Hillaire 2020, A Scalable and Production Ready Sky and Atmosphere Rendering Technique (opens in a new tab)
- web.dev: Largest Contentful Paint (opens in a new tab)
Questions
Can a WebGL scene coexist with good Core Web Vitals?
Yes, if nothing 3D sits on the load path. A static preview image is the Largest Contentful Paint element, and the WebGL scene mounts only after the page has loaded and the browser is idle, so the headline appears before any shader compiles.
Where does the data in the solar system come from?
Each fact is a typed record with a label, a value and a link to a primary source such as NASA NSSDC fact sheets, JPL Solar System Dynamics or the NASA Exoplanet Archive. Volatile numbers like moon counts carry an as-of date, and exoplanet views are labelled as artist's concepts.
How does the scene stay smooth on a mid-range phone?
It picks one of three quality tiers at runtime based on screen size, device memory and CPU cores, with smaller textures and no post-processing on the reduced tier. A frame-rate monitor then lowers the render scale and switches effects off if the device still struggles.
What does this have to do with client work?
The same trade-offs apply to any product with heavy visuals, such as maps, 3D product configurators, medical imaging or dense dashboards: sourced data, honest labelling, per-device budgets and never blocking the first paint.