at play · case study

Impasto

Real oil paint in the browser — strokes with thickness and light that mix like pigment, not pixels. Painting, style journeys after the masters, and a public gallery with no accounts, ever.

A dense, colorful field of thick painted strokes made in Impasto
open impastoimpasto.gallery · free · runs in the browser, best on Chrome/Android
role
design & code · paint engine
stack
WebGPU / WGSL · vanilla JS · Cloudflare Pages + KV
medium
oil brush · palette knife · blender · 5 style journeys
status
live — free, no accounts, works on phones
2render targets ping-ponged per stroke — color and paint thickness
5style journeys where the brush itself enforces the style
3input models: stylus pressure, speed dynamics, plain touch
0accounts — paintings stay on the device unless a parent publishes

Paint here — this frame is the live engine

Not a video. The frame below runs the real WebGPU engine: drag slowly to load the canvas with thick paint, flick fast to graze it, drag one color through another and watch the pigment mix. Needs WebGPU — Chrome or Edge on desktop, recent Android Chrome; elsewhere the frame explains itself.

Paint that behaves like paint

Two ping-ponged render targets: rgba16-float color and r16-float thickness. Every stamp samples the frame beneath it, so paint smears and mixes per-channel in Kubelka-Munk fashion — the way pigment behaves, not the way RGB averages. Lighting derives from the thickness buffer: a loaded stroke literally stands on the canvas and catches light on its ridges.

Pressure without a pen

Fingers and mice have no pressure, so stroke speed stands in: a slow drag presses a full brush into the canvas, a fast flick barely grazes it. A real stylus keeps its true pressure, untouched.

Styles as constraints

Choose Seurat and the brush splits color into divisionist dots; Van Gogh bends every stroke along a flow field; Cézanne clips them to facets. The free tools disappear — the brush itself is the lesson.

A black screen named meta

One WGSL struct field named meta — a reserved word — silently invalidated every shader: black canvas, zero errors. The fix was a rename; the lesson is permanent — the engine now checks every module with getCompilationInfo and fails loud instead of dark.

A gallery with no accounts

A consent checkbox, a per-IP daily cap, and a human review before anything becomes public. Painters show under invented names — GoldenLynx73, never a real one — each with a permanent page at impasto.gallery/<name>. Full-resolution paintings never leave the device.

open impastoimpasto.gallery · free · runs in the browser, best on Chrome/Android