fieldplay3D

Vector field
A 3D take on anvaka/fieldplay. Particles are integrated in a WebGPU compute shader; trails are an HDR frame buffer that fades.

How it works

Every particle asks your field for its velocity, takes a step, and leaves a trail. The field is a WGSL function:

fn get_velocity(p: vec3f) -> vec3f {
  return vec3f(-p.z, 0.0, p.x);   // orbit the y axis
}

Optionally colour them yourself (choose Colour by → get_color()):

fn get_color(p: vec3f, v: vec3f) -> vec3f {
  return turbo(length(v) * 0.5);
}

Uniforms

u.timesimulation time (sum of time steps)
u.frameframe counter
u.cursorthe mouse, projected onto the plane through the orbit centre that faces the camera
u.cursorDown1.0 while a mouse button is held
u.boundsMin u.boundsMaxthe box particles live in

Helpers

noise(p)gradient noise, about −1…1
noised(p)vec4f: value and its gradient
fbm(p)four octaves of noise
curl(p)divergence-free noise — smoke and swirls
hash3(p) hash1(p)random per integer cell
rotateX(p, a) …Y …Zrotate a vector
turbo(t) viridis(t) hsv2rgb(c) palette(t, k)colour maps
PI TAUconstants

WGSL in one breath

let is immutable, var mutable. Types are inferred: let r = length(p);. Vectors are vec3f(x, y, z) or vec3f(0.0), swizzles work (p.xz). Write floats with a point (2.0) — a bare var a = 2; is an integer. Functions may be declared in any order. Ternaries are select(ifFalse, ifTrue, cond).

Controls

dragorbit
right-drag / shift-drag / two fingerspan
wheel / pinchzoom
spacepause
Rreset particles
Hhide the panel
Ffullscreen
ctrl+entercompile now

The link button copies a URL that holds the whole state — field, settings and camera.

WebGPU is not available

This page runs its particles in compute shaders, which need WebGPU. It works in Chrome and Edge 113+ on Windows, macOS and ChromeOS, Chrome 121+ on Android, and Safari 26 / Firefox 141+.

If you are on a supported browser, check that hardware acceleration is on (chrome://settings/system) and that the page is served over http://localhost or https.