HomeJournalThis post

Physarum Generative Networks Measured

A field guide to Physarum-inspired network art: particle sensing, deposits, diffusion, decay, density thresholds, deterministic pass order, and accessible output.

JP
JP Casabianca
UI/UX designer and full-stack engineer · Bogotá

Physarum generative networks turn thousands of simple sensing and deposit decisions into veins, meshes, deltas, and lace. The visual richness comes from a feedback field: agents follow trail gradients, reinforce their passage, and inherit a landscape changed by diffusion and decay.

That loop becomes an art system only when pass order, mass, threshold, variants, and fallback are explicit enough to reproduce. The evidence distinguishes slime mold particle simulation, trail diffusion field, synthetic transport networks, and a canvas density heatmap so each visual decision stays inspectable.

Agent, trail, and diffusion feedback loopAgents sense a shared trail field, rotate toward a local gradient, move and deposit, then a diffusion and decay pass reshapes the field before the next ordered step. sensesteerdepositdiffuse
  1. sense
  2. steer
  3. deposit
  4. diffuse
Figure 1: The network is produced by a delayed feedback loop rather than a line-growing command. The cycle implies that changing pass order changes the algorithm even when every parameter stays equal.

Physarum generative networks need an ordered loop

Define one simulation tick as a sequence of immutable conceptual passes: sense the previous trail, choose an orientation, resolve movement, deposit new trail, diffuse the combined field, and decay it. Rendering reads the finished state without modifying it. This order matters. Depositing before movement versus after movement shifts the network; sensing a partially updated field lets earlier agents influence later agents inside the same tick; tying decay to display frames changes the result with refresh rate.

The synthetic Physarum transport-network study describes a particle approach to constructing and minimizing networks. A creative implementation can borrow the local feedback idea without claiming to simulate the organism completely. Physarum generative networks should name which behavior is modeled, which parameters are artistic, and which output is an inference from the synthetic system.

Use two buffers for trails and one previous-state array for agents. Agents read only the old field; deposits accumulate commutatively into a new buffer; diffusion and decay run after all deposits. Sort or index agents deterministically. Pin boundary behavior: wrap creates continuous topology, clamp piles mass at edges, and absorb removes agents. Record the timestep and seed. This simple schedule makes a frame reproducible and makes failures attributable to a pass rather than to “emergence.”

Seed stimuli and absence with equal care

Networks need conditions: agent positions, headings, trail background, attractant sources, repellents, obstacles, and a boundary. The Science study of adaptive Physarum networks examines how biological networks balance efficiency, fault tolerance, and cost. Use that as conceptual grounding, not as a license to label every branching image optimal. In art direction, stimuli can correspond to landmarks, text, data points, or purely compositional anchors.

Design three purposeful variants from the same seed. Veins can use stronger deposit and moderate diffusion for persistent trunks. Delta can diffuse broadly with slow decay to favor connected fans. Lace can decay faster and preserve narrow exploratory filaments. Physarum generative networks become comparable when variant names predict density, branch persistence, and negative-space behavior. Store the settings beside a small heatmap and trail-mass curve.

Empty stimuli deserve a visual decision. The system may settle, roam, or fade to blank; none should crash. A single stimulus reveals radial bias. Closely spaced sources reveal whether branches merge. Obstacles expose leakage and boundary aliasing. Use seeded randomness in generative art to hold the initial population fixed through the contact sheet. If one variant needs a new seed to look good, the preset may be fragile rather than distinct.

011000
124210
025531
013541
001220
Figure 2: Density is accumulated evidence of repeated passage, not a direct edge list. The heatmap implies that thresholding too early destroys faint exploratory branches while thresholding too late turns the network into a solid mass.

Make trail density a measured image source

The trail grid is a scalar field. Rendering can map it through a linear or logarithmic tone curve, isolate ridges, threshold it into vector-like branches, or composite several age bands. Each mapping changes the story. Physarum generative networks should preserve raw density for analysis and treat the displayed palette as a separate transform. Never feed a color-corrected render back into sensing.

