Autonomous Generative Studio - A-GEN
A local agent-driven evolutionary studio in which model agents propose bounded mutations to a seeded, frame-driven particle painting and a human selects which versions survive. Part artwork, part technical documentation of an evolvable image system.
- Year
- 2026
- Role
- Concept, system design, development
- Tools
- AI Agents, Evolutionary Systems, Generative Systems, three.js, Playwright
Updated 4 October 2026

A-GEN_01 · Evolutionary Studio · Technical Documentation
Technical images under human selection
A local agent driven studio in which a seeded, frame-driven particle painting is evolved by model agents and a human breeder.
Part I — The artistic concept
An evolutionary studio for technical images
1 · The apparatus and its program
A_GEN_01 is a particle painting. Tens of thousands of particles wander a field built from two noise functions — Perlin and curl — and deposit pigment on an accumulation buffer, frame after frame, until the painting is finished. Nothing in the picture is drawn by hand. Every mark is a consequence of a program: a seeded random generator, a fixed noise implementation, a configuration that names the field scales, the particle lives, the color cycle.
Vilém Flusser called images like this technical images: images produced by apparatuses rather than by hands. For Flusser, a photograph is not a window onto the world but a projection of a concept onto the world — the camera realizes possibilities that were latent in its program. A_GEN_01 makes that latency explicit. Its program is literally readable: config.json is the parameter surface of the apparatus, the code is its fixed part, and the seed is the throw of the dice that starts it. Where a photographer walks the world looking for scenes, this apparatus walks its own possibility space. Every seed, every field scale, every lifespan is an address in a landscape of possible paintings that exists before anyone looks at it.
2 · The artist as selector
An apparatus contains more possibilities than any player can visit. Flusser’s player is the one who probes the program, hunting for the improbable — for states the program contains but rarely shows. In this studio the exploration is shared. Model agents propose mutations — parameter shifts, focused code changes, open explorations — inside hard bounds. But they never judge. No fitness function ranks the children. The only aesthetic act in the system is selection: the artist looks at renders and chooses which versions survive as parents.
This puts the studio in an old lineage. Richard Dawkins’ biomorphs showed that a blind mutation walk becomes directed the moment an eye picks a branch. Karl Sims evolved virtual creatures by jury; William Latham grew sculptures by hand-picking mutations. In all of these, evolution is not automatic — it is a dialogue between a generator that can only enumerate and a selector that can only choose. The computer explores; the artist is the fitness function. The studio enforces this in code: an unselected version is not rejected, it simply waits; and nothing evolves without a new, confirmed human request. The machine proposes, the human disposes — and the proposing is bounded so that the disposing stays meaningful.
3 · Breeding: every child descends from both parents
One source produces variants of itself. But when the artist selects a second source, the pair is bred: every candidate of that round is then a hybrid of both parents, not a share of each. This is the rule that makes the crossing a real cross and not a collage.
Two kinds of hybrid exist, and the difference matters. A parameter hybrid is computed by the studio itself: the two genomes cross field by field, each field taken from one parent or the other. No model is called, no judgment is involved — the cross is arithmetic, and the provenance records exactly which field came from which parent. A code hybrid is synthesised by one worker that is given both sources, one of them read-only: two programs enter, one program leaves, and the child must carry the character of both. In both cases the parents are recorded, the inherited fields are listed, and a hybrid remains one seeded artwork — two sources can only be bred when they share one seed and one resolution, because the child replays from a single seed at frame zero.
Crossing is never automatic. It happens only where the artist selected two sources, the way a breeder chooses a pairing for traits already seen. The offspring is a descendant of both — a new point in the program’s possibility space that neither parent could reach alone.
The hybrid is therefore not just a visual mixture. It is a relation between two programs. One source contributes a way of moving, coloring, or accumulating; the other changes the field in which those behaviors become visible. The child is a new operation on the same seeded material, and its difference from each parent can be inspected rather than guessed from the image alone.
4 · The lineage as artwork
The studio’s real output is not any single painting. It is a lineage of technical images: a family tree in which every edge is a recorded decision and every card keeps its render, prompt, diff and failures. The graph is not a diagram placed around the artwork after the fact. It is a second surface of the work. It shows how visual form becomes history.
This is close to Gilbert Simondon’s idea that a technical object is understood through its becoming, not only through its finished shape. Each version carries traces of the conditions that produced it: the parent or parents, the selected render, the worker assignment, the actual diff, and the point at which the artist accepted or passed over it. The object is not isolated from its history. Its history is part of what it is.
The interface gives that history a readable form. Graph makes relations visible. Compare makes differences visible. Inspector makes causes visible. Together they turn an otherwise hidden program into a space the artist can inhabit and question.

