PL. 17 · FRACTALS / L-SYSTEM / PAPER FOLD
Heighway Dragon
John Heighway, William Harter, Bruce Banks, 1966
OPEN THE LIVE PLATE ▸DEFINITION
fold a strip in half n times, unfold to 90° turn sequence: Lₙ₊₁ = Lₙ · L · reverse(swap(Lₙ))
NOTES
Fold a strip of paper in half repeatedly, then open every crease to a right angle. Three NASA physicists worked it out in 1966; Michael Crichton put it in the chapter headings of Jurassic Park. It tiles the plane with copies of itself and never crosses its own path.
PROVENANCE
- Origin
- Heighway, Harter and Banks at NASA, 1966
- Standing
- Public domain — a folding rule
- Constants
- Order above 16 exceeds what a plate this size can resolve
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. 17 · HEIGHWAY DRAGON — John Heighway, William Harter, Bruce Banks, 1966
// fold a strip in half n times, unfold to 90°
// turn sequence: Lₙ₊₁ = Lₙ · L · reverse(swap(Lₙ))
// 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=dragon
float p_order = 13 + chf('order_tweak'); // folds · live 6 .. 17
// The plate's own colour: FORMA's FRACTALS 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.46 + 0.5 * cos(6.28318530718 * (t + 0)),
0.1389 + 0.151 * cos(6.28318530718 * (t + 0.05)),
0.1912 + 0.2078 * cos(6.28318530718 * (t + 0.1)));
}
// Fold a strip in half `order` times and open every crease to a right
// angle: the turn sequence Lₙ₊₁ = Lₙ · L · reverse(swap(Lₙ)), walked on the
// integer lattice in quarter-turns. The heading stays an integer 0–3 the
// whole way, so the curve cannot drift off its own lattice; coordinates are
// the lattice steps themselves — scale to taste.
int order = int(rint(p_order));
int turns[];
for (int g = 0; g < order; g++){
// reverse-and-negate the sequence so far, then splice around a left fold
int rev[];
for (int j = len(turns) - 1; j >= 0; j--) push(rev, -turns[j]);
push(turns, 1);
foreach (int tn; rev) push(turns, tn);
}
// canvas headings, y down: 0 = +x, 1 = +y, 2 = -x, 3 = -y
int dxs[] = {1, 0, -1, 0};
int dys[] = {0, 1, 0, -1};
int x = 0, y = 0, dir = 0;
int n = len(turns) + 1;
int prim = addprim(0, "polyline");
int pt = addpoint(0, set(0.0, 0.0, 0.0));
setpointattrib(0, "Cd", pt, forma_ramp(0.8));
addvertex(0, prim, pt);
for (int i = 0; i < len(turns); i++){
x += dxs[dir]; y += dys[dir];
// canvas y runs down; negated so the dragon sits as the plate shows it
pt = addpoint(0, set(float(x), -float(y), 0.0));
// colour sweeps the bright lobe of the ramp along the fold
float u = float(i + 1) / float(n - 1);
setpointattrib(0, "Cd", pt, forma_ramp(0.8 + 0.3 * u));
addvertex(0, prim, pt);
dir = (dir + turns[i] + 4) % 4;
}
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. 17 · HEIGHWAY DRAGON — John Heighway, William Harter, Bruce Banks, 1966
// fold a strip in half n times, unfold to 90°
// turn sequence: Lₙ₊₁ = Lₙ · L · reverse(swap(Lₙ))
// 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 FRACTALS accent, #FF4D6A. Animation runs on time.
// https://forma-gen.com/#plate=dragon
// 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_order = 13 + forma_tweak("order_tweak"); // folds · live 6 .. 17
// 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.376493674935773; // 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]; }
// Heighway's dragon by paper-folding: each fold appends a 1 and the reverse
// of the previous turns negated, then the turns are walked on the unit grid.
// 2^folds segments — 8,192 at the published 13, 131,072 at the top of the
// range, which After Effects carries but not quickly. Scaled to 0.88 of the
// frame, centred. The page draws it in over ~18 s: Trim Paths, End 0 → 100%.
var turns = [];
for (var f = 0; f < Math.round(p_order); f++){
var rev = [];
for (var k = turns.length - 1; k >= 0; k--) rev.push(-turns[k]);
turns = turns.concat([1], rev);
}
var x = 0, y = 0, dir = 0, raw = [[0, 0]];
var minx = 0, maxx = 0, miny = 0, maxy = 0;
for (var i = 0; i < turns.length; i++){
x += Math.round(Math.cos(dir * Math.PI / 2));
y += Math.round(Math.sin(dir * Math.PI / 2));
raw.push([x, y]);
if (x < minx) minx = x; if (x > maxx) maxx = x;
if (y < miny) miny = y; if (y > maxy) maxy = y;
dir = (dir + turns[i] + 4) % 4;
}
var s = Math.min(forma_W * 0.88 / (maxx - minx + 1), forma_H * 0.88 / (maxy - miny + 1));
var ox = (forma_W - (maxx - minx) * s) / 2 - minx * s;
var oy = (forma_H - (maxy - miny) * s) / 2 - miny * s;
var pts = [];
for (var j = 0; j < raw.length; j++) pts.push(forma_pt(ox + raw[j][0] * s, oy + raw[j][1] * s));
createPath(pts, [], [], false);