Game of Life: Tribes

#Limits And Performance

#Hard-Coded Limits

Limit Value Reason
Minimum grid columns/rows 33 Keeps Moore-neighborhood editing and topology behavior meaningful.
Neighbor count range 00 to 88 Moore neighborhood has exactly 88 neighbors.
Comparison margin 8-8 to 88 Margin is applied to neighbor counts.
Supported packing 11, 22, 44, 88, 1616, 3232 bits/cell Fits cleanly into u32 words.
Render color lookup 256256 tribes Render palette buffer is sized for 256256 colors.
Max recording frame 1 GiB1\text{ GiB} Keeps individual recorded frames bounded.
Recording chunk cap 256 MiB256\text{ MiB} Preferred chunk size before frame-size fallback.
Staging ring size 33 buffers Allows readback overlap while keeping VRAM bounded.
Max pending OPFS writes 1212 Hard backpressure threshold.
Pending OPFS write budget 512 MiB512\text{ MiB} Limits raw unsaved recording pressure.
Pending compression budget 1 GiB1\text{ GiB} Limits queued compression pressure.
Download chunk-mode threshold 2 GiB2\text{ GiB} Avoids very large in-memory normal exports.
Snapshot streaming threshold 256 MiB256\text{ MiB} Switches large snapshots to streaming.
Snapshot repack block 64 MiB64\text{ MiB} Bounded block size during streaming snapshot work.
MP4 max dimension 4096 px4\,096\text{ px} Keeps video output inside practical encoder limits.
MP4 small-grid reference 1080 px1\,080\text{ px} Upscales small grids to more useful video size.
MP4 persisted FPS 11 to 240240 Bounds user settings.
MP4 persisted bitrate 11 to 60 Mbps60\text{ Mbps} Bounds user settings.
Max combine inputs per row 88 Matches neighbor-scale logic and keeps generated rules bounded.
Rule branches per WGSL chain 3232 Avoids long chains to prevent reaching the shader chaining limit.
Random seed 11 to 42949672954\,294\,967\,295 Keeps deterministic probability rolls inside unsigned 3232-bit space.
Rule probability input 00 to 100100 Percentage with up to 33 decimal digits.
Brush density 11 to 100%100\% Bounds density validation and shader selection percentage.
Brush selected tribe ID slots 3232 Fixed brush uniform array size.

#Device Limits

Some limits are not hard-coded by the app. They come from the WebGPU adapter selected by the browser, so they can change across devices, browsers, drivers, and power modes.

Two important limits for grid size are:

  • maxBufferSize: the largest individual GPU buffer that can be created.
  • maxStorageBufferBindingSize: the largest storage-buffer range that can be bound for shader access.

The simulation frame must fit both constraints, so the effective maximum simulation frame size is:

maxSimulationBytes=min(maxBufferSize,maxStorageBufferBindingSize)\texttt{maxSimulationBytes}=\min\left(\texttt{maxBufferSize}, \texttt{maxStorageBufferBindingSize}\right)

See VRAM and packing for the frame-size formula and how packing affects these limits.

#Performance Tradeoffs

The largest costs are grid size, packing, rule complexity, recording, metrics, and export selection.

  • Larger grids increase compute work and frame bytes.
  • Lower bits per cell reduce memory and storage, but must still represent all tribes.
  • More rules and complex selectors generate larger shaders and more branch/count work.
  • Probabilistic rules add deterministic hash and threshold checks to generated simulation branches.
  • Recording copies every captured frame and writes chunks to OPFS. Active recording runs batch ordered step/copy command pairs to reduce per-generation queue-submission overhead without dropping generations.
  • Live metrics add GPU passes and readback.
  • PNG, MP4, and offline metrics require scanning recorded frames.
  • Max speed disables rendering so simulation work can dominate.

Non-recording high-throughput runs use adaptive batching. The grid-size tier is based on log10(colsrows100000)\log_{10}\left(\frac{\texttt{cols}\,\cdot\,\texttt{rows}}{100\,000}\right), clamped from 00 through 33, and seeds the initial generation budget. After each GPU queue drain, the worker measures drain time and adjusts the next budget with bounded growth and shrink. Max speed and multi-generation step-forward target runs use a 500 ms500\text{ ms} drain target because rendering is paused or transient. Fixed-speed catch-up uses a 33 ms33\text{ ms} target so visible canvas updates remain responsive when the requested speed is higher than the device can sustain. Recording runs keep separate pacing because they must preserve every captured frame and honor recording backpressure. Recording batching improves small-frame overhead by submitting several ordered step/copy pairs together, but large backpressured recordings are still limited mainly by GPU readback, CPU copy, and OPFS write throughput.

#Failure Boundaries

The app logs and surfaces failures at lifecycle boundaries:

  • WebGPU initialization and rebuild.
  • GPU device loss.
  • GPU validation/runtime errors.
  • OPFS cleanup and recording persistence.
  • Compression retries and deferrals.
  • Snapshot load/save failures.
  • Download preparation and worker export failures.
  • Wake lock acquisition and release.

When effective recording usage reaches 75%75\% of the browser-reported storage quota estimate, the app pauses the simulation to preserve data. Effective usage is pending raw recording bytes plus compressed recording bytes plus reserved recording headroom. At 100%100\%, it pauses and disables recording so the user can save data and reset. Recording is also disabled whenever the remaining quota after pending, compressed, and reserved bytes is smaller than 11 frame. The browser-reported quota estimate is approximate and may change as the browser manages storage. If an actual OPFS write still fails because the browser enforces a lower storage limit, recording is stopped and the app rolls back to the previous persisted frame instead of surfacing a GPU error.

Game of Life: Tribes · Simulation v1.0.0 · Open source