Repo · Live app
Emergence lab
Many agents following simple local rules exhibit emergent behaviour, seen in everything from ecosystems to economies.
A test project for multi-agent orchestration across Claude and Codex using the Autometta repo. The higher tier models plan and orchestrate while lower tiers code and verify with clear boundaries. Cross-family orchestration improves outcomes and catches failure modes that a single model misses, at more than twice the cost...
View the research: Emergent behaviour from nature to management theory.

The twenty
Twenty deterministic kernels behind one renderer. Each is a different discipline's way of pointing at the same thing: local rules, global form.
- Gray-Scott reaction diffusion. Chemistry's version of the question.
- Abelian sandpile. Self-organised criticality in one toy.
- 2D Ising model. A magnetic lattice assembling order at a phase transition.
- Kuramoto oscillators. Different rhythms crossing into collective synchrony.
- Conway's Game of Life. The original, and still the cleanest.
- Belousov-Zhabotinsky waves. The reaction that taught chemistry about excitable media.
- Boids. Flocking from three local rules.
- Particle life. A handful of attraction rules, and cells assemble themselves.
- Physarum. Slime-mould agents laying pheromone trails into networks.
- Strange attractors (Lorenz, Rössler, Thomas, Aizawa, Halvorsen — five separate cards). Where deterministic equations stop being predictable.
- Diffusion-limited aggregation. How dendrites and lightning agree.
- Elementary cellular automata. Wolfram's one-dimensional zoo.
- Brian's Brain. Three states. Somehow it breathes.
- Cyclic cellular automata. Rock-paper-scissors that spirals into itself.
- Lenia. Continuous cellular automata, and life gets smooth gliders.
- Mandelbrot, Julia, Burning Ship. Iterated maps as the geometry of feedback.
- Logistic Mandelbrot. The bifurcation cascade hung off the Mandelbrot set as a curtain.
- Clifford-DeJong. Clifford, De Jong, and Svensson point-map attractors, with live coefficient drift.
Gray-Scott is the priority kernel. The others are calibrated and held.
Run it
npm install
npm run dev
Open the URL Vite prints, normally http://localhost:5173/.
Checks:
npm run verify # types, kernel tests, production build
npm test
npm run build
Publish into the site
The lab's own standalone deploy serves from the root path (/) and is
unaffected by the steps below. To vendor a build into a host site instead, at
/labs/app/:
npm run publish:site
This publishes four artifacts (scripts/publish-site.sh), each rsynced with
-a --delete so stale files from a previous build are removed:
dist/→public/labs/app/— the static app (Vite base/labs/app/), standalone and fullscreen targetdist-lib/→public/labs/lib/— the library build (vite.lib.config.ts, entrysrc/app/lib.ts): the<emergence-lab-sim>web component plusmountLab, which the site's /labs/run shell imports by URL and mounts inline- KaTeX css + fonts →
public/labs/app/katex/— stable unhashed path the web component links into its shadow root (class rules) and the host document (font faces) - registry manifest →
src/vendor/emergence-lab/registry.json— lets the site's build fail on slug drift instead of shipping dead deep links
The promo-flow path defaults to a sibling checkout; override it with
PROMO_FLOW_DIR if promo-flow lives elsewhere:
PROMO_FLOW_DIR=/path/to/promo-flow npm run publish:site
Nothing is committed or pushed on the promo-flow side; that repository commits its own copy of the vendored build.
Baking a local point cloud
The logistic-Mandelbrot orbit3d view builds its point cloud in the browser, time-sliced and capped so it stays responsive on whatever device it lands on. That ceiling is the honest one for the web: the deployed app cannot assume a desktop GPU, and a cloud dense enough to resolve the cascade tail runs to hundreds of megabytes — the current machine-local bakes are 10 MB to 650 MB. Nobody should be asked to download that over a network, so it never ships.
The active route is exposed on the simulation canvas as
data-orbit3d-sampler (the DOM form of canvas.dataset.orbit3dSampler):
| Path | When selected | data-orbit3d-sampler |
data-orbit3d-boundary-detail |
|---|---|---|---|
| GPU sampled | WebGL2 float targets and the orbit sampler both complete | gpu-sampled |
active when Boundary detail is raised; otherwise off |
| CPU sampled | GPU sampling is unavailable or fails; the time-sliced sweep completes | cpu-sampled-gpu-failed |
degraded when requested; the normal Tail refinement plan is used |
| Prebaked ELPC | A valid requested local bake finishes loading and supersedes the live build | prebaked |
off; Boundary detail applies only to live builds |
| 2D field | orbit3d setup or both live cloud builders fail | orbit3d-fallback-field |
degraded when requested; otherwise off |
For a 3D cloud, data-orbit3d-build="complete" and a positive
data-orbit3d-points confirm that the selected path produced a whole cloud.
The 2D fallback intentionally has neither attribute.
Boundary detail is an opt-in GPU-only live-build control: at maximum it raises
the point budget from the unchanged 9.6M extreme ceiling to 16M and spends the
additional capacity on a 5x5 boundary sub-grid warmed for 20,000 iterations.
That depth is deliberate: stage 35 measured boundary-band period mismatch
falling from 1.71% at 1,500 warmup iterations to 0.25% at 20,000.
Tail refinement defaults to its maximum, 0.6, on the GPU path. There, 0 turns
tail refinement off. The CPU fallback keeps its bounded automatic policy: 0
selects its 0.3 share, positive settings up to 0.3 are honoured, and every higher
setting is capped at 0.3.
Instead the cloud can be baked offline, on the machine that will view it, with no time-slicing, a much higher warmup, and a second refinement level the browser can't afford:
npm run build:test # the baker reuses the compiled kernel
node scripts/bake-orbit3d.mjs --points 50e6 --warmup 30000 --out public/baked/lm-50M.elpc
Flags: --points (target total), --samples (orbit window per cell,
default 64), --warmup (default 20000), --refine-fraction (default 0.35),
--out (default public/baked/logistic-mandelbrot.elpc).
Total points ≈ cells × samples, so at a fixed --points budget raising
--samples buys vertical density in the attractor at the cost of resolution in
the c plane. Periodic cells only have p distinct heights however many
samples are taken; the chaotic bands are where extra samples show.
The live build and a bake lay their points out differently. Since card 103
the live build packs each cell's distinct points once: a period-p cell
occupies min(p, samples) rows of a slot, periodic cells share slots, and
Plotted iterations hides rows in the shader, so the base grid covers the whole
candidate pool at every Tail refinement setting and
data-orbit3d-layout="packed" is reported. A bake stays stacked (every cell
stores all of its samples, data-orbit3d-layout="stacked"), and the renderer
draws it exactly as before: the same point count, the same Plotted iterations
prefix and the same brightness, with the stacked energy factor applied in the
shader.
Each run writes the quantized .elpc binary and merges an entry into
public/baked/index.json. The app fetches that manifest at mount and turns it
into the Model source dropdown in the View controls — live plus one
option per bake. With no manifest the control is not rendered at all, and the
fetch is skipped outright unless the page is served from a local host, so the
published site never asks for a file that cannot exist there.
public/baked/ is git-ignored and excluded from the publish rsync
(scripts/publish-site.sh), so bakes stay on the machine that made them.
Stack
Vite and TypeScript throughout. Kernels are pure deterministic numerics with no runtime dependencies. The renderer uses a quality-first WebGL2/GPU path, including direct fragment-shader fractals, with Canvas 2D kept as a fallback and debug surface. Legacy browsers are not a target.
Adding a simulation
- Extend the
SimKernelcontract indocs/INTERFACE.mdif the new sim needs it. - Add the kernel under
src/sims/<name>/and wire the gallery insrc/app/**. - Write the essay in
essays/<name>.md.
There is no fixed split of the codebase by model — any agent may work any
part of it. Multi-agent work runs through autometta (a worker/verifier loop)
when parallel work and cross-checking are wanted. The kernel contract in
docs/INTERFACE.md stays a reviewed boundary. See
MODELS.md.
Repository layout
emergence-lab/
src/ # kernels, renderer, controls, gallery
sims/<name>/kernel.ts # deterministic kernel
app/ # renderer, gallery, controls, presets
essays/ # one .md per sim
scripts/ # publish, deploy, thumbnail and point-cloud baking
public/baked/ # machine-local .elpc bakes + index.json (git-ignored)
docs/
INTERFACE.md # the SimKernel contract
PUBLISH-WORKFLOW.md # publish-safety hooks and remotes
state/handoffs/ # autometta worker completion envelopes
Version history
| Version | Date | Summary |
|---|---|---|
| v0.2.0 | 2026-07-20 | Immersive fullscreen, web-component build + site publish pipeline |
| v0.1.0 | 2026-07-20 | Initial public mirror: 19 kernels behind one renderer |