HomeCompanion pagesPID-Regulated Cellular Automaton

PID-Regulated Cellular Automaton

A cellular automaton whose update rule is a per-cell PID controller: each cell measures its active-neighbor count against a target and computes proportional, integral, and derivative terms to decide its next state. The result is a rule family with local memory (integral windup), local anticipation (derivative), and tunable dynamical regimes — under-damped ripples, critically-damped blobs, over-damped glassy creep — all from the same control-theoretic mechanism.

Open the interactive lab Watch the video

PID-Regulated Cellular Automaton

Reference implementation of the specification in idea.md: a discrete-state, discrete-time cellular automaton whose update rule is a per-cell PID controller regulating the cell's active-neighbour count N_t(c) toward a target T.

Running

ES modules require an HTTP origin (they will not load from file://):

python3 -m http.server 8080
# then open http://localhost:8080/index.html

No build step, bundler, or backend is required.

Module map

File Role
js/config.js Parameter schema, defaults, validation, JSON/hash serialisation, change notification
js/grid.js State + controller-state buffers, neighbour enumeration, boundary conditions, initial conditions, seeded PRNG
js/controller.js Pure PID computation: (e_(t-1), I_(t-1), e_t, gains) -> (P, I, D, u) with anti-windup clamping
js/stateExpression.js Pluggable u_t -> state strategies: threshold, quantisation, semantic, probabilistic
js/simulation.js One synchronous timestep per call, double buffering, playback scheduling, statistics
js/renderer.js Canvas drawing of states plus optional u_t / I_t / e_t heat overlay
js/ui.js Schema-driven control panel, playback buttons, grid painting, presets, export/import
js/presets.js Named configurations for the behaviour classes in §10 of the spec
js/main.js Wiring + frame scheduler

Update semantics

Each step is a pure function of (states_t, prevError_t, integral_t, config). All cells read a frozen snapshot; results are written to shadow buffers and swapped atomically (Grid.commit()), so runs are deterministic for a given seed and configuration — except in probabilistic expression mode, whose randomness is itself seeded by config.seed.

Controls

Extending