Skip to content
Sample of Hobbs Watercolor Edge (Tyler Hobbs — Soft Textures / Watercolor)

art_blocks · HobbsWatercolorEdge

Hobbs Watercolor Edge (Tyler Hobbs — Soft Textures / Watercolor)

An implementation of Tyler Hobbs' 2017 'watercolor' technique: a recursive polygon-deformation algorithm whose midpoints are jittered by Gaussian noise across many passes, then stacked with low alpha (4% per layer, 30–100 layers total) to produce the soft, ink-bleed look of wet pigment on paper. Hobbs describes the technique explicitly in his essay: 'The core of the watercolor technique is a recursive polygon deformation algorithm ... stack lots of nearly transparent layers on top of each other.' Here we condense the pipeline to one numpy pass per layer: a deformed substrate is blurred, Sobel-edge-detected to extract the ink-bleed lines, tinted with a per-layer colour from a curated palette, and alpha-stamped onto the canvas. The cumulative effect is Hobbs' signature watercolor-edge aesthetic — visible in his Isohedral, Linear II and subsequent series, and the foundation for the soft textures in *Fidenza* and *QQL*.

Original: https://www.tylerxhobbs.com/words/a-guide-to-simulating-watercolor-paint-with-generative-art

▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.

Family
art_blocks
Source
art blocks
Evolution
geometry
Render
stacked_translucent_edges
Complexity
medium
Agents
1
Year
2017
Author
Tyler Hobbs

Math essence

For each layer L: (1) build a deformed copy of the base polygon P via D iterations of midpoint Gaussian jitter (sigma = jitter_frac * segment_length); (2) rasterise the deformed polygon to a mask; (3) blur the mask with a Gaussian of sigma blur_sigma px; (4) compute the Sobel magnitude of the luminance and threshold by edge_threshold; (5) tint the thresholded edge by the layer's palette colour; (6) alpha-blend onto the canvas with edge_alpha. Stack num_layers such layers. The Sobel magnitude effectively extracts the polygon boundary as a 1-px ink line.

Parameters

Name Type Default Range Description
dim int 512 200–1500 Square canvas side length (px).
num_layers int 24 4–120 Number of translucent watercolor layers to stack (Hobbs: 30–100).
base_shape str polygon null–null polygon|noise — substrate generator.
base_sides int 5 3–12 Sides on the base polygon (3 = triangle, 6 = hexagon).
deformation_depth int 5 2–8 Polygon deformation recursion depth (Hobbs: 4–7).
jitter_sigma float 0.3 0.05–0.6 Gaussian jitter sigma as a fraction of segment length.
blur_sigma float 4 0.5–16 Per-pass Gaussian blur sigma in pixels.
edge_threshold float 0.1 0.01–0.6 Sobel-magnitude threshold for ink edges.
edge_alpha float 0.35 0.05–0.95 Per-edge alpha when stamping.
blur_alpha float 0.04 0.005–0.3 Per-layer soft-fill alpha (Hobbs: ~0.04).
palette list 60,95,145,185,70,60,210,155,70,80,130,90,115,60,130 null–null Per-layer colour tints sampled from this palette.
background tuple 252,250,244 null–null Paper-white background RGB.

Reference

First 60 lines of reference.py. Full source: library_external/art_blocks/HobbsWatercolorEdge/reference.py