Track total mass, peak density, occupied-cell ratio, connected components above several thresholds, and branch coverage near stimuli. A threshold sweep is more honest than selecting the single value that creates the prettiest network. Faint paths represent exploration; dense paths represent repeated use. If the render clips both to the same ink, it discards time. Canvas blend modes can enrich the image, but keep a normal-composite control that reveals the field without glow or accumulation tricks.

The heatmap is also an accessibility fallback. A semantic table can expose small illustrative values, while a full artwork needs a concise description of branch density, direction, and anchors rather than thousands of cells. Provide a static heatmap when reduced motion is requested. Make palette meaning redundant with luminance or contour, and test grayscale and high contrast. Density remains the source of meaning even when animation, hue, and canvas are unavailable.

Prove mass and agent-order invariants

Deposits add a known amount of trail. Diffusion should redistribute rather than create unexplained mass, subject to the declared boundary. Decay should remove a known fraction. Floating-point and edge handling introduce small differences, so calculate an expected upper bound instead of demanding an impossible conservation law. If mass grows faster than deposits permit, Physarum generative networks have an algorithmic bug hidden inside an attractive bloom.

Agent iteration order should not change a synchronous tick. Reverse the agent array and compare the finished trail. This test requires agents to sense the previous field and deposits to add commutatively. If collisions or exclusive occupancy are part of the model, resolve them with a deterministic policy and test that policy separately. Repeat the same seed twice and compare field summaries or exact arrays under the reference implementation.

The artifact below isolates deposit, local diffusion, decay, and rendering policy on a tiny grid. It covers repeatability, reversed-agent order, mass bounds, three variants, empty agents, grid performance limits, reduced motion, no-canvas fallback, and a semantic density description. A GPU implementation from WebGPU generative art can accelerate Physarum generative networks, but it should match this CPU-scale receipt within published tolerance before its higher population count becomes evidence.

Runnable artifact: The fixture keeps Physarum generative networks accountable through ten assertions and three named outcomes. Its density table and static heatmap are explicit fallbacks, while the cell budget prevents a visual preset from allocating an accidental million-cell test.

Save this as physarum-trail-mass.mjs and run node physarum-trail-mass.mjs. Expected final line: PASS: 10 Physarum trail assertions.

import assert from "node:assert/strict";
const variants = Object.freeze({ veins: Object.freeze({ deposit: 1, diffusion: 0.45, decay: 0.08 }), delta: Object.freeze({ deposit: 0.7, diffusion: 0.7, decay: 0.04 }), lace: Object.freeze({ deposit: 1.3, diffusion: 0.25, decay: 0.16 }) });
const index = (x, y, width) => y * width + x;
export function trailPass({ width, height, trail, agents }, config, maxCells = 65_536) {
  if (!Number.isInteger(width) || !Number.isInteger(height) || width * height > maxCells) throw new RangeError("grid_budget_exceeded");
  if (trail.length !== width * height) throw new RangeError("trail_shape_mismatch");
  const deposited = [...trail];
  for (const agent of agents) { const x = Math.max(0, Math.min(width - 1, Math.floor(agent.x))); const y = Math.max(0, Math.min(height - 1, Math.floor(agent.y))); deposited[index(x, y, width)] += config.deposit; }
  const next = deposited.map((value, cell) => {
    const x = cell % width, y = Math.floor(cell / width); let sum = 0, count = 0;
    for (const [dx, dy] of [[0,0],[-1,0],[1,0],[0,-1],[0,1]]) { const nx = x + dx, ny = y + dy; if (nx >= 0 && nx < width && ny >= 0 && ny < height) { sum += deposited[index(nx, ny, width)]; count += 1; } }
    return ((1 - config.diffusion) * value + config.diffusion * sum / count) * (1 - config.decay);
  }); return Object.freeze(next);
}
export const renderMode = ({ reducedMotion, canvasAvailable }) => !canvasAvailable ? "density-table" : reducedMotion ? "static-heatmap" : "animated-canvas";
export const densityDescription = (trail) => "Trail field with peak density " + Math.max(0, ...trail).toFixed(2);
const setup = Object.freeze({ width: 4, height: 3, trail: Object.freeze(Array(12).fill(0)), agents: Object.freeze([{x:1,y:1},{x:2,y:1}]) }); const mass = (values) => values.reduce((sum, value) => sum + value, 0);
let assertions = 0; const check = (fn) => { fn(); assertions += 1; };
check(() => assert.deepEqual(trailPass(setup, variants.veins), trailPass(setup, variants.veins)));
check(() => assert.deepEqual(trailPass({ ...setup, agents: [...setup.agents].reverse() }, variants.veins), trailPass(setup, variants.veins)));
check(() => assert.ok(mass(trailPass(setup, variants.veins)) <= setup.agents.length * variants.veins.deposit));
check(() => assert.notDeepEqual(trailPass(setup, variants.veins), trailPass(setup, variants.delta)));
check(() => assert.notDeepEqual(trailPass(setup, variants.delta), trailPass(setup, variants.lace)));
check(() => assert.deepEqual(trailPass({ ...setup, agents: [] }, variants.veins), Array(12).fill(0)));
check(() => assert.throws(() => trailPass({ ...setup, width: 1000, height: 1000, trail: [] }, variants.veins), /grid_budget_exceeded/));
check(() => assert.equal(renderMode({ reducedMotion: true, canvasAvailable: true }), "static-heatmap"));
check(() => assert.equal(renderMode({ reducedMotion: false, canvasAvailable: false }), "density-table"));
check(() => assert.match(densityDescription(trailPass(setup, variants.veins)), /peak density/));
assert.equal(Object.keys(variants).length, 3); assert.equal(assertions, 10); console.log("PASS: 10 Physarum trail assertions");

