Skip to content
Sample of 2D FFT Texture Spectrum (WebGPU butterfly FFT)

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.

Family
anadol_webgpu
Source
anadol · webgpu
Evolution
compute
Render
fft_spectrum
Complexity
medium
Agents
1
Year
2024
Author
WebGPU compute primitives (concept)

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

Sample of Compute Particle System (WebGPU @compute pattern)

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.

View →
Sample of Data Particle Field (Refik Anadol — Machine Hallucinations / AI Data Sculptures)

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.

View →
Sample of Dreamscape Field (Refik Anadol — Dreams / Nature Dreams)

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.

View →
Sample of Latent Walk (Refik Anadol — Unsupervised / Machine Hallucinations)

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.

View →
Sample of Parallel Reduction (WebGPU workgroup-shared-memory tree reduction)

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.

View →
Sample of Tile Blur (WebGPU separable Gaussian tile-shared-memory blur)

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.

View →

See all anadol_webgpu artworks →