anadol_webgpu · computeParticleSystem
Compute Particle System (WebGPU @compute pattern)
The canonical WebGPU 'compute-to-render' pattern: a @compute shader updates N particles in a storage buffer (one thread per particle, simple Verlet-style integration: v += gravity; v *= drag; p += v; bounce off walls; life++; respawn if life>max), and a render pass consumes the same buffer as vertex data to draw point sprites. This numpy reference does exactly the same algorithm in vectorised form (one thread per particle, identical state transitions) so a downstream model can be trained on the GPU semantics without needing a browser runtime.
Original: https://www.mysimulator.uk/content/articles/webgpu-compute.html
▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.
Math essence
Per-particle update each step: vel += gravity; vel *= drag; pos += vel; pos reflected on wall; life += 1; if life >= life_max then respawn at centre with radial outward velocity and randomised hue seed. Canvas fades geometrically and each live particle splats a 5x5 weighted Gaussian point coloured by its current HSV(per-particle seed + step drift).
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 600 | 200–1500 | Square canvas side length. |
num_particles | int | 80000 | 1000–500000 | Particle count N. |
life_max | int | 240 | 16–2000 | Life in steps before respawn. |
gravity | float | 0.04 | 0–0.5 | Per-step downward acceleration. |
drag | float | 0.985 | 0.5–1 | Per-step velocity damping. |
spawn_radius | float | 0.3 | 0.05–0.5 | Respawn radius (fraction of dim, centred). |
workgroup_size | int | 64 | 32–256 | Equivalent WebGPU @workgroup_size for the compute dispatch. |
Reference
First 60 lines of reference.py. Full source:
library_external/anadol_webgpu/computeParticleSystem/reference.py
"""computeParticleSystem — WebGPU compute-shader particle system.
This is the canonical "compute-to-render" pattern in WebGPU: a
@compute shader updates N particles in a storage buffer, and a
separate render pass draws them as point sprites. Here we implement
the same algorithm in numpy as a faithful reference implementation
of the GPU loop.
Each particle has:
pos: vec2f
vel: vec2f
life: f32 (in [0, life_max])
seed: u32 (per-particle hue)
Every step the compute shader does:
vel += gravity_and_drag(pos, vel, life, t); // simple Verlet-ish
pos += vel;
vel *= drag;
life += dt;
if (life > life_max) reset particle to a new born state;
After the compute pass we render the live particles as glowing points
with colour = hsv(seed + life_normalised). The numpy analogue updates
the same arrays with vectorised ops — semantically one thread per
particle.
Reference: WebGPU compute pattern as documented at
- https://www.mysimulator.uk/content/articles/webgpu-compute.html
- https://dev.to/dev48v/webgpu-i-simulated-250000-particles-entirely-on-the-gpu-in-the-browser-2o0f
- https://www.abratabia.com/webgpu/compute-shaders.php
- https://www.cg.tuwien.ac.at/research/publications/2023/PETER-2023-PSW/PETER-2023-PSW-.pdf
"""
from __future__ import annotations
from typing import ClassVar
import numpy as np
class ComputeParticleSystem:
"""WebGPU compute-shader style particle system (numpy reference).
Parameters
----------
dim : int
Square canvas side length (px).
num_particles : int
Number of particles N.
life_max : int
Steps before a particle is "reset" (born anew at the centre
with a random outward velocity).
gravity : float
Constant downward acceleration per step (per particle).
drag : float
Per-step velocity damping (1.0 = no damping).
spawn_radius : float
Radius (in pixels) inside which a respawning particle lands.
workgroup_size : int
For documentation only — this is the WebGPU @workgroup_size
that an equivalent @compute shader would use. Stored so the Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/anadol_webgpu/computeParticleSystem/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from anadol · webgpu
2D FFT Texture Spectrum (WebGPU butterfly FFT)
anadol_webgpu · fft2dTexture
The WebGPU butterfly 2D FFT pattern: stage by stage, a @compute shader reads two texels per output (input[p] and twiddle-weighted input[q]) and writes the combined result; after log2(N) horizontal stages, transpose, and log2(N) vertical stages, the 2D FFT is complete.
Data Particle Field (Refik Anadol — Machine Hallucinations / AI Data Sculptures)
anadol_webgpu · dataParticleField
Inspired by Refik Anadol Studio's AI Data Sculptures and 'data universe' portraits (e.g.
Dreamscape Field (Refik Anadol — Dreams / Nature Dreams)
anadol_webgpu · dreamscapeField
Inspired by Refik Anadol Studio's Dreams series (Nature Dreams, Urban Dreams, Space Dreams and the MoMA Fluid Dreams algorithm): a smooth low-frequency Perlin noise field drives slow global drift while a finer detail octave adds turbulent perturbation — Anadol's fluid solver aesthetic realised with cheap numpy.
Latent Walk (Refik Anadol — Unsupervised / Machine Hallucinations)
anadol_webgpu · latentWalk
Inspired by Refik Anadol Studio's Unsupervised — Machine Hallucinations — MoMA (2022): a chain of N points is sampled from a synthetic 64-dimensional latent space (a smoothed random walk) and projected to 2D via a fixed random matrix; the path is integrated step-by-step with spherical-linear interpolation between adjacent chain points, so the rendered trail visualises the path the 'latent space browser' would take through the embedding.
Parallel Reduction (WebGPU workgroup-shared-memory tree reduction)
anadol_webgpu · parallelReduction
The classic WebGPU parallel-reduction pattern: load N values into workgroup-shared memory, workgroupBarrier(), tree-reduce by stride (shared[lid.x] = op(shared[lid.x], shared[lid.x + stride]); stride /= 2), workgroupBarrier(), and emit per-workgroup partials from thread 0.
Tile Blur (WebGPU separable Gaussian tile-shared-memory blur)
anadol_webgpu · tileBlur
The WebGPU separable Gaussian blur primitive: a workgroup of TILE×TILE threads loads a tile+halo of the source image into workgroup-shared memory, workgroupBarrier(), convolves each pixel with a 1D Gaussian along the row, writes to a ping-pong texture; then a second compute pass applies the column-direction Gaussian on the intermediate texture.