Skip to content
Sample of Daily Sketch Curve Morph (Synthetic Gesture)

audio_reactive · LiebermanDailySketch

Daily Sketch Curve Morph (Synthetic Gesture)

A parametric polar / hypotrochoid curve whose three radii (R, r, d) are continuously morphed by independent sin-wave chains — Zach Lieberman's signature 'use mathematical functions like sin() and tan() in place of randomness' rule from his Daily Sketches project. The trail canvas rotates the line each step to evoke the AR / rotation experiments from his openFrameworks sketches (notably the *Audio in Space* ARKit series). Each chain is its own frequency band, so the curve 'wiggles in three different ways at once' — one of his signature compositional moves.

Original: http://zach.li/

▶ 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
audio_reactive
Source
audio-reactive
Evolution
temporal
Render
trail_accumulation
Complexity
low
Agents
600
Year
2024
Author
Zach Lieberman (openFrameworks co-creator, MIT Media Lab, Future Sketches / SFPC)

Math essence

theta = linspace(0, 12π, N). R, r, d each = base + Σ amp_k * sin(2π freq_k * t + phase_k) for their own chain. x = (R + r*cos(3θ))*cos(θ); y = (R + r*cos(3θ))*sin(θ) + d*sin(7θ + 0.7). Rotate (x, y) by _rot about centre, rasterize to canvas. canvas *= trail_decay each step; ink = line_color * stroke_alpha, with 1-pixel halo to either side.

Parameters

Name Type Default Range Description
dim int 400 200–800 Square canvas side length in pixels.
num_points int 600 200–2000 Number of curve samples per frame.
base_R float 0.42 0.05–0.95 Outer radius base.
base_r float 0.18 0.01–0.6 Inner radius base.
base_d float 0.32 0.01–0.9 Vertical warp base.
R_freq tuple 0.21,0.43,0.91 — Sin-chain frequencies for the outer radius.
r_freq tuple 0.31,0.83,1.7 — Sin-chain frequencies for the inner radius.
d_freq tuple 0.11,0.67,2.3 — Sin-chain frequencies for the vertical warp.
R_amp tuple 0.05,0.04,0.03 — Sin-chain amplitudes for the outer radius.
r_amp tuple 0.06,0.05,0.04 — Sin-chain amplitudes for the inner radius.
d_amp tuple 0.08,0.06,0.05 — Sin-chain amplitudes for the vertical warp.
morph_speed float 1 0.05–4 Time multiplier for the sin-chain t.
rotation_speed float 0.18 -0.5–0.5 Per-step canvas rotation (radians).
line_color tuple 245,240,230 — Ink colour.
bg_color tuple 12,12,18 — Background colour.
trail_decay float 0.96 0.7–0.999 Per-step multiplicative fade of the canvas.
stroke_alpha float 0.65 0.05–1 Per-pixel ink intensity.
seed int null 0–999999 RNG seed (initial sin-chain phases).

Reference

First 60 lines of reference.py. Full source: library_external/audio_reactive/LiebermanDailySketch/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


class LiebermanDailySketch:
    """Lieberman-style daily-sketch aesthetic: a parametric curve
    whose parameters morph continuously under a synthetic "gesture"
    signal (a chain of sin waves at different frequencies, mimicking
    mouse / pen motion).

    Implements a polar-rose / hypotrochoid family whose radii
    (R, r, d) and phase offsets are driven by independent sin
    chains. Each chain has its own frequency band — so the curve
    "wiggles in three different ways at once" (one of Zach
    Lieberman's signature rules from his *Daily Sketches* project:
    "use mathematical functions like sin() and tan() in place of
    randomness"). The trail canvas rotates the line each step to
    evoke the AR / rotation experiments from his openFrameworks
    sketches (notably the *Audio in Space* ARKit series).
    """

    family: ClassVar[str] = "audio_reactive"
    sketch: ClassVar[str] = "liebermandailysketch"

    def __init__(
        self,
        dim: int = 400,
        num_points: int = 600,
        base_R: float = 0.42,
        base_r: float = 0.18,
        base_d: float = 0.32,
        R_freq: tuple[float, ...] = (0.21, 0.43, 0.91),
        r_freq: tuple[float, ...] = (0.31, 0.83, 1.7),
        d_freq: tuple[float, ...] = (0.11, 0.67, 2.3),
        R_amp: tuple[float, ...] = (0.05, 0.04, 0.03),
        r_amp: tuple[float, ...] = (0.06, 0.05, 0.04),
        d_amp: tuple[float, ...] = (0.08, 0.06, 0.05),
        morph_speed: float = 1.0,
        rotation_speed: float = 0.18,
        line_color: tuple[int, int, int] = (245, 240, 230),
        bg_color: tuple[int, int, int] = (12, 12, 18),
        trail_decay: float = 0.96,
        stroke_alpha: float = 0.65,
        seed: int | None = None,
    ) -> None:
        self.dim = dim
        self.num_points = num_points
        self.base_R = base_R
        self.base_r = base_r
        self.base_d = base_d
        self.R_freq = np.array(R_freq, dtype=np.float32)
        self.r_freq = np.array(r_freq, dtype=np.float32)
        self.d_freq = np.array(d_freq, dtype=np.float32)
        self.R_amp = np.array(R_amp, dtype=np.float32)
        self.r_amp = np.array(r_amp, dtype=np.float32)
        self.d_amp = np.array(d_amp, dtype=np.float32)
        self.morph_speed = morph_speed

Try running it locally

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

python library_external/audio_reactive/LiebermanDailySketch/reference.py

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

More from audio-reactive

See all audio_reactive artworks →