Clage evolves neural agents in a 2D ecosystem where organisms move, eat and reproduce. Its from-scratch NEAT (NeuroEvolution of Augmenting Topologies) engine evolves both network structure and weights, without an ML framework.
Clage Studio 1.0 is an interactive browser workbench for watching that ecosystem, inspecting the networks behind each action, tracing evolutionary lineage, and running reproducible experiments. Live Canvas visualization, evolution history, analysis tools and deterministic replay make the simulation inspectable from individual neurons to whole populations. It runs locally with no cloud account, API key or frontend build. The Python engine, seeded experiments and terminal/matplotlib viewers also work independently.
python3 -m venv .venv
.venv/bin/pip install -e ".[studio]"
.venv/bin/python -m studio
# Open http://127.0.0.1:8765 — starts paused. Click Resume.- Ecosystem: zoom/pan/follow, six layers, direction markers, event pulses, live statistics, charts and distributions. Simulation ticks and render FPS are separate.
- Neural atlas: actual pre-action inputs/activations, weights, biases, enabled genes, semantic labels, node/edge inspection and side-by-side genome comparison.
- Evolution: evaluated generation summaries, champions, recorded evolutionary parents/net mutation deltas, and a separate within-world organism lineage.
- Laboratory: frozen validated settings, browser presets, seeded held-out policy evaluation and persistent local artifacts.
- Replay/export: bounded JSON/gzip archives, seeking, generation navigation, synchronized second-replay view, metrics CSV, chart/world PNG and champion JSON.
Studio 1.0 was merged into main through PR #1,
with the reconciled 0.4.1 engine. The release supports one active run and bounded
recording (at most 600 frames / 16 MiB encoded JSON). Replay supports deterministic
reruns from configuration and seed; it is not a resumable checkpoint.
For controls, experiments and exports, see the Studio guide.
The completed release passed 429 Python tests, 4 JavaScript tests, and 21 Playwright workflows, with Ruff, mypy and main-branch CI passing.
Controlled engine validation used the documented unchanged budget: 100 founders,
seeds 0–4, 300 generations, and minimal initialization (minimal-v1).
| Problem | Solved | Generations to solve, seed order |
|---|---|---|
| OR | 5/5 | 9, 6, 5, 6, 8 |
| AND | 5/5 | 6, 8, 7, 8, 17 |
| XOR | 5/5 | 212, 125, 68, 143, 102 |
| Sine | 0/5 | not solved in 300 (mean best training fitness 0.9195) |
These results validate the engine on the specified tasks and seeds; they do not
establish generalizable learning, learned foraging, cooperation or intelligence.
Studio defaults to dense-random initialization (dense-random-v1), so these
minimal-initialization benchmarks are not measurements of Studio's default
ecosystem. The merge verification
records the setup and outcomes; the historical pre-Studio results below remain separate.
The following tables and interpretations are preserved from the pre-Studio
README at 2105fee (five seeds each). These tables retain their original
pre-Studio measurements; current-source engine validation is recorded separately
in the final release review. The environmental experiments were not rerun during
Studio development. These are not results of the new dense-random Studio
initialization. Keep their original configurations and definitions separate.
Engine validation (python -m benchmarks.run --problems or,and,xor,sin --trials 5 --generations 300):
| Problem | Solved (of 5) | Generations to solve (min / median / max) |
|---|---|---|
| OR | 5 | 7 / 13 / 13 |
| AND | 5 | 19 / 27 / 105 |
| XOR | 4 | 147 / 184.5 / 243 |
| sin | 0 | not solved in 300 (mean best fitness 0.91) |
Environmental experiment (food_abundance.json: 20 organisms, 25 generations,
300 ticks per generation; final-generation values, mean ± std over 5 seeds):
| Condition | Food target | Best fitness | Food eaten (all organisms) | Offspring born in-world | Champion connections |
|---|---|---|---|---|---|
| food_low | 20 | 9.17 ± 2.83 | 10.8 ± 5.3 | 30.4 ± 5.1 | 1.0 ± 1.7 |
| control | 60 | 14.60 ± 4.54 | 31.4 ± 9.2 | 48.2 ± 8.8 | 1.6 ± 1.5 |
| food_high | 120 | 23.76 ± 7.02 | 145.4 ± 138.7 | 144.8 ± 110.5 | 4.2 ± 2.9 |
Left: best fitness per generation (mean ± spread over 5 seeds). Right: a recorded
generation replayed with python -m visual replay --export-tick 40; colour = genome,
tick marks = facing.
What this does and does not show:
- These historical aggregates describe environmental differences, not a proven cross-seed separation or evidence of learned foraging.
- It is not a learning curve. Each generation reseeds the world, and over the shipped 25 generations these plots do not establish generalizable learning. The earlier unarchived 100-generation anecdote is not reproducible evidence.
- Evolved networks stay small (the champion has a few connections), and in every condition almost no organism survives the full 300 ticks.
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]" # original engine/viewers; Studio is optional
.venv/bin/pytest # optional Studio API tests skip without its extras
.venv/bin/pytest --cov=neat --cov=world --cov=diversity --cov=experiments # optional coverageRequires Python 3.10+. CI runs Ruff, mypy and Python tests on Python 3.10/3.12, plus JavaScript and Playwright tests in a separate Chromium job. The release review and merge verification document installation checks, deterministic reruns, screenshots and known limitations. The CI badge above links to the latest remote status.
To run all checks, install the development and Studio extras in the virtual environment, then activate it:
.venv/bin/pip install -e ".[dev,studio]"
source .venv/bin/activate
python -m pytest -o addopts='' -q
python -m ruff check .
python -m mypy neat world diversity experiments benchmarks visual studio
npm ci
npx playwright install chromium
npm test
npm run test:e2eNode is needed only for frontend testing, not for running Studio. Local validation used Python 3.13.6 and headless Chromium on macOS; see the release reviews for coverage and portability limitations. The engine/viewers declare matplotlib as their runtime dependency. The optional Studio extra also installs FastAPI, Uvicorn, Pydantic, websockets and httpx.
Note: the packages are installed as top-level modules (neat, world, ...), so use
a dedicated virtual environment. neat would clash with neat-python.
Validate the engine on the controlled benchmarks:
python -m benchmarks.run --problems or,and,xor,sin --trials 5 --generations 300
# writes results/benchmarks/validation_report.md and benchmarks/plots/*.pngRun an environmental experiment (one factor changed at a time, control included, 5 seeds each):
python -m experiments.run --config experiments/configs/food_abundance.json \
--out results --record-generation 9 # optionally omit recordingRaw results are overwrite-protected. Use a fresh output directory for every rerun
(for example, --out results-second-run); do not rerun into the same experiment
directory or delete historical artifacts just to reuse its path.
Configs shipped: base.json, food_abundance, food_regeneration,
population_density, available_space, reproduction_cost. Each condition
changes exactly one parameter (registry in experiments/config.py).
Analyze and compare conditions:
python -m experiments.analyze --results results/food_abundance \
--report results/food_abundance/report.mdVisualize:
python -m visual tui --recording results/food_abundance/recordings/food_high/0.json
python -m visual tui --recording <file> --export-tick 150 --export-out frame.txt
python -m visual analytics --results results/food_abundance # interactive
python -m visual analytics --results results/food_abundance --export results/plots # PNGs
python -m visual replay --recording <file> # animated window
python -m visual replay --recording <file> --export-tick 40 --export-out tick.png # one frame
python -m visual network --recording <file> --organism 3 --export net.pngtui is a boxed terminal replay (world on the left, selected-organism info +
an ASCII neural network on the right, generation/population/species/survival/
average-fitness in the footer). Controls: arrows/WASD move a cursor, Enter
selects the organism under it (opening its network panel), Space toggles
play/pause, q quits. The matplotlib replay window has a tick slider,
play/pause/step, and click-to-inspect.
Evolve against the world (shared world, one run per generation):
from neat import Population
from world import EnvironmentConfig, make_evaluator
config = EnvironmentConfig(width=20, height=20, ticks=200, initial_food=50, food_target=50)
pop = Population(
fitness_fn=None,
evaluator=make_evaluator(config),
population_size=30,
input_ids=list(range(9)), # 9 observations
output_ids=[10, 11, 12, 13], # 4 actions
seed=0,
)
pop.run(10)
print(pop.statistics[-1]) # best/mean fitness, species count
print(pop.best_genome) # champion genomeEvolve against a plain fitness function (no world):
from neat import Population
def fitness(genome, generation):
return float(len(genome.connections))
pop = Population(fitness, population_size=50, seed=0,
input_ids=list(range(9)), output_ids=[10, 11, 12, 13])
pop.run(20)Decode a genome into a network and run it:
from neat import Network
net = Network(pop.best_genome)
outputs = net.activate([0.5, -0.2, 0.1, 0.0, 0.9, 0.3, 0.2, 1.0, 0.0]) # 9 obs -> 4 outputsRun one world generation directly:
from world import run_generation
population_genomes = pop.population
organisms = run_generation(population_genomes, config, generation=3)
for org in organisms:
org.x, org.y, org.energy, org.food_eaten, org.age, org.offspring, org.aliveEngine 0.4.1 semantically reconciles the recovered Work audit with Studio. Its
corrected evolution is neat-reconciled-v1; historical results are not rewritten.
Read correctness notes and
reconciliation evidence for provenance, limits,
and unchanged-budget benchmark results. Old replays remain viewable; rerunning
them requires the original scientific source, not the newly corrected engine.
- Randomness is explicit. The engine rng comes from
seed; the world rng is derived per(trial seed, generation)viaEnvironmentConfig.world_rng_seed. Same seed ⇒ identical results. Different trials are independent. - Fitness is world-relative per generation. Each generation reseeds the
world, so
best_fitnessis a champion snapshot from one generation's world; onlybest_fitness >= current evaluated bestis guaranteed. Compare across seeds/conditions, not as a single absolute number. - Evaluation happens before reproduction. After
pop.run(1)returns,pop.populationalready holds the unevaluated offspring of the generation just finished, carrying copied or stale fitness that no longer describes their own structure. Read evaluated per-generation values frompop.statistics[-1]orpop.evaluated_best_genome(a fresh defensive copy on every read,Nonebefore the first generation) — never by scanningpop.population. - Measurement definitions.
transition_entropypools boundary-respecting transition counts across the organisms of a genome: only action pairs consecutive within one organism's trace are counted, normalized by the total number of within-trace transitions.food_alignmentpairs each displacement with the food direction observed immediately before the action that caused it, within a single trace, which excludes each trace's first recorded action (the position preceding it is never recorded). Results produced before this fix are not comparable fortransition_entropy,food_alignmentandbehavioral_diversity. - No hard-coded behaviors. Organisms move/turn/eat purely from observation → network → argmax action. Avoidance of other organisms is not even expressible (no directional organism sensor), and none of the diversity metrics claim cooperation, competition, aggression, or avoidance.
food_alignmentis excluded from automatic analytics charts — it is base-rate confounded by food density. View it only within a fixed food condition.- The engine is agnostic. It only sees genomes and
fitness_fn/evaluator. Thevisual/package contains no evolutionary logic; it consumes recorded JSON only.
More detail on the measurement fixes and the configuration validation is in docs/correctness-notes.md.
neat/ custom NEAT engine: genome, phenotype, innovation ledger, mutation,
crossover, speciation, population lifecycle
world/ 2D walled grid: organisms, food, energy, movement, death, in-world
reproduction, food regeneration
experiments/ config-driven one-factor-at-a-time environmental sweeps + analysis
diversity/ behavioral metrics (action entropy, transition entropy, coverage,
food alignment, behavioral diversity index)
visual/ pure-data viewer: world replay, condition-comparison analytics,
neural-network inspector (no evolutionary logic)
studio/ optional local API, incremental orchestration, replay contracts,
browser assets, held-out evaluation and reproduction tools
benchmarks/ NEAT validation suite (OR / AND / XOR / sin)
tests/ Python tests, browser workflows and pure-JS tests
docs/ correctness notes, Studio guide, screenshots and measured workloads
results/ experiment and benchmark output (gitignored)


