anadol_webgpu · fft2dTexture
2D FFT Texture Spectrum (WebGPU butterfly FFT)
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. Here np.fft.fft2 computes the spectrum of a synthesised image (sinusoidal sources + circular blobs + noise), bandpass filtering applies a soft circular mask, and the result is rendered as a log-magnitude colormap with the DC cross highlighted. Stepping rotates the spectrum's phase, animating the visualisation.
Original: https://www.gamedev.net/forums/topic/671549-ifft-compute-shader/
▶ 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
F = fft2(synthesised_image); F = fftshift(F); mask F with a soft bandpass; render log|F| with a dark-blue → cyan → yellow → white colormap, OR inverse-Fourier to the real-domain image, OR wrapped phase. Each step multiplies the spectrum by exp(i*2π/n_textures) so successive frames show smoothly varying phase.
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 512 | 64–2048 | Square image/FFT side length. |
n_textures | int | 4 | 1–32 | Number of phase steps per cycle. |
texture_seed | int | 9 | 0–2147483647 | RNG seed for the synthetic image. |
high_pass | float | 0 | 0–256 | Cut frequencies below this radius (cycles). |
low_pass | float | 1000000000 | 0–2048 | Cut frequencies above this radius. |
brightness | float | 1 | 0.1–4 | Brightness multiplier before tone-map. |
domain | str | freq | null–null | freq|real|phase — what to render. |
Reference
First 60 lines of reference.py. Full source:
library_external/anadol_webgpu/fft2dTexture/reference.py
"""fft2dTexture — WebGPU 2D FFT (butterfly) magnitude spectrum.
The WebGPU analogue is the classic two-pass Cooley-Tukey butterfly
algorithm: pass k does a butterfly operation at each (x, y) pixel
combining input1[p] and weighted input2[q] where (p, q) are the two
input indices and weights are precomputed butterfly twiddles for
that stage. The two passes (rows then columns, with a transpose in
between) yield the 2D FFT. GLSL/WGSL implementations are described
in the GPU Gems series and the gamedev.net butterfly compute-shader
write-up.
Here we use ``np.fft.fft2`` directly — the WebGPU equivalent (which
is what the user would write for a WebGPU port) is captured in
``spec.json``. We synthesise a 2D image (sine + circles + Gabor-like
texture) so the spectrum has interesting structure, then render
log-magnitude to the canvas, optionally with a circular low/high-pass
mask and shifted by fftshift so DC is centred.
Reference:
- https://www.gamedev.net/forums/topic/671549-ifft-compute-shader/
- https://en.wikipedia.org/wiki/Cooley%E2%80%93Tukey_FFT_algorithm
- https://github.com/rreusser/glsl-fft (GLSL butterfly FFT, ~paradigm)
"""
from __future__ import annotations
from typing import ClassVar
import numpy as np
class FFT2DTexture:
"""2D FFT spectrum visualisation (numpy FFT, WebGPU butterfly target).
Parameters
----------
dim : int
Side length of the square FFT (and image).
n_textures : int
Number of synthetic images whose spectra are blended into the
canvas. Each step rotates phase by 2π/n_textures.
texture_seed : int
RNG seed for the input image synthesis.
high_pass : float
Radius (in cycles) below which low-frequency content is damped.
low_pass : float
Radius above which high-frequency content is damped. Set to
``np.inf`` for no high-frequency cut.
brightness : float
Multiplier on the log-magnitude before tone-map.
domain : str
``"freq"`` (default) renders shifted log-amplitude in
frequency space; ``"real"`` renders the IFFT real part
(the reconstructed image); ``"phase"`` renders the
wrapped phase.
"""
family: ClassVar[str] = "anadol_webgpu"
sketch: ClassVar[str] = "fft2dTexture"
def __init__( Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/anadol_webgpu/fft2dTexture/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from anadol · webgpu
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.
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.