Skip to content
Sample of Text Rain (Utterback & Achituv)

generative_pioneers_2 · utterback_text_rain

Text Rain (Utterback & Achituv)

Camille Utterback and Romy Achituv's 'Text Rain' (1999) is one of the canonical interactive installations of the late-1990s computer-vision era, and the piece that won them a US patent (US 6,928,313) in 2005. Visitors stand in front of a camera; their silhouette is thresholded into a binary mask and projected back as a dark figure on the screen. Coloured letters of Evan Zimroth's poem 'Talk, You' fall from the top of the screen, and they stop wherever they intersect the silhouette — letters 'land' on the body and pile up like snow on a roof. When the visitor moves, the letters that had collected resume falling. In this reference the silhouette is synthesised (an array of Gaussian blobs in the lower 70% of the canvas) so the piece is reproducible without a webcam; the optical-flow field is the gradient of that silhouette, and each character steps in a per-cell lattice under gravity + flow + a small noise term until it lands on a high-silhouette cell or a cell already occupied by another letter.

Original: https://camilleutterback.com/projects/text-rain/

▶ 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
generative_pioneers_2
Source
generative_pioneers_2
Evolution
particle_system_with_flow
Render
ascii_glyphs_on_silhouette
Complexity
high
Agents
1100
Year
1999
Author
Camille Utterback and Romy Achituv

Math essence

Let S : [0,1]² → [0,1] be the silhouette image. The 2D optical flow field is f = (∇ₓ S, ∇ᵧ S). On a coarse cells×cells lattice each character c at (i, j) advances by (Δi, Δj) = (g + f_x(i,j)·strength + ε_x, v_flow_y(i,j)·strength + ε_y). When the local S exceeds a threshold, the character settles in that cell. The cells form a hard-occupancy grid, so no two characters can share a cell. The piece reads as a Poisson-disc-like fall: characters never stop in mid-air, and they pile up only on the silhouette.

Parameters

Name Type Default Range Description
dim int 512 200–1200 Square canvas side length
cell int 14 6–32 Lattice cell size in pixels
gravity float 0.6 0–2 Per-step vertical fall speed (cells)
obstacle_bias float 0.55 0–1 Probability that a character lands on an obstacle cell when it crosses one
flow_strength float 0.5 0–2 Strength of the optical-flow push (per step)
silhouette_threshold float 0.4 0–1 Silhouette threshold above which a cell is 'dark' enough to catch a letter
silhouette_seed int 99 0–999999 Seed for the synthesized silhouette
font_size int 13 6–32 Glyph size (px)
bg_color tuple 245,245,240 null–null Background RGB
seed int null 0–999999 RNG seed for the character alphabet + initial jitter

Reference

First 60 lines of reference.py. Full source: library_external/generative_pioneers_2/utterback_text_rain/reference.py

from __future__ import annotations

from typing import ClassVar

import imageio.v2 as imageio
import numpy as np


class UtterbackTextRain:
    family: ClassVar[str] = "generative_pioneers_2"
    sketch: ClassVar[str] = "utterback_text_rain"

    POEM_LINES = (
        "TALK YOU AND I",
        "EVERY WALK IS A TALK",
        "EVERY BODY MAKES A SOUND",
        "EVERY WALK A BODY TALKS",
        "I HEAR YOUR BODY TALK",
        "BODIES HEAR BODIES",
    )

    PALETTE = (
        (220, 60, 60),
        (240, 200, 60),
        (60, 140, 230),
        (90, 220, 130),
        (220, 110, 220),
        (240, 140, 80),
    )

    def __init__(
        self,
        dim: int = 512,
        cell: int = 14,
        gravity: float = 0.6,
        obstacle_bias: float = 0.55,
        flow_strength: float = 0.5,
        silhouette_threshold: float = 0.4,
        silhouette_seed: int = 99,
        font_size: int = 13,
        bg_color: tuple[int, int, int] = (245, 245, 240),
        seed: int | None = None,
    ) -> None:
        self.dim = dim
        self.cell = cell
        self.gravity = gravity
        self.obstacle_bias = obstacle_bias
        self.flow_strength = flow_strength
        self.silhouette_threshold = silhouette_threshold
        self._silhouette_seed = silhouette_seed
        self.font_size = font_size
        self.bg_color = np.array(bg_color, dtype=np.uint8)
        self._seed = seed
        self.rng = np.random.default_rng(seed)
        self.canvas = np.full((dim, dim, 3), self.bg_color, dtype=np.uint8)
        self._silhouette = self._build_silhouette()
        self._flow = self._build_flow()
        self._chars: list[dict] = []
        self._init_chars()
        self.time = 0

Try running it locally

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

python library_external/generative_pioneers_2/utterback_text_rain/reference.py

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

More from generative_pioneers_2

Sample of Computer-Aided Painting (Schwartz)

Computer-Aided Painting (Schwartz)

generative_pioneers_2 · lillian_schwartz

Lillian Schwartz, who worked at Bell Labs from 1968 to 2001 with Kenneth C.

View →
Sample of Hypertransformations (Molnár)

Hypertransformations (Molnár)

generative_pioneers_2 · molnar_hypertransformations

Vera Molnár's 'Hypertransformations' (1974-1976), executed on the IBM 360 with a CRT 2250 display and output on a Benson plotter.

View →
Sample of P-159 D (Mohr)

P-159 D (Mohr)

generative_pioneers_2 · mohr_p159d

Manfred Mohr's 'P-159 D' (1969, Vincennes) sits in the P-159 family that the artist developed from his earlier P-021 / P-036 line-arrangement programs.

View →
Sample of Raster Sequence (Nake)

Raster Sequence (Nake)

generative_pioneers_2 · nake_raster_sequence

Frieder Nake's 'Walk-through-Raster' / 'Raster Sequence' (1966) is the first non-stationary Markov chain in computer art history.

View →
Sample of Structure Computer (Cohen / AARON)

Structure Computer (Cohen / AARON)

generative_pioneers_2 · cohen_structure_computer

Harold Cohen's AARON, begun at UCSD in 1967-1973 and continuously developed until Cohen's death in 2016, is the longest-running art-making AI in history.

View →
Sample of Wall Drawings (LeWitt)

Wall Drawings (LeWitt)

generative_pioneers_2 · sol_lewitt_wall_drawings

Sol LeWitt's wall drawings (1968-2007) are the canonical example of conceptual-art-as-algorithm: the work is the set of written instructions, and any number of draftspeople can execute them with different results that all satisfy the same rule.

View →

See all generative_pioneers_2 artworks →