- Rust 72.7%
- JavaScript 24.5%
- HTML 1.7%
- CSS 1.1%
The mountain rendering was broken in two places:
1. Vertex positions never updated after scenario load. buildMesh()
read planet.positions() once (which baked in elevation displacement
from initial terrain), then never refreshed. Plate tectonics could
raise mountains in the data, but the mesh stayed flat — colours
updated each frame but vertices didn't move.
2. Vertex normals never recomputed. Even if positions had updated,
computeVertexNormals() only fired at mesh build, so lighting
wouldn't reflect the new shape.
Fixes:
- WASM: write_positions(buf: &mut [f32]) — zero-allocation positions
writer paired with the existing colour writers.
- Frontend: pullColors() became pullState() — refreshes colours every
frame, refreshes positions and recomputes normals only when geology
has changed (planet.time_myr() advanced) or sea level was just
dragged. Position refresh is the expensive part (~5ms at depth 5
including normal recomputation), so we skip it on frames where
nothing geological happened.
Also:
- Displacement scale changed from 30,000 m → 12,000 m per unit of
sphere radius. At 30K, an 8 km mountain showed as 27% of radius —
technically there but visually understated. At 12K, the same
mountain shows as 67% — clearly mountainous. Earth's actual ratio
is ~0.14% (mountains are essentially invisible from orbit), so
we're exaggerating by ~10×, which is the right move for a planet
you're meant to read at a glance.
- Ocean cells now sit on the reference sphere regardless of their
underwater bathymetry — the rendered ocean surface looks flat,
matching real-water visual conventions. Sub-sea-level land
(continental basins below sea level) sinks correctly.
- Sea-level slider now flushes lastGeoTime so position refresh
happens on the next frame — drag the slider and watch the ocean
surface rise/fall in real time.
Out of scope for this fix:
- The mesh still renders the icosahedron triangulation, not the
Goldberg dual hex tiles. Cells *are* triangle vertices, so
mountains spike at vertex points and the triangles between cells
create "tent" geometry. Rendering the dual mesh would be cleaner
but is its own substantial change.
Tests: 54, all green (no test surface changes).
Authored by Rowan Valle; Executed by Claude Code
By Symbiont Systems LLC
|
||
|---|---|---|
| metabolic-core | ||
| metabolic-wasm | ||
| research | ||
| web | ||
| .gitignore | ||
| ARCHITECTURE.md | ||
| Cargo.toml | ||
| CLAUDE.md | ||
| README.md | ||
Metabolic
A coupled-subsystems planetary simulator. The biological-systems sibling to Forms (mathematics) and Elemental (chemistry).
The successor question to Lovelock's SimEarth (1990): given thirty-six years of compute, biogeochemistry, microbial ecology, and Earth-systems modelling, what does a planetary simulator look like today?
Approach
Forms is a generative engine because mathematics is generative. Elemental is an atlas because the periodic table is finite. Biology is neither — it is process: coupled, multi-scale, historical, relational. Metabolic is therefore a simulator you inhabit, not a catalog you browse or an expression you evaluate.
The engine is a pure Rust library; visualisation and interaction layers
attach later via WASM, the same pattern Forms uses for math-core →
math-wasm → browser.
The Hello World
metabolic-core ships with one scenario: Daisyworld
(Watson & Lovelock 1983).
Two daisies, six equations, one feedback loop. As solar luminosity ramps from
0.6 to 1.6 over geological time, the planet's effective temperature is held
inside the daisy growth window — without intent, without coordination, by
selection alone.
If Daisyworld self-regulates here, the engine philosophy is right.
Author
Authored by Rowan Valle. By Symbiont Systems LLC.