Skip to content
Sample of Penne Ringers (Aaron Penne concentric ring extrusion)

hobbs_cherniak_penne_deep · PenneRingers

Penne Ringers (Aaron Penne concentric ring extrusion)

Penne's take on the *Ringers* idiom (best known from Cherniak's Art Blocks release, 2021): concentric rings extruded along the Z axis, rotated about a common axis, projected to 2D. 200 concentric rings of varying radii are drawn, each rotated by 1.5° about the Z axis from the previous one. The 2D projection uses a simple perspective scale (r_projected = r * z_far / (z_far + z)), so the front rings appear larger than the back rings. The 1° per-ring rotation creates a slow helical pattern that makes the stack read as a 3D form being viewed from the side. A single connecting line between adjacent rings at their phase-0 point ties the stack together — the 'string' Penne adds to make the rings feel connected. The palette is muted and low-saturation, the Penne signature.

Original: https://www.aaronpenne.io

▶ 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
hobbs_cherniak_penne_deep
Source
hobbs_cherniak_penne_deep
Evolution
geometry
Render
concentric_polyline_stack
Complexity
low
Agents
200
Year
2024
Author
Aaron Penne (algorithmic homage)

Math essence

For ring i: r_i = lerp(r_max, r_min, i / (N-1)), z_i = i * z_step, theta_i = i * rotate_per_ring (radians), scale_i = z_far / (z_far + z_i). Project to 2D: draw a polyline of M points on a circle of radius r_i * scale_i, centred at (cx, cy), phase-shifted by theta_i. Sort rings by scale descending (front-to-back) so the front rings are drawn on top. For each adjacent pair of rings, draw a single connecting line between their phase-0 points.

Parameters

Name Type Default Range Description
dim int 600 200–1500 Square canvas side length (px).
num_rings int 200 5–1000 Number of rings N to draw.
points_per_ring int 64 6–256 Number of points M in each ring's polyline.
radius_min_frac float 0.2 0–1 Smallest ring radius (fraction of dim/2).
radius_max_frac float 0.95 0–1 Largest ring radius (fraction of dim/2).
z_step float 0.04 0.001–1 Z-step between rings (arbitrary units).
z_far float 1 0.01–100 Z value at which the perspective projection equals 1.0.
rotate_per_ring float 1 0–30 Degrees of rotation per ring.
palette_hue_base float 0.55 0–1 Base hue of the palette.
palette_hue_range float 0.2 0–1 Hue range across the rings.
saturation float 0.45 0–1 HSV saturation.
value float 0.75 0–1 HSV value / brightness.
line_thickness int 1 1–5 Line thickness in pixels.
background tuple 240,236,226 null–null Background RGB colour.
seed int 42 null–null RNG seed.

Reference

First 60 lines of reference.py. Full source: library_external/hobbs_cherniak_penne_deep/PenneRingers/reference.py

"""PenneRingers — Aaron Penne concentric ring extrusion (3D -> 2D).

A direct homage to Aaron Penne's take on the *Ringers* idiom
(best known from Cherniak's Art Blocks release, 2021).  Penne's
version is geometrically different: instead of "string around
pegs", it's "concentric rings extruded along the Z axis,
rotated about a common axis, projected to 2D".

The kernel we implement:

    1. Build N concentric rings of varying radii, all sharing
       a common centre.  Each ring has its own radius r_i.
    2. Each ring is "extruded" along the Z axis by a small
       amount z_i, and *rotated* about the Z axis by an angle
       theta_i.
    3. The 2D projection of the ring at (r_i, z_i, theta_i) is
       a circle of radius r_i * (z_far / (z_far + z_i)) (the
       standard perspective projection) but with a phase shift
       of theta_i.  So the projected ring is a circle of
       radius proportional to (1 / (1 + z_i / z_far)), drawn
       with an offset that depends on theta_i.
    4. Each ring is drawn as a closed polyline of M points
       (default 64), with the line colour sampled from a
       palette (Penne's signature: muted, low-saturation
       colours inspired by 1980s data visualisations and
       vintage infographics).
    5. A line connects each ring to the next one at one point
       on each ring (the "string" Penne adds to make the rings
       feel like a connected 3D form).

The visual is a stack of concentric rings, each rotated by a
small angle, with the rings closest to the viewer (smallest z)
the largest on screen, and the rings furthest from the viewer
the smallest.  The 1° per-ring rotation creates the slow
"spiral" feeling of a 3D form being viewed from the side.

Reference:
- Cherniak's *Ringers* (2021) for the underlying idiom.
  https://www.artblocks.io/collection/ringers-by-dmitri-cherniak
- Penne's *Apparitions* (2021) for the muted palette and the
  "layered form" aesthetic:
  https://www.artxcode.io/journal/aaron-penne-apparitions-on-art-blocks
"""
from __future__ import annotations

