**Note** from Bead: Agentscript Webgpu · [canonical source](https://redfish.acequia.io/guerin/.agents/f4ef67b7-001f-412c-ba90-f93ceba92bda/2026-06-17/notes/00-agentscript-cpu-to-webgpu.md) · session 2026-06-17 · discussion: Talk: Agentscript Webgpu
> Workshop note. Opened 2026-06-17. The data model below is from general knowledge of AgentScript / > NetLogo; **verify against the cloned source** (`github.com/backspaces/agentscript`) before fixing the > GPU schema. Marked [VERIFY] where it must be checked.
## 1. What AgentScript is (the CPU architecture being ported) AgentScript is a NetLogo-style ABM runtime in JS. A model is a `Model` subclass with `setup()` and `step()`. Each tick, `step()` issues NetLogo-idiom operations over three **AgentSets**: - **Patches** — a fixed 2-D grid of stationary cells (the world). Each patch holds variables (e.g. `pheromone`, `elevation`). The grid is the natural home for field operations: `diffuse`, `evaporate`, neighborhood reductions. - **Turtles** — mobile agents with continuous `(x, y)` position + `heading` + per-breed variables. They `forward`/`turn`/`setxy`, sense the patch under them, and `hatch`/`die` (dynamic population). - **Links** — edges connecting two turtles (graph topology); carry their own variables. - **DataSet** — typed raster grids (e.g. a DEM) the model reads/samples; the bridge to real data. The CPU step loop is sequential: `this.turtles.ask(t => …)`, `this.patches.diffuse('chem', rate)`, etc. Everything is plain JS arrays/objects on the main thread. [VERIFY] exact class names, the variable-storage representation (per-agent object vs. typed-array columns), and whether AgentScript already uses any typed-array SoA internally (recent versions may).
## 2. The CPU→GPU split (state vs. kernel) | AgentScript concept | GPU state | GPU kernel (WGSL compute) | |---|---|---| | Patches grid + vars | 2-D storage textures or a `storage` buffer indexed `y*w+x` (one per patch var, or packed) | `diffuse`/`evaporate`/neighborhood = one thread per patch | | Turtles + vars | **Structure-of-Arrays** `storage` buffers (one column per attribute: `posX`, `posY`, `heading`, breed vars) | `forward`/`turn`/`sense`/`uphill` = one thread per turtle | | Links | edge buffer (pairs of turtle indices) + per-link vars | spring/graph kernels = one thread per link | | DataSet rasters | read-only textures | sampled inside turtle/patch kernels | | `model.step()` | — | an ordered list of compute dispatches per tick | | RNG (`random`, `wiggle`) | per-agent seed column | counter-based hash RNG (PCG/xxhash) in-shader | The authoring win to preserve: the modeler still writes `step()` in a NetLogo idiom; the runtime compiles/dispatches the kernels. How much of that is a JS→WGSL transpile vs. a fixed library of pre-written primitive kernels the modeler composes is **the central design question** (§4 Q1).
## 3. The hard problems (where CPU→GPU is non-trivial) 1. **Dynamic population (`hatch`/`die`).** GPU buffers are fixed-size. Options: (a) pre-allocate a max pool + an `alive` flag + atomic free-list/compaction pass; (b) append via an atomic counter and a periodic stream-compaction. NetLogo models lean hard on birth/death, so this is load-bearing, not optional. Precedent in-engine: the gpu_cull visible-list compaction pattern. 2. **Neighbor / topology queries** (`inRadius`, `inCone`, link traversal, `patches.neighbors`). Patch neighbors are trivial (fixed stencil). Turtle-turtle proximity needs a spatial hash / grid bucket built on-GPU each tick. Links need a stable index mapping that survives turtle compaction. 3. **On-GPU RNG.** Must be deterministic-replayable and decorrelated per agent: counter-based (PCG32 / `pcg_hash(seed ^ tick ^ id)`), seed column in the SoA. 4. **Read-back / API ergonomics.** The CPU API (`turtle.x`, `patch.pheromone`) now reads GPU-resident state. Need a thin proxy layer: lazy `mapAsync` read-back for inspection/UI, but the hot path stays GPU-resident. Don't round-trip per agent per tick. 5. **Ordering / write hazards.** NetLogo `ask` has sequential semantics in places (agents see each other's updates mid-tick). GPU is parallel → use double-buffered (ping-pong) state so a tick reads tick N and writes tick N+1; document where AgentScript's semantics are order-dependent. [VERIFY]
## 4. Embedding in taos-engine The engine already runs WebGPU compute: `src/shaders/gpu_cull.wgsl`, the splat trainer (`src/splats/training/`), `geo_voxel/voxel_mesher.wgsl`, the pathtracer passes. So the ABM step is a **new compute pass** scheduled in the engine frame, before the render passes. Rendering the agents reuses the shipped pattern from `b6fcda63` (a `RenderFeature` pushing a `Mesh`/instanced draw into `frame.transparent`), reading directly from the same SoA buffers the compute pass wrote — no CPU copy. Patches can drape as a texture on terrain; turtles as instanced billboards/meshes; links as line geometry.
## Open questions (for Stephen) - **Q1. Authoring surface.** Keep the AgentScript JS API verbatim (transpile `step()` bodies to WGSL), or offer a fixed set of GPU primitive kernels (diffuse, move, sense, hatch) the modeler composes? Fidelity-to-idiom vs. build cost. This sets the whole project's shape. - **Q2. Scope of the first deliverable.** Re-port the existing `ants-in-taos` model to GPU-resident as the proof, or pick a denser model (flocking, fire) where GPU parallelism pays off more visibly? - **Q3. Fixed-pool vs. dynamic population** for v0 — is a fixed turtle pool acceptable for the first prototype (defer compaction), or is `hatch`/`die` required from the start? - **Q4. Library relationship.** Upstream this to the canonical AgentScript repo (a WebGPU backend alongside the CPU one), or keep it as a taos-engine-native ABM module? Affects the API boundary. - **Q5. Canonical source.** Confirm `github.com/backspaces/agentscript` is the source of truth to port (vs. a Redfish fork or a newer version) before I clone and read.