Liveness THE AUTONOMOUS VARIANT

On

On Liveness

A note on the autonomous variant.


The static creatures in the catalogue are pure functions. You move a slider; a body appears. You let go; the body stays. Nothing in the system has any state of its own.

The living variant — currently creatures/jellyfish-live.html — is the same creature, equipped with the minimum machinery required to walk its own morphospace. No external input. No sliders. The jellyfish becomes an agent, drifting through its own parameter manifold under the discipline of a finite state machine and a synthesized fitness landscape.

This document explains what was added and why.


The architecture

Three layers, cleanly separated.

Layer 1 — The parametric formula (unchanged)

The vertex shader is identical to the static jellyfish. Five parameters (bell curvature, tentacle count, tentacle length, pulsation frequency, flexibility) compute a body. This layer doesn't care whether the parameters come from sliders, a state machine, a learned policy, or a coin flip.

Layer 2 — The agent (new)

A small JavaScript object that holds:

Each tick, the agent:

  1. Decrements energy by the current state's cost rate.
  2. Optionally picks a new target (each state has its own retarget cadence).
  3. Pulls current parameters toward the target with a state-specific leash strength, plus jitter.
  4. Considers transitioning to a new state based on conditions and a Poisson-style probability.

The agent writes its parameters into the shader uniforms every frame. The render layer doesn't know an agent exists.

Layer 3 — The fitness landscape (synthesized)

