Skip to content
Sample of Perlin Streamlines

reas_processing · perlinStreamlines

Perlin Streamlines

A 2-D vector field is sampled from one or more octaves of Perlin gradient noise. Particles emit at uniformly random positions and advance along the field one step per tick. Their trails are accumulated on the canvas with a low-alpha blend so the result is the classic painted-noise flow-field visualization popularised by Casey Reas's Processing community. Particles die after particle_lifespan ticks and respawn.

Original: https://en.wikipedia.org/wiki/Perlin_noise

▶ 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
reas_processing
Source
reas · processing
Evolution
flow_field
Render
trail_accumulation
Complexity
medium
Agents
6000
Year
1985
Author
Ken Perlin (noise); Reas / Form+Code (creative-coding flow-field canon)

Math essence

For each particle (x, y) compute theta = 2*pi * perlin(x*scale, y*scale) (summed across octaves). Position update: (x', y') = (x, y) + cos(theta), sin(theta) * noise_speed. Per-pixel trail blend: canvas = (1-a)*canvas + a*ink on the segment endpoints.

Parameters

Name Type Default Range Description
dim int 500 200–1200 Square canvas side length.
num_particles int 6000 100–20000 Number of tracer particles.
field_scale float 0.012 0.003–0.08 Spatial frequency of the Perlin noise.
noise_speed float 1.6 0.2–6 Particle velocity (pixels per step).
octaves int 2 1–4 Number of Perlin octaves summed into the angle.
particle_lifespan int 90 10–400 Ticks before a particle respawns at a new random position.
line_alpha float 0.06 0.01–0.5 Trail opacity per stamp.
bg_color tuple 15,18,30 —
ink_color tuple 210,230,255 —
seed int null 0–999999

Reference

First 60 lines of reference.py. Full source: library_external/reas_processing/perlinStreamlines/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


def _fade(t: np.ndarray) -> np.ndarray:
    return t * t * t * (t * (t * 6 - 15) + 10)


def _perlin2(rng: np.random.Generator, grid_w: int, grid_h: int):
    perm = np.arange(256, dtype=np.int32)
    rng.shuffle(perm)
    perm = np.concatenate([perm, perm])
    angles = rng.uniform(0.0, 2.0 * np.pi, size=(grid_h + 1, grid_w + 1))
    grads = np.stack([np.cos(angles), np.sin(angles)], axis=-1).astype(np.float32)
    return perm, grads


def perlin(x: np.ndarray, y: np.ndarray, perm: np.ndarray, grads: np.ndarray) -> np.ndarray:
    gw = grads.shape[1] - 1
    gh = grads.shape[0] - 1
    x = np.mod(x, gw)
    y = np.mod(y, gh)
    xi = x.astype(np.int32)
    yi = y.astype(np.int32)
    xf = x - xi
    yf = y - yi
    u = _fade(xf)
    v = _fade(yf)
    x1 = np.clip(xi + 1, 0, gw)
    y1 = np.clip(yi + 1, 0, gh)
    g00 = grads[yi, xi]
    g10 = grads[yi, x1]
    g01 = grads[y1, xi]
    g11 = grads[y1, x1]
    d00 = xf * g00[..., 0] + yf * g00[..., 1]
    d10 = (xf - 1) * g10[..., 0] + yf * g10[..., 1]
    d01 = xf * g01[..., 0] + (yf - 1) * g01[..., 1]
    d11 = (xf - 1) * g11[..., 0] + (yf - 1) * g11[..., 1]
    nx0 = d00 + u * (d10 - d00)
    nx1 = d01 + u * (d11 - d01)
    return nx0 + v * (nx1 - nx0)


class PerlinStreamlines:
    """Streamlines in a Perlin noise field.

    A 2-D vector field is constructed from the gradient of one or two
    octaves of Perlin noise. Particles emit from random positions and
    follow the field, depositing semi-transparent trails. step(n)
    advances the simulation and accumulates onto the canvas. The
    rendering is the canonical 'flow field' popularised by Reas and the
    Processing community.
    """

    family: ClassVar[str] = "reas_processing"
    sketch: ClassVar[str] = "perlinStreamlines"

Try running it locally

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

python library_external/reas_processing/perlinStreamlines/reference.py

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

More from reas · processing

See all reas_processing artworks →