5 · Determinism and the improbable
Every version is frozen: source, configuration, ordered textures, seed — hashed and kept. A render is a fixed number of frames computed from that frozen state, not a duration; a replay walks from frame zero to the recorded frame. The image is therefore a calculation that can, in principle, be repeated — the projection surface is documented along with the projected image.
The archive also keeps what did not survive. Failed versions sit in the graph beside the sealed ones, each stating its reason; a rejected submission and its repair remain inspectable as evidence. Flusser worried that technical images would become a memoryless flow. This studio’s answer is an append-only memory in which the improbable path through the program — including its dead ends — stays visible. The project is close to Sol LeWitt’s idea that a concept can become a machine that makes the art, but it keeps the machine open to human interruption. The program proposes; the artist chooses; the archive remembers both actions.
Part II — Technical documentation
How the studio works
1 · Three parts
2 · The artwork and its contract
The artwork is the existing three.js port of the Processing sketch A_GEN_01.pde. It is bit-exact against Processing at seed 42. The studio does not rewrite it; it drives it and mutates copies of it. Three rules make that safe:
- Seeded. One number (seed 42, inherited by every version unless the artist changes it explicitly) drives a 48-bit LCG and the noise functions. Same seed, same bytes, same painting.
- Frame-driven. The simulation advances by calling
step()and drawing once per frame. No wall-clock time enters the artistic state, so a render is defined by a frame count, not by a duration. - Config-driven. Everything that changes the look lives in
config.json. The studio imports those bytes, hashes them, and treats them as the genome.
3 · The genome
The genome is the config: six fixed sections plus a free custom namespace that code workers may add to. The studio knows which fields can change pixels and within which ranges — fields marked inactive exist in the config but do not move pixels today (for example canvas.background, which the renderer ignores). Parameter workers may only edit active fields.
| Section | Fields | Notes |
|---|---|---|
run | seed · frames · width · height · speed | seed and resolution are locked unless the artist changes them |
particles | amount · maxParticles · strokeWeight · opacity · speed · noiseOffset | amount is capped by maxParticles |
flowfield | perlinNoiseScale · curlNoiseScale · curlMix | the two noise fields and their mix |
life | baseLifespan · lifespanJitter · finalColorInfluence · initialColorInfluence | particle lifetime and color blend over life |
sequence | timer · amtTex · zoomAmount | texture swap cadence and zoom |
canvas | background · cap · hud | background inactive today; hud is a DOM overlay, never captured |
custom | data only | code workers may extend; the studio keeps it, warns on unknown fields |
4 · One round
A round is one confirmed request from the browser. It is frozen before anything runs: parents, the exact renders the artist saw, the verbatim prompt, the model, the mutation mix, the frame count and the budgets. An idempotency key makes retries safe — a refresh cannot start a second round or spend twice.