"""HobbsWatercolorEdge — Tyler Hobbs' watercolor "soft-edge" stack.

Inspired by Tyler Hobbs' *A Guide to Simulating Watercolor Paint with
Generative Art* (2017) and his subsequent watercolor series.  Hobbs'
recipe, quoted directly from his essay:

    "The technique to simulate watercolor in generative art consists of
    stacking lots of nearly transparent layers on top of each other.
    To create the watercolor-like appearance of a gradual fade out,
    the layers need to have a lot of variation in their shape.

    The core of the watercolor technique is a recursive polygon
    deformation algorithm:
        1. For each line A -> C in the polygon, find the midpoint B.
           From a Gaussian distribution centred on B, pick a new point B'.
        2. Update the polygon, replacing A -> C with two lines A -> B'
           and B' -> C.
        3. If we haven't hit our max recursion depth, repeat from
           step 1, splitting the child lines."

Hobbs runs the input through ~7 deformation rounds to make the "base
polygon", then for every layer (30–100 layers total) he starts from
that base and runs another 4–5 rounds, drawing each with ~4% alpha.
The result is the soft, ink-bleeding look of wet pigment on paper.

This implementation condenses Hobbs' "many translucent layers" recipe
into a single, fast numpy pipeline that captures the essential
visual signature:

    1. Build a base noise-driven substrate (Hobbs' underlying form —
       in his essay a polygon, here a Perlin-noise tinted field).
    2. Apply a sequence of small "translucent layer" passes: each pass
       is a deformed copy of the substrate plus a Gaussian blur and
       a Sobel edge detector to extract the ink-bleed lines, then
       alpha-blended onto the canvas.
    3. The cumulative effect after `num_layers` passes is the
       characteristic watercolor edge — soft, broken, organic.

Reference:
- https://www.tylerxhobbs.com/words/a-guide-to-simulating-watercolor-paint-with-generative-art
- https://www.tylerxhobbs.com/words/creating-soft-textures-generatively
"""
from __future__ import annotations

from typing import ClassVar

import numpy as np


# ---------------------------------------------------------------------------
# Local helpers: 2D convolution, Gaussian kernel, Sobel edge detector
# ---------------------------------------------------------------------------
def _gaussian_kernel(size: int, sigma: float) -> np.ndarray:
    """Return a normalised (size x size) Gaussian kernel."""
    ax = np.arange(size) - (size - 1) / 2.0
    xx, yy = np.meshgrid(ax, ax)
    g = np.exp(-(xx * xx + yy * yy) / (2.0 * sigma * sigma))
    return (g / g.sum()).astype(np.float32)

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/art_blocks/HobbsWatercolorEdge/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from art blocks

Sample of Cherniak Ringers (Dmitri Cherniak — Ringers)

Cherniak Ringers (Dmitri Cherniak — Ringers)

art_blocks · CherniakRingers

An implementation of Dmitri Cherniak's *Ringers* algorithm (Art Blocks Curated, January 2021, 1,000 NFTs) — one of the most-cited generative-art projects of the NFT era, and the source of *Ringers #879* ('The Goose') and *Ringers #962* (acquired by LACMA).

View →
Sample of Chromie Squiggle (Snowfro / Cherniak — Squiggles)

Chromie Squiggle (Snowfro / Cherniak — Squiggles)

art_blocks · CherniakSquiggles

An implementation of the squiggle-generation kernel used in Snowfro's *Chromie Squiggle* (Art Blocks #0, November 2020) — the on-chain generative-art project that launched the Art Blocks platform itself and was the work Dmitri Cherniak cited (alongside *Genesis*) as his inspiration for Ringers.

View →
Sample of Genesis (DCA — Day-Zero Art Blocks)

Genesis (DCA — Day-Zero Art Blocks)

art_blocks · DcaGenesis

An interpretation of DCA's *Genesis* (Art Blocks #1, November 2020) — one of three Day-Zero collections (alongside Snowfro's Chromie Squiggle and Jeff Davis's Construction Token) that minted in the first 24 hours of the Art Blocks platform.

View →
Sample of Hobbs Flow Field (Tyler Hobbs — Incomplete Control / Fidenza)

Hobbs Flow Field (Tyler Hobbs — Incomplete Control / Fidenza)

art_blocks · HobbsFlowField

The canonical perlin-noise flow-field algorithm described by Tyler Hobbs in his 2020 essay 'Flow Fields', and the algorithmic backbone of his Art Blocks Curated works *Incomplete Control* (Bright Moments, 2021) and *Fidenza* (Art Blocks, 2021).

View →
Sample of Hobbs Watercolor Blob

Hobbs Watercolor Blob

art_blocks · HobbsWatercolorBlob

Layered translucent Gaussian blobs with watercolor-style alpha falloff.

View →
Sample of Penne 80s Vector (Aaron Penne — outrun perspective wireframe)

Penne 80s Vector (Aaron Penne — outrun perspective wireframe)

art_blocks · Penne80sVector

The 80s-synthwave outrun grid, the visual aesthetic Aaron Penne references in his generative-art practice (notably in *Apparitions* and his broader body of work).

View →

See all art_blocks artworks →