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.
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
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.
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.
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.
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).
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.
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.