Separate simulation resolution from display resolution

Trail resolution controls behavior because it changes sensor sampling, diffusion distance, and branch width. Display resolution controls how the finished field is rasterized. Scaling one without the other is not a neutral quality change. Physarum generative networks need a simulation grid defined in world units and a renderer that can sample it into multiple output sizes. Record device pixel ratio only in the rendering receipt.

Set budgets for agents, grid cells, sensor reads, diffusion passes, trail buffers, and milliseconds per tick. Pause offscreen work. Under load, draw fewer frames without skipping or merging simulation ticks; otherwise the network evolves differently on slower devices. Offscreen canvas generative posters can move raster work away from the main thread, while static export should remain available for print and sharing.

The WHATWG Canvas specification makes clear that canvas is a bitmap drawing surface with a fallback subtree. Use that subtree for an informative caption or static image, not a duplicate inaccessible simulation. Test loss of canvas context, tab suspension, resize, reduced motion, low memory, and a zero-agent state. Performance degradation should select a declared quality tier; it should not silently modify diffusion or decay and produce another artwork under the same preset name.

PassConserved or bounded quantityFailure clue
Agent depositAdds declared trail massOrder-dependent collision
DiffusionRedistributes local massEdge leakage or amplification
DecayRemoves a declared fractionInfinite persistence
RenderReads without mutationFrame-rate changes outcome
Figure 3: A visual simulation still needs an accounting identity. The ledger implies that unexpected trail growth should be traced to deposit, boundary, diffusion, or decay instead of tuned away by eye.

Publish the network as a process receipt

Store seed, agent count and initialization, sensors, rotation, step size, occupancy rule, deposit amount, diffusion kernel, decay, boundary, stimuli, obstacles, tick count, thresholds, palette, renderer, and code revision. Include raw field summaries, three variant stills, a time sample, mass plot, and fallback description. Physarum generative networks become editions when the process is repeatable enough to distinguish an intentional mutation from a software accident.

Review the family at matched tick counts and output scales. Reject a variant that succeeds only after arbitrary stopping, a network that leaks through obstacles, a threshold that hides all exploration, or a field whose mass depends on agent array order. Compare the raw heatmap before evaluating glow and color. Label scientific inspiration precisely and avoid equating visual similarity with biological fidelity or transport optimality.

The final piece should hold at three levels: local trails feel alive, global density supports the composition, and the system receipt explains why the image formed. A static fallback should retain that structure; a performance tier should retain the same dynamics at lower resolution. The useful magic of Physarum generative networks is not unaccountable randomness. It is the visible accumulation of small ordered decisions into a network no single agent planned.