Building MUJŌ: WebGPU Watercolor Engine
A physically-based Japanese watercolor and sumi-e simulation running entirely on WebGPU compute shaders — Kubelka-Munk optics, fluid dynamics, 3D elastic bristle physics, and Zen impermanence.
MUJŌ (無常) started as a question: can you make a digital brush that feels like real sumi ink on handmade washi paper? Not "looks like" in a screenshot — feels like under your hand. The answer required going deep: Navier-Stokes fluid mechanics for the ink flow, 2-flux Kubelka-Munk spectral radiative transfer for physically correct pigment mixing, Darcy tensor porous flow through virtual paper fibers, and 48 elastic guide bristle rods simulated via Position-Based Dynamics entirely in WGSL compute shaders.
No Three.js. No React. No physics libraries. Every simulation pass — from the Jacobi pressure solver to the coffee-ring evaporation pinning — is a custom WGSL compute shader running on the GPU. The result: a meditative painting canvas with five authentic Nihonga mineral pigments, three Japanese brush types, three washi paper varietals, and a procedural sound engine that responds to your brush pressure.
This thread tracks the engineering from first ink to artifact elimination, and the Atelier testbed that proves the physics hold up.
Timeline
Built a complete WebGPU watercolor simulation engine in a single session. MUJŌ (無常) — a meditative Japanese sumi-e painting canvas running entirely on GPU compute shaders. No Three.js, no React, no external physics libraries. Custom Navier-Stokes fluid solver, 2-flux Kubelka-Munk spectral color mixing, and Darcy porous flow through virtual paper fibers. Five authentic Nihonga mineral pigments — sumi carbon black, cinnabar vermilion, fermented indigo, yellow ochre, malachite verdigris — each with real spectral absorption/scattering tables. The ink bleeds through virtual Kozo mulberry fibers and darkens along evaporating edges. 1024×1024 simulation grid running at interactive framerates on M4 Pro.
Replaced the simple swept-capsule brush with 48 Position-Based Dynamics elastic guide rods running entirely in WGSL compute shaders. Each bristle has distance constraints, bending rigidity, Coulomb friction against the paper surface, and capillary clumping when wet. The brush fans out under pressure, pinches on turnarounds, and flicks on lift-off with real inertia. Three brush types: Maru-fude (round calligraphy), Menso (fine liner), and Hake (broad wash). The difference is visceral — strokes went from "digital stamp" to "I can feel the paper" in one refactor. Also added a procedural Web Audio soundscape: bamboo water drops, singing bowls, and paper friction acoustics that respond to brush pressure.
Spent two days hunting and killing every artifact that made the strokes look digital instead of organic. The "caterpillar beads" — visible blob spacing along fast strokes — were eliminated by switching to Catmull-Rom C1 spline interpolation with lag-by-one future-point basis. The "reed-screen grating" came from frame-boundary discontinuities in bristle injection, fixed by continuous sub-frame interpolation. Then the needle artifact at stroke ends: a kinematic speed/pressure gate smoothly tapers the contact area to zero. Finally, added fude-ashi bristle fringes and tooth gating so even straight lines have the organic irregularity of real ink on handmade paper. The Shisho benchmark suite (永 永字八法, 一 ichi, 心 kokoro, 円 ensō) now produces strokes that would fool a calligrapher at thumbnail scale.
Built a companion scientific testbed — MUJŌ Atelier — with 5 standalone lab experiments. Each experiment isolates and compares one physics layer: Darcy tensor diffusion vs. Lattice Boltzmann D2Q9 fluid bleed, single-layer Kubelka-Munk vs. Saunderson 3-layer glaze stacks, 6 botanical washi substrates with different fiber structures, and an M4 Pro hardware profiler measuring per-pass GPU timings. The Atelier is the "show your work" for the simulation — same ethos as the Gemma 4 benchmark suite, different domain. If someone asks "how does your Kubelka-Munk compare to LBM?" the answer is a side-by-side live experiment, not a paragraph.