▶ Live demo: run this tutorial — built from src/03_patches.ts. Distilled from samples/agent_slime.ts.
Patches are AgentScript's grid — a field of numbers that diffuses and evaporates each tick. Agents read it (
sense) and write it (deposit), coordinating only through the trail they leave behind. That's stigmergy, and the canonical example is the slime mold (Physarum): agents steer up the pheromone gradient they themselves lay, and self-organize into a network.
Declaring a patch grid#
Add a patches block — a square grid mapped onto your world rectangle, holding one
or more scalar fields:
patches: {
resolution: 512, // a 512×512 grid
worldMin: [-HALF, -HALF], worldSize: [WORLD, WORLD],
fields: [{ name: 'pheromone', diffuse: 0.12, evaporate: 0.94 }],
},Per field: diffuse (in [0,1]) is the fraction each cell blends toward its
4-neighbor average per step — AgentScript's patches.diffuse. evaporate (in
[0,1]) is the multiplicative decay (1 = none, 0.94 ≈ fades over ~16 steps).
Sensing and depositing#
Inside the step, agents read and write the field by index:
step: [
agent.turn(agent.steer(0, { dist: 9, angle: 0.5, turn: 0.5 })), // sniff & steer
agent.turn(agent.randomTurn(0.1)), // wander → branching
agent.moveForward(18),
agent.deposit(0, 5), // lay pheromone here
],agent.sense(f)samples fieldfat the agent's cell.agent.senseAhead(f, d)/agent.senseAt(f, d, angle)sample a point ahead / off to a side — AgentScript'spatchAheadetc.agent.steer(f, {dist, angle, turn})is the 3-sensor Physarum "sniff": it samples the field ahead and to each side and returns a turn toward the strongest.agent.deposit(f, amount)atomically adds to the agent's cell (negative to subtract). Many agents depositing into one cell is safe — it's an atomic add.
Deposits are fixed-point under the hood (depositScale, default 1024); for a
static field that agents only edit (no spreading), use diffuse: 0, evaporate: 1.
Visualizing the field#
A field is invisible on its own. Add a patchViews entry to the runAgentModel
call and the harness draws it as an additive colormapped ground quad — it wires the
live buffer source and world rectangle for you:
runAgentModel({
params: {},
model: slimeMold,
camera: { /* … */ },
patchViews: [{ field: 0, intensity: 0.05 }],
});PatchViewOptions also takes tint, flat (a solid color for discrete state
fields rather than the ramp), opaque (draw under the agents), and a two-field
compare mode. To tweak a view live from a control, app.patchView(0) returns it.
importDataSet / exportDataSet#
To preload a field from a CPU raster (a heightmap, a cost map — AgentScript's
importDataSet), call app.seedPatch(field, Float32Array) from onReady (row-major
[z*res + x]). To read one back for measurement (exportDataSet),
await app.readPatch(field) — see tutorial 8.
Next#
- Tutorial 4 — Neighbors & Flocking: agent-to- agent interaction via the spatial hash.