/ concept-collection / turing-sphere-2
Sign in
concept-collection / turing-sphere-2
turing-sphere-2 / README.md
7.0 KBPreviewCodeBlameHistoryRaw

turing-sphere#

Reaction–diffusion systems (Turing patterns) solved live in the browser on the surface of a sphere, using a spectral spherical-harmonic method with the transforms running on the GPU via WebGPU.

Live demo: https://concept-collection.github.io/turing-sphere/

What it does#

It solves the N-species system

d(u_k)/dt = D_k*lap_s(u_k) + f_k(t, x, y, z, u_1, ..., u_N),    k = 1, ..., N

on the unit sphere, where lap_s is the Laplace–Beltrami operator. Diffusion is treated implicitly in spherical-harmonic coefficient space, where lap_s is diagonal with eigenvalues -l(l+1); reaction is treated explicitly on the grid. The two are combined with a first-order IMEX Euler step — the entire time loop is

V_k  = synth(U_k)                          # spectral -> grid
R_k  = analys(f_k(t, x, y, z, V_1..V_N))   # reaction on grid -> spectral
U_k  = (U_k + dt*R_k) / (1 + dt*D_k*l(l+1))

You watch the patterns emerge in real time on orbitable 3D spheres (one per species, cameras synced), with pause/resume, re-seeding, live parameter editing, and colormap selection.

Three presets are included:

Provenance#

This is the browser port of a MATLAB reference implementation (SphericalReactionDiffusion.m, "websph"), which defines the solver through a four-member porting boundary: coeffs2vals, vals2coeffs, grid.lat, grid.lon. Profiling of the MATLAB version shows the transforms are ~96% of compute, so this port swaps in:

Numerics#

Desktop vs browser#

How much does running this in a browser cost? scripts/bench.ts answers that by running the same code — same Simulation, same WGSL transforms, same parameters — from Node on desktop WebGPU (Google Dawn), and the app prints the command line that reproduces whatever it is currently simulating:

node scripts/bench.ts --preset schnak-spots --lmax 63 --backend webgpu --steps 2000 \
  --seed 1 --a 0.1 --b 0.9 --D1 0.0004 --D2 0.008 --dt 0.05

Copy it from under the stats line, run it, and compare the ms/step it reports with the app's. Both sides go through the one shared src/bench/runSpec.ts — the app formats a run into that command, the benchmark parses it back — so there is no second copy of the defaults for the two runs to drift apart on. Node runs the TypeScript sources directly, so src/ is literally the same code in both places, down to the device request in requestShtDevice() (Dawn is installed under navigator.gpu and the WebGPU globals, and the rest runs unchanged).

Desktop WebGPU comes from the optional webgpu package (prebuilt Dawn, ~70 MB). A plain npm install picks it up; npm install --omit=optional skips it and leaves --backend cpu working. Other flags: --steps, --warmup, --json, --help; DAWN_FLAGS='backend=vulkan' (;-separated) passes Dawn options through, e.g. to pick a backend or compare against Dawn's own software adapter.

What the comparison does and does not control for:

Tests#

A note on canvas resizing#

Early long runs killed the browser after ~700–800 steps. The cause was the colorbar's min/max labels changing width as their digit count changed, which reflowed the panel, fired the ResizeObserver, and called renderer.setSize() — reallocating the WebGL drawing buffer. Assigning canvas.width also blanks the canvas even when the value is unchanged, so the same bug caused visible flicker. Fixed by giving the colorbar column a fixed width and making SphereScene.resize() return early on no-op resizes.

Development#

npm install
npm run dev       # local dev server
npm run build     # type-check + production build to dist/

Deployed to GitHub Pages by .github/workflows/deploy.yml on push to main.

License#

CECILL-2.1 (inherited from SHTNS via shtns-webgpu, whose sources are vendored).

moveopenescclose