Skip to the plate
FORMA PUBLIC DOMAIN GENERATIVE ATLAS / ED. 0.28
Plate 45, Euler Spiral: a still of the parametric / linear curvature plate as the atlas renders it, in the curves accent.

PL. 45  ·  CURVES / PARAMETRIC / LINEAR CURVATURE

Euler Spiral

Leonhard Euler, 1744 · Alfred Cornu, 1874

OPEN THE LIVE PLATE ▸

DEFINITION

κ(s) = sᵏ⁻¹,  k = 2 is the Euler spiral
x(s) = ∫₀ˢ cos(uᵏ/k) du
y(s) = ∫₀ˢ sin(uᵏ/k) du

NOTES

The curve whose curvature grows in exact proportion to the distance travelled along it — which is what a car traces when the driver turns the wheel at a constant rate. Euler found it studying a coiled spring; Cornu rediscovered it in the intensity integrals of diffraction. Railway and motorway engineers use the middle of it to ease straights into circular arcs, and each arm winds forever toward its Fresnel limit point without reaching it.

PROVENANCE

Origin
L. Euler, additamentum to "Methodus inveniendi lineas curvas", 1744; A. Cornu, on diffraction, 1874
Standing
Public domain — 18th-century analysis
Constants
Arc length sets how far each arm winds into its eye

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. 45 · EULER SPIRAL — Leonhard Euler, 1744 · Alfred Cornu, 1874
//   κ(s) = sᵏ⁻¹,  k = 2 is the Euler spiral
//   x(s) = ∫₀ˢ cos(uᵏ/k) du
//   y(s) = ∫₀ˢ sin(uᵏ/k) du
// 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=clothoid

float p_len  = 4.6 + chf('len_tweak');      // arc length · live 2.5 .. 7
float p_expo = 2 + chf('expo_tweak');       // k — curvature exponent · live 1.2 .. 4

// 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)));
}

// The generalised Fresnel pair integrated with midpoint steps: curvature
// grows as s^(k−1), and k = 2 is the classic Euler spiral. The second arm is
// the first rotated half a turn — exact at k = 2, where the integrand pair
// is odd, and kept as the figure's symmetry elsewhere.
int forma_n = 1100;

float ds = p_len / float(forma_n);
float hx[], hy[];
float x = 0.0, y = 0.0;
for (int i = 0; i <= forma_n; i++){
    push(hx, x);  push(hy, y);
    float s = (float(i) + 0.5) * ds;
    x += cos(pow(s, p_expo) / p_expo) * ds;
    y += sin(pow(s, p_expo) / p_expo) * ds;
}
// second arm first, reversed, so the polyline runs end to end through zero
int prim = addprim(0, "polyline");
int total = 2 * forma_n + 1;
for (int i = 0; i < total; i++){
    int   j = i - forma_n;                       // −n … n
    float sgn = j < 0 ? -1.0 : 1.0;
    int   a = abs(j);
    // canvas y runs down; negated so the spiral winds as the plate shows it
    int pt = addpoint(0, set(sgn * hx[a], -sgn * hy[a], 0.0));
    // colour sweeps the bright lobe of the ramp along the curve
    float u = float(i) / float(total - 1);
    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. 45 · EULER SPIRAL — Leonhard Euler, 1744 · Alfred Cornu, 1874
//   κ(s) = sᵏ⁻¹,  k = 2 is the Euler spiral
//   x(s) = ∫₀ˢ cos(uᵏ/k) du
//   y(s) = ∫₀ˢ sin(uᵏ/k) du
// 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=clothoid

// 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_len  = 4.6 + forma_tweak("len_tweak");    // arc length · live 2.5 .. 7
var p_expo = 2 + forma_tweak("expo_tweak");     // k — curvature exponent · live 1.2 .. 4

// 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.5242642718367279;   // 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 generalised Euler spiral: curvature grows as s^(k−1), so the tangent
// angle is s^k / k, and the curve is the Fresnel pair integrated by midpoint
// steps — 1,100 of them over the arc length len, exactly as the page does. The
// second arm is the first rotated half a turn: exact at k = 2, where the
// integrand pair is odd, and kept as the figure's symmetry elsewhere. Fitted to
// 0.8 of the frame. The page's comet laps the whole curve in 20 s: Trim Paths.
var N = 1100, ds = p_len / N, k = p_expo;
var half = [], x = 0, y = 0;
for (var i = 0; i <= N; i++){
  half.push([x, y]);
  var s = (i + 0.5) * ds, a = Math.pow(s, k) / k;
  x += Math.cos(a) * ds;
  y += Math.sin(a) * ds;
}
var raw = [];
for (var j = half.length - 1; j >= 1; j--) raw.push([-half[j][0], -half[j][1]]);
for (var m = 0; m < half.length; m++) raw.push(half[m]);
var minx = 1e9, maxx = -1e9, miny = 1e9, maxy = -1e9;
for (var n = 0; n < raw.length; n++){
  var q = raw[n];
  if (q[0] < minx) minx = q[0]; if (q[0] > maxx) maxx = q[0];
  if (q[1] < miny) miny = q[1]; if (q[1] > maxy) maxy = q[1];
}
var sc = Math.min(forma_W * 0.8 / ((maxx - minx) || 1), forma_H * 0.8 / ((maxy - miny) || 1));
var ox = (forma_W - (maxx - minx) * sc) / 2 - minx * sc;
var oy = (forma_H - (maxy - miny) * sc) / 2 - miny * sc;
var pts = [];
for (var r = 0; r < raw.length; r++) pts.push(forma_pt(ox + raw[r][0] * sc, oy + raw[r][1] * sc));
createPath(pts, [], [], false);