For the "climbing" state to mean anything, there has to be something to climb. We define a fitness function as a sum of Gaussian peaks in normalized parameter space, one peak per canonical jellyfish (moon, lion's mane, box, siphonophore). The climbing state computes the local fitness gradient via finite differences and steps in its direction. Other states use fitness in different ways (rest pulls toward the moon peak; exploring ignores fitness).

The fitness function is synthesized, not learned. This is the cheap version. The expensive version is a neural net trained on actual hydrodynamic simulations. The architecture supports either; only the function body changes.


The five behaviors

State Energy Retarget Pull Jitter Picks target by
Exploring -0.04/s every 3–7s 0.6 0.08 semi-random walks with occasional distant jumps
Climbing -0.10/s every 4–8s 1.0 0.02 local fitness gradient
Resting +0.18/s every 6–10s 0.25 0.012 gentle drift toward the moon-jelly peak
Mutating -0.20/s every 1.5–2.5s 1.8 0.18 sharp jumps to extreme corners
Dreaming -0.03/s every 8–14s 0.4 0.05 emphasizes pulse + flex over morphology

Transitions

Transitions are conditional and probabilistic. From any state, a possible next state requires the current state to have run for at least some minimum time, optionally requires energy to be above or below some threshold, and then fires with a Poisson-like probability per second.

Concretely, the structure of exploring's transitions is:

exploring → climbing  (after 4s, p ≈ 0.18/s, requires energy > 0.30)
exploring → resting   (immediately, p = 1.0,  requires energy < 0.15)
exploring → mutating  (after 8s, p ≈ 0.04/s)
exploring → dreaming  (after 15s, p ≈ 0.08/s)

The "requires energy < 0.15" transitions are gates — a starving creature must rest. The other transitions are tendencies — a creature might try to climb if it's been exploring a while and has the energy.


The trace

A row of small colored dots below the state panel shows the last ~24 states the creature has been in. Each dot is the color of that state at the moment it was recorded. Recent dots are bright; older dots fade.

This is not for the creature's benefit. It's for yours — it makes the agent's lived history visible at a glance. If you see a long blue streak (exploring) followed by sudden orange (mutating) followed by purple (resting), you've watched a behavior arc unfold.


What this is and isn't

This is: a state-machine-driven agent, walking a parametric morphospace under an energy budget, with a hand-coded fitness landscape.

This is not: a learned policy, a real fitness simulator, a population, or anything biological in a literal sense. The creature does not "want" anything; it doesn't know what a jellyfish is; it cannot reproduce. It is a small, well-behaved automaton in a five-dimensional space.

The point is to demonstrate the shape of liveness, not to simulate biology. The architecture cleanly accepts upgrades:


Why a state machine and not just a learned policy

Because state machines are legible. The labels mean something. A user watching the creature can tell what it's doing and why. "It's resting because energy got low." "It's mutating, watch it jump." A learned policy is opaque by comparison; you'd see the creature move but not know why.

The right move is probably both — a learned policy that emits state labels as a discrete head alongside its continuous action. The state machine becomes interpretation, not control. But that requires a learned policy, which requires training, which requires a real fitness function, which requires a real simulator. We're nowhere near that yet. The state machine is the cheap version that already works.


What's next

The natural next steps, in roughly increasing scope:

  1. Living variants for the other creatures. Fireeel, quadruped, and wing each have parameter spaces; each can walk its own. The fitness landscapes differ — the wing's is aerodynamic, the quadruped's is biomechanical — but the agent architecture is shared.

  2. Memory. The creature could remember the highest-fitness location it's visited and bias toward returning. This is one variable plus one transition; gives the creature a sense of the past.

  3. Population. Done. See creatures/aquarium.html, below.

  4. Real fitness. Replace the synthesized Gaussian peaks with a small neural surrogate trained on real physics. The creature is then actually optimizing, not just walking toward hand-placed targets.

  5. Learned policy. Replace the state machine. The behaviors emerge instead of being scripted. This is the largest jump and only worth it once 1–4 are in place.

Each step earns its keep on the previous one.


The Aquarium

creatures/aquarium.html — a population of jellyfish in a shared scene, each running its own FSM, optionally directed by a small in-browser LLM.

This is what the liveness doc set up. Each individual creature is the jellyfish-live agent. The aquarium is N of them at once, plus a coordination layer.

ECS-light architecture

The architecture is recognizably Entity-Component-System, sized down to the problem:

Each creature gets its own THREE.ShaderMaterial with per-instance uniforms (uOrigin, uScale) — same vertex math as the single jellyfish, just positioned at the creature's world location. Shared geometry attributes (point indices, kind flags) keep this from being wasteful. Point count per creature is 8,000; with 6–12 creatures on screen, total point budget is ~50K–100K, well within GPU comfort.

The Director

The LLM doesn't drive the simulation. It directs it. Three roles:

  1. Naming & introduction. Each creature gets a seed name from a fixed list (Aether, Briar, Cyra…). The Director writes a single observational sentence introducing the population on awakening.
  2. Goal-setting. Every ~22 seconds, the Director reviews each creature's state, energy, fitness, and goal, then optionally assigns up to two creatures a new "peak region" to climb toward. The FSM still executes the climb; the goal just tells it where.
  3. Narration. Each pass also emits one short observational sentence. The ticker at the bottom of the panel shows what the aquarium is doing in language.

A small chat input lets the user address the aquarium directly. The Director can both narrate in response and (optionally) emit a goals block that redirects a creature.

Model choice

Qwen3 0.6B (q4f16_1) loaded via WebLLM.

The Director architecture is model-agnostic. The integration is a single chatOnce(systemPrompt, userPrompt, opts) adapter. Swapping in a different model is changing one line. Specifically intended for the moment Bonsai 1.7B (PrismML's 1-bit family) ships with a npm-publishable browser runtime — currently their llama.cpp fork isn't on npm, and stock wllama doesn't have the Q1_0 kernels, so it's not a five-minute integration today. The hook is there.

Why infrequent inference

The state machine runs every frame; the Director runs every 22 seconds.

A 0.5B model produces tokens at ~70 tok/s under WebGPU. A reasonable Director response is ~80 tokens. That's ~1.1 seconds of compute. Doing it every frame is impossible. Doing it every few seconds is fine. Doing it every 20+ seconds is cheap.

The right framing isn't "the LLM controls the creature." It's: the FSM is the body, the LLM is the will. The body acts at body speed. The will acts at thought speed. Both run on their own clocks.

Graceful degradation

If WebGPU isn't available, the Director load fails fast and the page tells the user. The aquarium continues running on FSM alone — exactly the same experience as N copies of jellyfish-live swimming around. The Director adds personality, names, and narration; it doesn't add motion. The motion was always there.

This is the right separation. The simulation is the foundation. The model is the garnish.


Extensions

The aquarium's initial form was the FSM-per-creature population plus the Director. Six extensions earn their keep on that foundation.

Per-creature memory

Each creature now carries a bestFitness, bestParams, and bestSeenAt. The memory system runs every frame, evaluates effective fitness at the creature's current position, and updates the trio whenever a new high is found.

The memory shows up in two places. The Director's population summary includes best alongside current fitness, so the model can reason about peaks already visited not just current state. And the climbing state has a 35% chance of targeting bestParams instead of the local gradient when bestFitness > 0.4. Translation: a creature that has already touched a peak will tend to return toward it rather than always pursuing the steepest local ascent. This is a tiny version of the explore/exploit trade-off — explore via gradient, exploit via memory.

It also makes the lineage interesting: when a creature reproduces, its offspring's starting parameters are seeded from the parent's best-known params, not its current ones. The parent's memory becomes the child's inheritance.

Inter-creature awareness (schooling)

Creatures now feel each other. Every 0.5 seconds, each creature recomputes its nearest neighbors in morphospace (not world space) within a radius of 0.35 normalized units, capped at the 3 closest.

The pull is then applied in world space, weighted by morphospace proximity: creatures that are similar gather toward each other; dissimilar ones don't. There's a soft world-space repulsion at very close distances to prevent overlap.

The result is herding. Jellyfish with similar bell shapes drift toward each other. When one of them mutates, it briefly pulls away from its school. When two climbing creatures converge on the same peak, they end up adjacent in both morphospace and world space at once.

In the aviary, the schooling system also aligns headings (so wings genuinely fly in formation, not just adjacent positions). In the pasture, schooling is 1D along the ground line.

Spawning and dying as natural processes

The Spawn / Cull buttons still exist, but the population now manages itself.

Each creature has a lifespan randomized between roughly 10 and 20 minutes of subjective time. There's also an energy-exhaustion path: a creature older than 30 seconds that has been below 4% energy and isn't resting has a small chance per second of dying. The exhaustion route is rare in practice — the FSM is good at sending low-energy creatures into resting — but it's possible.

Death is a 2-second fade-out (uAlpha driven to zero via the shader), then removal. Spawning is symmetric: new creatures fade in over 1.5 seconds.

Reproduction is a lifecycle event, not a button. When the living population is below the target maximum and a cooldown of ~14 seconds since the last birth has elapsed, the system looks for a parent: at least 20 seconds old, energy above 0.5, and current fitness above 0.6. If one is found, a child is born nearby with parameters seeded from the parent's bestParams (the memory inheritance above) plus a 12% range mutation. The parent pays a 0.35 energy cost.

The population thus oscillates around the target band, with deaths creating openings and the fittest creatures producing offspring to fill them. It's a soft evolutionary loop — not strict selection, but biased reproduction toward fit lineages.

A real fitness model (surrogate)

The original fitness function was four Gaussian peaks placed by hand. That's a useful pedagogical landscape but it isn't physics.

The aquarium now ships an architectural seam for a learned surrogate: a small 2-layer MLP (5 → 16 → 1, tanh hidden, sigmoid output, 113 total params) that can be trained in-browser at the user's click. The training data is sampled from the Gaussian field itself — which is honestly framed in the UI as illustrative, not realistic. The point isn't that the surrogate adds intelligence; the point is that the swap mechanism works.

When the user clicks "Train & Switch to Surrogate", the aquarium trains the MLP for 200 epochs on 800 samples (manual backprop, no autodiff library), reports the final MSE, and from that point forward all fitness queries — climbing gradients, reproduction eligibility, memory updates, Director summaries — route through the surrogate instead of the analytic function.

A real version of this would train on CFD output for the aquatic family, biomechanics simulations for the pasture, or aerodynamic computations for the aviary. The interface is the same. The architecture is ready for it. What's missing is the dataset — which is a research project, not a frontend project.

Bonsai 1.7B integration (not yet)

The Director interface is fully model-agnostic: a single chatOnce(systemPrompt, userPrompt, opts) function with a model-specific adapter behind it. The current adapter is WebLLM/Qwen3 0.6B. The intended drop-in is Bonsai 1.7B from PrismML.

Why it's not live today. PrismML's 1-bit Bonsai requires their fork of llama.cpp to decode the Q1_0 quantization format. The fork isn't published to npm. Their Ternary Bonsai family (1.58-bit) is currently MLX-first — native Apple Silicon, but not portable to a Chromium/Edge tab via WebGPU yet. Stock wllama (the WASM port of llama.cpp) doesn't have the Q1_0 kernels either. The vanilla browser path to Bonsai therefore doesn't exist as of May 2026.

What exists. The official Bonsai 1.7B demo at ternarybonsai.click runs in a Hugging Face Space using PrismML's separately-compiled WebGPU build. It works. It just isn't installable from a CDN with one import line. The integration cost today is a custom build pipeline; the integration cost the day PrismML publishes their runtime to npm is one import statement and one model ID.

What's ready. When the runtime ships:

  1. Add Bonsai's loader to a new branch in the model selector
  2. Point the Director at Bonsai-1.7B-Q1_0 (or whatever the published handle is)
  3. The prompts, the JSON contract, the cadence — all stay identical

The seam is deliberate. We're not waiting on Bonsai to add agency to the aquarium; the aquarium has agency already. We're waiting on Bonsai to replace the current model with a smaller, cheaper, more energy-efficient one. That's a swap, not a redesign.

The Aviary and the Pasture

Each is a new page that proves the architecture is family-agnostic.

Aviary (creatures/aviary.html) — a flock of wings with violet/lavender accents. Wings glide in 2D with heading, banking, and gliding physics. The schooling system aligns headings, not just positions. Peaks are albatross (high AR, low flap), falcon (medium AR, swept, fast flap), owl (low AR, elliptical, slow), and swift (medium AR, heavily swept). Toroidal wraparound at the edges — wings fly off one side and appear on the other. Six initial creatures, population cap 10.

Pasture (creatures/pasture.html) — a herd of quadrupeds with earthy umber accents. Quadrupeds walk along a horizontal ground line with a facing direction; speed scales with state (resting moves slowly, climbing trots faster, mutating slows down). Schooling is 1D along the ground. Random turnaround events ~0.3% per tick keep the herd from drifting all to one side. Peaks are trotter (balanced), cheetah (long-legged, springy), bear (heavy, low gait duty), and dachshund (long body, short legs). Five initial creatures, population cap 8.

Both pages share the same ECS structure as the aquarium: physicsSystem, fsmSystem, memorySystem, schoolingSystem, lifecycleSystem, renderSystem, in that order, plus the Director called every ~22 seconds. The Director prompts are adapted ("aviary of wings", "pasture of quadrupeds", "atmospheric" / "earthy" tone). The naming pools differ — Cirrus and Kestrel in the aviary, Aspen and Loam in the pasture. Everything else is the architecture proving it generalizes.


What's actually next now

With these six items in place, the natural next steps shift again:

The bones are right. The skin is now thick enough to feel like flesh. The remaining work is research, not architecture.


Vivarium · Volume I · MMXXVI