anadol_webgpu · parallelReduction
Parallel Reduction (WebGPU workgroup-shared-memory tree reduction)
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. A second dispatch (or atomics) reduces those to a single scalar. Here the algorithm is re-implemented in numpy level-by-level, and each intermediate level of the reduction tree is rendered as a horizontal bar chart (leaves at top, root scalar at bottom) so the spatial structure of the reduction is directly observable. Stepping refills the input array with a different seed, animating the reduction in time.
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
Initialise a length-N array of doubles (sin+cos+noise+spikes). Apply a binary tree reduction op ∈ {sum, max, mean}; intermediate levels L[0..k] saved. Render row r with horizontal bars whose heights equal the L[r] elements (normalised per-level). Repeating with a fresh seed each step animates the level rows.
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 600 | 200–1500 | Square canvas side length. |
array_size | int | 65536 | 256–1048576 | Array length N (power of 2 preferred). |
workgroup_size | int | 256 | 32–1024 | Equivalent WebGPU @workgroup_size. |
mode | str | sum | null–null | sum|max|mean — reduction operator. |
data_seed | int | 42 | 0–2147483647 | Input array seed (incremented each step). |
data_scale | float | 1 | 0–10 | Input scaling. |
bar_height_max | float | 220 | 20–600 | Max bar height in px. |
Reference
First 60 lines of reference.py. Full source:
library_external/anadol_webgpu/parallelReduction/reference.py
"""parallelReduction — WebGPU parallel reduction primitive.
The WebGPU 'parallel reduction' pattern reduces an array of N elements
to a single scalar (or per-workgroup vector) using a tree-of-sums in
workgroup-shared memory. This is the same primitive used in
histograms, image moments, mean/max/sum statistics, and the first
phase of a softmax / dot-product kernel.
Reference implementation in WGSL (illustrative):
const WS = 256u;
var<workgroup> sh : array<f32, WS>;
@compute @workgroup_size(WS)
fn reduce_sum(
@builtin(global_invocation_id) gid : vec3u,
@builtin(local_invocation_id) lid : vec3u,
@builtin(workgroup_id) wid : vec3u,
) {
let i = gid.x;
let n = arrayLength(&data);
sh[lid.x] = select(0.0, data[i], i < n);
workgroupBarrier();
var stride = WS / 2u;
loop {
if (stride == 0u) { break; }
if (lid.x < stride) {
sh[lid.x] = sh[lid.x] + sh[lid.x + stride];
}
workgroupBarrier();
stride /= 2u;
}
if (lid.x == 0u) { result[wid.x] = sh[0]; }
}
A second dispatch then sums the per-workgroup partials.
Here we reduce a synthesised array of N floats and render the
workgroup-block partials as a 1D bar chart so the spatial structure
of the reduction (root-of-tree semantics) is directly visible.
Reference:
- https://www.mysimulator.uk/content/articles/webgpu-compute.html
(parallel reduction using shared memory in WGSL)
- https://webgpufundamentals.org/webgpu/lessons/webgpu-compute-shaders.html
"""
from __future__ import annotations
from typing import ClassVar
import numpy as np
class ParallelReduction:
"""Workgroup-tree parallel reduction visualised as a 1D bar chart.
Parameters
----------
dim : int
Square canvas side length (px).
array_size : int Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/anadol_webgpu/parallelReduction/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.
Compute Particle System (WebGPU @compute pattern)
anadol_webgpu · computeParticleSystem
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.
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.
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.