PL. 109 · CURVES / ARITHMETIC / OPEN CONJECTURE
Collatz Hailstone Tree
Lothar Collatz, 1937 · surveyed by Jeffrey Lagarias, 1985 · rendering after Edmund Harriss
OPEN THE LIVE PLATE ▸DEFINITION
n → n/2 (n even), n → 3n + 1 (n odd) every orbit drawn backward from the root at 1 bend +Δθ per even step, −2Δθ per odd
NOTES
Take any number: halve it if even, triple-and-add-one if odd, repeat. The conjecture — that every start falls to 1 — has been checked past 2⁶⁸ and proved for no infinite family; Erdős said mathematics is not ready for such problems. The picture draws every orbit backward from the shared root, bending gently one way at even steps and harder the other way at odd ones, so orbits that travel together stay together and the shared trunks thicken into a coral. That rendering idea belongs to Edmund Harriss and is credited as his — the mathematics is 1937, the way of seeing it is not.
PROVENANCE
- Origin
- The problem circulated from L. Collatz, 1937, unpublished; the standard survey is J. C. Lagarias, "The 3x + 1 Problem and Its Generalizations", American Mathematical Monthly 92(1), 1985, 3–23
- Rendering
- The parity-bend coral is Edmund Harriss, popularised around 2017. Crediting the visualisation matters here exactly as crediting the mathematics does
- Standing
- An open conjecture in the public domain. No patent
- Constants
- Count is the number of orbits and the cost dial; the bend pair 1 : −2 keeps the coral from curling into itself, because odd steps are roughly half as frequent
- Source
- doi:10.2307/2322189
HOUDINI · VEX
The same published mathematics as a Detail Wrangle body. Paste it into a Wrangle with Run Over set to Detail; every constant is the published value plus a tweak channel, so Create Spare Parameters gives a slider that starts where the paper does.
// FORMA — PL. 109 · COLLATZ HAILSTONE TREE — Lothar Collatz, 1937 · surveyed by Jeffrey Lagarias, 1985 · rendering after Edmund Harriss
// n → n/2 (n even), n → 3n + 1 (n odd)
// every orbit drawn backward from the root at 1
// bend +Δθ per even step, −2Δθ per odd
// Paste into a Detail Wrangle (Run Over: Detail), no inputs needed.
// Written from the published mathematics, not adapted from any code.
// Constants arrive at their published values. Press the node's Create
// Spare Parameters button and every tweak becomes a slider — starting
// at 0, the published figure, and moving in the constant's own units.
// https://forma-gen.com/#plate=collatz
float p_count = 300 + chf('count_tweak'); // orbits drawn · live 150 .. 600
float p_bend = 8 + chf('bend_tweak'); // Δθ — bend per step (°) · live 4 .. 14
// The plate's own colour: FORMA's CURVES accent as a cosine ramp,
// brightest near t = 0 and t = 1, near-black around t = 0.5.
vector forma_ramp(float t){
return set(
0.11 + 0.1196 * cos(6.28318530718 * (t + 0)),
0.46 + 0.5 * cos(6.28318530718 * (t + 0.05)),
0.2453 + 0.2667 * cos(6.28318530718 * (t + 0.1)));
}
// Every hailstone orbit drawn backward from the shared root at 1, bending
// +bend per even step and -2*bend per odd — the parity-bend coral, which is
// Edmund Harriss' way of seeing the conjecture and is credited on the plate.
// waived: sway — animation pacing; the coral sways on the page, a still cooks here
int forma_count = int(rint(p_count));
float forma_bend = radians(p_bend);
for (int n = 2; n < 2 + forma_count; n++){
// the orbit, then reversed so the walk starts at the root
int seq[];
int m = n;
while (m != 1 && len(seq) < 400){
append(seq, m);
m = (m % 2 == 1) ? 3 * m + 1 : m / 2;
}
append(seq, 1);
seq = reverse(seq);
float x = 0.0, y = 0.0, a = -PI / 2.0;
int prim = addprim(0, "polyline");
int pt0 = addpoint(0, set(0.0, 0.0, 0.0));
setpointattrib(0, "Cd", pt0, forma_ramp(0.95));
addvertex(0, prim, pt0);
for (int k = 1; k < len(seq); k++){
a += (seq[k] % 2 == 0) ? forma_bend : -2.0 * forma_bend;
x += cos(a); y += sin(a);
// canvas y runs down, so the tree is negated onto Houdini's y-up
int pt = addpoint(0, set(x, -y, 0.0));
float shade = 0.95 - min(0.25, float(len(seq)) * 0.002);
setpointattrib(0, "Cd", pt, forma_ramp(shade));
addvertex(0, prim, pt);
}
}
AFTER EFFECTS · EXPRESSION
The same published mathematics as a Shape Layer path expression. Paste it onto a Path property; every constant is the published value plus a Slider Control named
// FORMA — PL. 109 · COLLATZ HAILSTONE TREE — Lothar Collatz, 1937 · surveyed by Jeffrey Lagarias, 1985 · rendering after Edmund Harriss
// n → n/2 (n even), n → 3n + 1 (n odd)
// every orbit drawn backward from the root at 1
// bend +Δθ per even step, −2Δθ per odd
// After Effects port — paste onto a Shape Layer's Path property
// (Contents › Shape › Path). Written from the published mathematics, not
// adapted from any code. Constants arrive at their published values; add a
// Slider Control (Effect › Expression Controls) named <k>_tweak and that
// constant moves in its own units, starting at 0 — the published figure.
// The plate's comet and its reveal are Trim Paths; the stroke colour is
// FORMA's CURVES accent, #3DFF88. Animation runs on time.
// https://forma-gen.com/#plate=collatz
// A missing slider reads 0, so a bare paste already draws the figure.
function forma_tweak(n){ try { return effect(n)("Slider"); } catch (e){ return 0; } }
var p_count = 300 + forma_tweak("count_tweak"); // orbits drawn · live 150 .. 600
var p_bend = 8 + forma_tweak("bend_tweak"); // Δθ — bend per step (°) · live 4 .. 14
var p_sway = 0.1 + forma_tweak("sway_tweak"); // sway · live 0 .. 0.3
// The frame: the plate's W × H canvas is this comp, with the origin at the
// layer's anchor; canvas y already runs down, as After Effects' does.
var forma_W = thisComp.width, forma_H = thisComp.height, forma_t = time;
var forma_phase = 0.7527875283267349; // this plate's own fixed phase, as the page has it
function forma_pt(x, y){ return [x - forma_W / 2, y - forma_H / 2]; }
// The Collatz tree as Edmund Harriss draws it: every orbit n → 3n+1 or n/2
// → … → 1 is walked backwards from 1 as a chain of unit steps, bending by the
// slider's angle at an even term and twice that the other way at an odd one,
// so orbits that share a tail share a stem and the set becomes a tree. The
// page draws each orbit whole; here the orbits are merged into the tree they
// already are — a term reached by two orbits has one position, because the
// bend history from 1 is the same in both — and the tree is one path, walked
// depth first with every branch retraced back to its fork. The sway is the
// page's own breathing of the bend, from this plate's phase; the fit is
// measured at rest, as the page measures it.
var N = Math.round(p_count), base = p_bend * Math.PI / 180;
var wob = 1 + p_sway * Math.sin(forma_t * 0.3 + forma_phase * 6.283);
var kids = {}, seen = { 1: true };
for (var n = 2; n < 2 + N; n++){
var seq = [], m = n;
while (m !== 1 && seq.length < 400){ seq.push(m); m = m % 2 ? 3 * m + 1 : m / 2; }
seq.push(1);
seq.reverse();
for (var k = 1; k < seq.length; k++){
if (seen[seq[k]]) continue;
seen[seq[k]] = true;
(kids[seq[k - 1]] = kids[seq[k - 1]] || []).push(seq[k]);
}
}
// Geometry of the tree at a given bend scale — once to measure, once to draw.
var x0 = 0, x1 = 0, y0 = 0, y1 = 0;
function forma_walk(term, x, y, a, scale, emit){
var list = kids[term] || [];
for (var i = 0; i < list.length; i++){
var c = list[i], a2 = a + (c % 2 === 0 ? base : -2 * base) * scale;
var x2 = x + Math.cos(a2), y2 = y + Math.sin(a2);
if (emit) emit(x2, y2);
else { if (x2 < x0) x0 = x2; if (x2 > x1) x1 = x2; if (y2 < y0) y0 = y2; if (y2 > y1) y1 = y2; }
forma_walk(c, x2, y2, a2, scale, emit);
if (emit) emit(x, y); // back to the fork
}
}
forma_walk(1, 0, 0, -Math.PI / 2, 1, null);
var sc = Math.min(forma_W / (x1 - x0 + 8), forma_H / (y1 - y0 + 8)) * 0.94;
var ox = forma_W / 2 - (x0 + x1) / 2 * sc, oy = forma_H / 2 - (y0 + y1) / 2 * sc;
var pts = [forma_pt(ox, oy)];
forma_walk(1, 0, 0, -Math.PI / 2, wob, function (x, y){ pts.push(forma_pt(ox + x * sc, oy + y * sc)); });
createPath(pts, [], [], false);