Visualizations
024 / 500

024 · Generative art

Painterly Rendering

A harbor at dusk repainted coarse to fine in thousands of curved, bristly strokes.

A procedurally drawn harbor at sunset is the reference, and Aaron Hertzmann's 1998 painterly rendering algorithm repaints it on a toned canvas. For each brush size, from big to small, it blurs the reference, walks a grid over the painting, and starts a stroke wherever the painting so far differs too much from the target. Each stroke grows step by step perpendicular to the image gradient, so it runs along edges, and stops when the color under it drifts from its own, which is why small brushes only land where detail is missing. Strokes are rendered with bristle tracks and drawn into a height map that is lit for impasto relief, and the four styles are Hertzmann's own parameter sets.

Try it. Pick a style (or press 1 to 4) to repaint from the ground up. Drag to scrape paint off with a palette knife and the painter repairs the patch, coarse to fine. Hold Space or toggle Reference to see the source picture, and press N for a new harbor.

  • Hertzmann multi-layer painting
  • Gradient-following spline strokes
  • Bristle stroke rendering
  • Height-map impasto lighting
  • Procedural scene drawing

View the source · one module, plus a small shared runtime for sizing, the animation loop and input

Build your own

Paste this into Claude Code, Codex or any coding agent to get a simple version running, then take it wherever you like.

Build a painterly renderer with JavaScript and the HTML canvas element, following Aaron Hertzmann's 1998 paper "Painterly Rendering with Curved Brush Strokes of Multiple Sizes". Put everything in a single index.html file with no libraries or build step, so I can open it directly in a browser.

Start simple:
- Draw a reference picture into an offscreen canvas about 480 pixels wide using plain canvas calls: a sunset sky gradient, a sun, a dark hill, a sea with a few bright horizontal glints. Read its pixels with getImageData.
- Make the visible canvas the same size, filled with a warm brown ground color.
- Paint in layers with brush radii 8, 4 and 2. For each radius, blur the reference (a few passes of a box blur work) and compute its luminance gradient with a Sobel filter.
- Walk a grid with spacing equal to the radius. In each grid cell, average the color difference between the painting so far (read it back with getImageData once per layer) and the blurred reference. If it is above a threshold, start a stroke at the cell's worst pixel.
- A stroke starts with the reference color at that point and grows up to 16 steps of length R, each step perpendicular to the gradient, flipping direction if needed so it does not double back. After 4 steps, stop when the reference under the stroke is closer to the painting than to the stroke's color.
- Shuffle each layer's strokes and draw them as round-capped polylines with lineWidth 2R. Animate a few hundred strokes per frame so you can watch the painting build up.

Once that works, make it beautiful:
- Smooth each stroke's points with a Catmull-Rom spline.
- Add bristle texture: draw several thin offset lines along each stroke, each slightly lighter or darker.
- Add the paper's style parameters (threshold, curvature filter, opacity, color jitter) and buttons for Impressionist and Expressionist presets.

Explain the key ideas in short code comments. When you're done, tell me how to open it and suggest three directions I could take it next, such as impasto lighting from a height map of the strokes, a pointillist style made of dots, or letting the mouse scrape paint off so the algorithm repairs it.
PreviousHopf FibrationThe 3-sphere as linked circles, projected into nested tori of pastel rings. NextWormholeFly through a traversable wormhole into another universe, with real light bending.

Related visualizations

  • Plotter LandscapeGenerative art A mountain range inked stroke by stroke, with exact floating-horizon hidden lines.
  • Ink Landscape ScrollGenerative art An endless ink handscroll of misty peaks, pines, pavilions and boats, unrolling.
  • Sumi-e BrushGenerative art A bristle-by-bristle ink brush paints bamboo on rice paper that wicks and bleeds.
  • KaleidoscopeGenerative art Draw once, and twelve mirrored strokes bloom into a mandala.
  • Evolved TrianglesGenerative art Two hundred translucent triangles evolve from chaos into a painted landscape.
  • Paper MarblingGenerative art Ink drops, combs and styluses on a size bath, then a print pulled onto paper.
  • Op ArtGenerative art Riley and Vasarely style compositions that ripple, bulge and vibrate.
  • Pen PlotterGenerative art A SCARA plotter arm inks hatched, hidden-line drawings along a 2-opt optimized route.
  • WatercolorGenerative art Pigment rides a shallow-water layer, pools at the edges and granulates on paper.

Use ← and → to move between demos. While the canvas has focus, keys go to the demo instead.

← More from Emergent Mind Labs