ml_artists · SarinWatercolor
GAN Watercolor (Helena Sarin — Neural Bricolage / #latentDoodles)
Inspired by Helena Sarin's Neural Bricolage practice (#latentDoodles, GAN Weaver, The Book of veGAN): she trains GANs on her own watercolor paintings and sketches, then curates the output back into analog paintings. We replicate the kernel: a small Sarin-style palette of 5-7 colours; per step, paint a few curved strokes whose colour is sampled (with mild jitter) from the palette; each stroke is composited with low alpha for true watercolor translucency; after each batch a 1D vertical Gaussian 'bleed' simulates pigment diffusion through paper; the whole canvas slowly fades toward paper-white so older strokes become ghostly underlayers. The output is a layered Sarin-flavoured watercolor, not a photoreal GAN image — the algorithmic structure is the same.
▶ 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.
Math essence
Stroke is a densely-sampled curved path through 3-5 control points; rasterised as a Gaussian-thickness mask onto a float canvas; composited with alpha α. Vertical bleed = 1-D Gaussian convolution along y (kernel σ). Global fade: canvas ← canvas · γ + (1-γ)·paper. Palette is a K-row RGB table (3 fixed Sarin palettes or a low-frequency HSV random walk).
Parameters
| Name | Type | Default | Range | Description |
|---|---|---|---|---|
dim | int | 600 | 200–1500 | Square canvas side length (px). |
n_palette | int | 6 | 2–16 | Number of palette colours K (Sarin: 5-7). |
palette_seed | int | 20240601 | 0–2147483647 | Seed for synthetic palette in 'random' mode. |
strokes_per_step | int | 4 | 1–40 | Strokes painted per step. |
alpha | float | 0.06 | 0.001–0.5 | Per-stroke opacity (low values = watercolor layering). |
bleed_sigma | float | 1.4 | 0–8 | Vertical bleed Gaussian sigma (0 disables). |
fade | float | 0.997 | 0.9–1 | Per-step multiplicative canvas fade. |
palette_name | str | sarin_warm | null–null | sarin_warm|sarin_cool|sarin_folk|random. |
Reference
First 60 lines of reference.py. Full source:
library_external/ml_artists/SarinWatercolor/reference.py
"""SarinWatercolor — Helena Sarin "GAN + watercolor" kernel.
Helena Sarin works at the seam of GANs and analog watercolor: she
trains generative models on her own watercolor paintings and sketches
("#latentDoodles", *An Eternal Tangle of What Ifs*, *GAN Weaver*),
then curates the output back into physical paintings. Her aesthetic
is a *low-frequency palette with watercolor bleed*: a small handful
of colours per piece, layered translucently, with the soft wet-blur
edges that come from pigment diffusion in paper.
We replicate the algorithmic kernel:
1. Maintain a small fixed palette of K colours (Sarin-style: 5-7).
2. At each step, pick a palette colour and stroke shape (a
parametric cubic spline or short curve segment with a few
control points).
3. Paint the stroke into a float canvas with low alpha so layers
accumulate translucently.
4. Apply a vertical "wet bleed" — a 1D Gaussian blur along the
y-axis — *after* each stroke, mimicking watercolor diffusion
through paper grain.
5. Slowly fade the whole canvas so newer strokes dominate while
older ones remain as ghostly underlayers.
References:
- https://www.artnome.com/news/2018/11/14/helena-sarin-why-bigger-isnt-always-better-with-gans-and-ai-art
- https://www.nvidia.com/en-us/research/ai-art-gallery/artists/helena-sarin/
- https://aiartists.org/helena-sarin
"""
from __future__ import annotations
from typing import ClassVar
import numpy as np
class SarinWatercolor:
"""Layered watercolor strokes sampled from a small Sarin-style palette.
Parameters
----------
dim : int
Square canvas side length (px).
n_palette : int
Number of palette colours K (Sarin typically uses 5-7).
palette_seed : int
Seed used to draw the synthetic palette from a low-frequency
random walk.
strokes_per_step : int
Strokes drawn per ``step`` call.
alpha : float
Per-stroke opacity (kept low for true watercolor layering).
bleed_sigma : float
Vertical bleed Gaussian sigma (px). 0 disables bleed.
fade : float
Per-step multiplicative canvas fade (close to 1.0 keeps old
strokes; lower lets newer ones dominate).
palette_name : str
``"sarin_warm"``, ``"sarin_cool"``, ``"sarin_folk"`` or
``"random"`` for a generated palette.
""" Try running it locally
The reference implementation is self-contained Python with numpy. Install the package and run:
python library_external/ml_artists/SarinWatercolor/reference.py Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.
More from ml artists
DeepDream Field (Mike Tyka — AI: DeepDream / Inceptionism)
ml_artists · TykaDeepDreamField
Inspired by Mike Tyka's AI: DeepDream series (2015-) and his co-authorship of 'Inceptionism: Going Deeper into Neural Networks' with Mordvintsev and Olah.
Glitch (Mario Klingemann — glitch-print aesthetic)
ml_artists · KlingemannGlitch
Klingemann's practice amplifies the visible failure modes of digital pipelines — distorted faces mid-decode, colour-shifted artefacts, rectangular datamosh tears — into the subject matter itself.
Memories of Passersby (Mario Klingemann — Memories of Passersby I)
ml_artists · KlingemannMemorex
Inspired by Mario Klingemann's Memories of Passersby I (2018, Sotheby's) — a custom GAN trained on 17th–19th-century portraits that runs forever, never repeats an output, and 'observes itself' in a feedback loop.
Neural Biodiversity (Sofia Crespo — Neural Zoo / Artificial Natural History)
ml_artists · CrespoNeuralBiodiversity
Inspired by Sofia Crespo's Neural Zoo (2018-), Artificial Natural History (2019-), and Beneath the Neural Waves (2021): she uses 3D GANs, CNN-based texture synthesis and 3D style transfer to fabricate a 'natural history book that never was' — chimerical creatures whose anatomies recombine fragments of real species.
Style Transfer Field (Gene Kogan — ml4a)
ml_artists · KoganStyleTransferField
Inspired by Gene Kogan's Experiments with Style Transfer and the ml4a curriculum: neural style transfer iteratively updates an image so its Gram-matrix feature statistics match a style reference — the gradient of the style loss with respect to pixels defines a vector field.