from typing import ClassVar

import numpy as np


# ---------------------------------------------------------------------------
class PenneRingers:
    """Penne-style concentric ring extrusion (3D -> 2D).

    Parameters
    ----------
    dim : int
        Square canvas side length (px).
    num_rings : int
        Number of rings N to draw.

Try running it locally

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

python library_external/hobbs_cherniak_penne_deep/PenneRingers/reference.py

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

More from hobbs_cherniak_penne_deep

Sample of Cherniak Constellations (Dmitri Cherniak point-cloud + graph)

Cherniak Constellations (Dmitri Cherniak point-cloud + graph)

hobbs_cherniak_penne_deep · CherniakConstellations

A pseudo-random point cloud with connecting lines filtered by distance, producing graph-like emergent forms.

View →
Sample of Cherniak Nibbles (Dmitri Cherniak cell-eating cellular automata)

Cherniak Nibbles (Dmitri Cherniak cell-eating cellular automata)

hobbs_cherniak_penne_deep · CherniakNibbles

A cellular-automata growth algorithm in the spirit of Dmitri Cherniak's body of work — small 2D shapes ('nibbles') that grow by selectively removing neighbours.

View →
Sample of Hobbs Color-Field Overlay (Tyler Hobbs compositional method)

Hobbs Color-Field Overlay (Tyler Hobbs compositional method)

hobbs_cherniak_penne_deep · HobbsColorFieldOverlay

Hobbs's compositional method of choice: combine TWO independent fields — a Perlin flow field that drives the *trajectory* of a set of line segments, and a Gaussian-blurred colour field that drives the *colour* and *opacity* of those segments.

View →
Sample of Hobbs QLIQUE (Tyler Hobbs — QQL dense-streamline tangle)

Hobbs QLIQUE (Tyler Hobbs — QQL dense-streamline tangle)

hobbs_cherniak_penne_deep · HobbsQLIQUE

A dense tangle of curves where every curve is a streamline through a Perlin flow field, in the visual spirit of Tyler Hobbs's 2022 QQL release (with Dandelion Mane).

View →
Sample of Hobbs Spirograph (Tyler Hobbs — Petal Loops / Elektroanima)

Hobbs Spirograph (Tyler Hobbs — Petal Loops / Elektroanima)

hobbs_cherniak_penne_deep · HobbsSpirograph

A rotating-circular-arc trail in the spirit of Tyler Hobbs's *Petal Loops* (2020) and *Elektroanima* (2020) — works described by Hobbs as 'looping taken to an excessive level.' M rotating points (default 8) are connected by a closed polyline that morphs each frame as the whole ring rotates by a small angle around a central anchor.

View →
Sample of Penne Imprecise Balance (Aaron Penne stacked-blocks column)

Penne Imprecise Balance (Aaron Penne stacked-blocks column)

hobbs_cherniak_penne_deep · PenneImpreciseBalance

A column of stacked rectangular blocks where each block is rotated by a seed-derived angle.

View →

See all hobbs_cherniak_penne_deep artworks →