| Default | Value | Note |
|---|---|---|
candidates | 3 (max 9) | mix must total the candidate count |
mutation mix | 2 parameter · 1 code focused | code exploratory replaces focused for open directions |
parents | 1–2 | two sources are bred, see §5 |
frames | 100 (options 100 / 200 / 500) | an exact frame count beats a duration |
watchdog | 75 s | external, for stuck frames and stuck workers |
worker turns | 8 by mode (more for code exploration) | a turn is one model round-trip |
repair attempts | 1 | the failure reason is quoted back to the worker |
models | 366 available via Pi | chosen in the UI per round; never silently substituted |
5 · Breeding two sources
With one selected parent, the candidate count is divided across that parent: each candidate is a variant of it. With two selected parents, the studio breeds the pair instead — every candidate of the round is a hybrid of both. Two sources must share one seed and one resolution, because a hybrid is one seeded artwork, not a collage of two.
The two kinds of hybrid differ in an important way. The parameter hybrid is mechanical — the studio performs the cross itself, so the result is exactly the arithmetic and nothing else; no model is called and no taste is involved. The code hybrid is a synthesis: a worker reads both programs and writes one that keeps the seeded, frame-driven contract while carrying both parents’ character. In both cases the artist chose the pair, and the record shows the cross field by field or the actual source diff.
6 · The worker sandbox
Each candidate gets an isolated working copy of its parent’s frozen bytes. A worker agent never sees the studio, the browser, the network, or another worker’s copy. It has six tools and nothing else — no shell, no filesystem outside its copy, no package installation. Completion requires a structured submission, never an assistant message.
| Tool | What it may do |
|---|---|
list_artwork | see the files of its working copy |
read_artwork | read allowed paths (its own editable files plus the read-only second source) |
patch_artwork / replace_in_artwork | edit config.json, src/a-gen.js, src/renderer.js — and, for code work, add bounded helper modules under src/ |
validate_artwork | pre-check its own work against the seal-time rules |
submit_candidate | submit hypothesis, intended changes, and the edited files — once |
Enforcement lives in the extension, not in the model: resolved-path containment (traversal, UNC paths and symlinks are rejected), mutation-mode allowlists, a 256 KiB source quota, at most 12 files, 64 KiB tool results, and static import checks (only three may be imported; the parser is a real ES-module parser, nothing is executed). Parameter workers cannot edit JavaScript at all — the freeze compares file hashes.
7 · Validation, freezing, repair
When a worker submits, the studio — never the model — decides what happens. The config is resolved against known bounds (a field outside its range is an error; the legacy numeric canvas.hud written by an earlier validator version is resolved back to its boolean). Diffs are computed against the actual parent snapshot, so a model’s summary is never trusted as the diff. A submission that changes nothing is rejected as a no-op. A valid submission is frozen: the source files, the canonical config, and the ordered textures become content-addressed blobs, and a sealed version is created — immutable forever.
A rejected submission gets one repair attempt: a new session in the same workspace, with the failure reason quoted back. The rejected submission file is removed first, so the repair is a real worker turn rather than a replay of the rejected bytes. Every attempt — initial, repair, failed — stays in the record with its validation result, token usage, and timing. Failed candidates remain visible in the graph, each card stating its reason. A restart marks unfinished rounds as interrupted instead of guessing.
8 · Rendering
Rendering uses only frozen bytes. A fresh, sandboxed Chromium opens on a separate ephemeral origin and loads a trusted runner that was itself frozen into the version snapshot. The runner starts the simulation, waits for an explicit ready handshake, and steps the artwork frame by frame while the controller counts frames — the controller owns time limits and success decisions, not the artwork’s own frame counter. When the requested frame count is reached, the final visible canvas is captured at native size (no UI chrome), the PNG dimensions are verified, and the image plus a derived thumbnail are archived together with a recipe.
- Never fewer particles, never lower resolution, never reduced quality to finish faster.
- The wall clock is only a safety ceiling (5 s per frame, 75 s stall watchdog).
- The recipe records source hash, config, asset hashes, seed, frame count, runner hash, Chromium revision, and the runtime identity — so a replay is honest about what it can and cannot promise.
- Additional renders of the same source belong to the same version and restart from the seed.

9 · The archive
Runtime data lives outside the repository, under %LOCALAPPDATA%\a-gen\projects\<project-id>\: archive.sqlite, the blob store, private agent sessions, disposable job directories, and exports. The browser’s state and the agents’ chat history are not the experiment database — the server’s records are.
10 · The interface
The browser is a working archive, not a chat. Three views carry the work. The Graph is the lineage: cards with real renders (or a hatched placeholder and an honest state), thin arrows for parentage — solid for parameter mutations, dashed for focused code mutations, dotted for explorations, faint dashes for the same-round braces. Siblings that share a parent set are grouped and centered under their parents, with a wider gap between families. Compare places two versions side by side at equal size with frames and seconds shown. The Inspector holds the summary, the config delta, the source diff, and the full provenance of one version. The bar at the bottom carries the selection, the verbatim direction, the model, and the mutation mix.
With exactly one version selected, the Inspector offers View: the studio serves that version’s frozen source from an origin of its own and the artwork plays there — not as a render, but as the artist watching the frozen code run. Watching is not work: it writes no render, event or provenance row, and the frame count in the window is measured against the length that was last recorded for that version.

11 · Safety rules
- The studio and its API are loopback-only; the browser never receives a shell or a raw agent channel.
- Model output is untrusted input at every boundary — submissions are parsed, not executed.
- Artwork code runs only in an isolated origin of its own — the sandboxed render Chromium, or the preview origin the studio serves to the artist’s window; never in Node.
- Worker tools are allowlisted by path and mode; quotas are finite; credentials stay server-side.
- Cancellation is confirmed only after owned processes have actually stopped.
- Failures stay visible; completed siblings are preserved; nothing is silently retried or deleted.
A-GEN_01 — human-directed evolution of a seeded noise-field painting
Documentation compiled 2026-09-14 from the running system and the repository. This page and the screenshots next to it are part of the experiment record; images are kept exactly as captured.