Skip to the plate
FORMA PUBLIC DOMAIN GENERATIVE ATLAS / ED. 0.28
Plate 10, Thomas Attractor: a still of the flow / cyclic symmetry plate as the atlas renders it, in the attractors accent.

PL. 10  ·  ATTRACTORS / FLOW / CYCLIC SYMMETRY

Thomas Attractor

René Thomas, 1999

OPEN THE LIVE PLATE ▸

DEFINITION

ẋ = sin y − b·x
ẏ = sin z − b·y
ż = sin x − b·z

NOTES

Cyclically symmetric in x, y and z — the same equation rotated through all three. Thomas described it as a particle drifting through a three-dimensional lattice of forces with friction b. Above b = 1 it dies to a point; below roughly 0.208 it becomes chaotic and starts wandering the lattice.

PROVENANCE

Origin
René Thomas, Int. J. Bifurcation and Chaos, 1999
Standing
Public domain — a system of differential equations
Constants
b ≈ 0.19 sits just inside the chaotic regime
Source
doi:10.1142/S0218127499001383

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. 10 · THOMAS ATTRACTOR — René Thomas, 1999
//   ẋ = sin y − b·x
//   ẏ = sin z − b·y
//   ż = sin x − b·z
// 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=thomas

float p_b = 0.19 + chf('b_tweak');    // b — dissipation · live 0.05 .. 0.35

// The plate's own colour: FORMA's ATTRACTORS 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.0956 + 0.1039 * cos(6.28318530718 * (t + 0)),
    0.4041 + 0.4392 * cos(6.28318530718 * (t + 0.05)),
    0.46 + 0.5 * cos(6.28318530718 * (t + 0.1)));
}

// waived: view — the azimuth the finished attractor is viewed from, and Houdini has a camera of its own
// Euler integration matching the plate: dt = 0.02, 30000 steps from
// (1.1, 1.2, −0.6). Thomas is cyclically symmetric in x, y and z, so the
// axes carry over to Houdini's unchanged.
int   forma_steps = 30000;
float forma_dt    = 0.02;

float x = 1.1, y = 1.2, z = -0.6;
int prim = addprim(0, "polyline");
for (int i = 0; i < forma_steps; i++){
    float dx = sin(y) - p_b * x;
    float dy = sin(z) - p_b * y;
    float dz = sin(x) - p_b * z;
    x += dx * forma_dt;  y += dy * forma_dt;  z += dz * forma_dt;
    int pt = addpoint(0, set(x, y, z));
    // colour sweeps the bright lobe of the ramp along the path
    float u = float(i) / float(forma_steps);
    setpointattrib(0, "Cd", pt, forma_ramp(0.8 + 0.3 * u));
    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 _tweak, so a bare paste already draws the figure and each slider moves one constant in its own units. Trim Paths is the comet.

// FORMA — PL. 10 · THOMAS ATTRACTOR — René Thomas, 1999
//   ẋ = sin y − b·x
//   ẏ = sin z − b·y
//   ż = sin x − b·z
// 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 ATTRACTORS accent, #35E0FF. Animation runs on time.
// https://forma-gen.com/#plate=thomas

// 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_b    = 0.19 + forma_tweak("b_tweak");     // b — dissipation · live 0.05 .. 0.35
var p_view = 0 + forma_tweak("view_tweak");     // view azimuth ° · live -180 .. 180

// 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.5586424041539431;   // 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]; }

// Euler integration of Thomas's cyclically symmetric attractor, the plate's
// 30,000 steps of dt = 0.02 from (1.1, 1.2, −0.6). The page's view: the x–y
// plane turns at 0.1 rad/s, z is the screen vertical, 1/12 of the shorter
// side. Every second point is emitted.
var x = 1.1, y = 1.2, z = -0.6, dt = 0.02;
var DEG = Math.PI / 180;
var rot = forma_t * 0.1 + p_view * DEG, co = Math.cos(rot), si = Math.sin(rot);
var s = Math.min(forma_W, forma_H) / 12, cx = forma_W / 2, cy = forma_H / 2;
var pts = [];
for (var i = 0; i < 30000; i++){
  var dx = Math.sin(y) - p_b * x;
  var dy = Math.sin(z) - p_b * y;
  var dz = Math.sin(x) - p_b * z;
  x += dx * dt; y += dy * dt; z += dz * dt;
  if ((i & 1) === 0) pts.push(forma_pt(cx + (x * co + y * si) * s, cy - z * s));
}
createPath(pts, [], [], false);