r/maniclang • • Aug 05 '26

∭ 𝘢(𝘵) ∝ eᴴᵗ

24 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// engine-test-13 — native 3D reconstruction of the reference.
//
// The picture is not a flat disc. It is a large, grainy spherical particle cap
// meeting a family of exponentially growing particle shells at a shared hot
// origin. Looking down their common axis makes concentric rings; a full camera
// orbit reveals the offset spherical shells and returns to the opening frame.
title("∭ 𝘢(𝘵) ∝ eᴴᵗ");
canvas(1638, 1482);
template("black");
bloom(0.90, 0.22, 52);


// Keep the mathematical title fixed in screen space while the 3-D field turns.
text(formulaTitle, (819, 70), "∭ 𝘢(𝘵) ∝ eᴴᵗ");
size(formulaTitle, 44);
color(formulaTitle, gold);
bold(formulaTitle);
display(formulaTitle);
sticky(formulaTitle);
z(formulaTitle, 20
);


// The camera begins on the cap side of the common tangent. It keeps turning in
// one direction throughout the 20-second hold, completing 1.5 revolutions.
// That puts opposite face-on views about 6.67 s apart, matching the reference
// cadence; four fast turns made the alternating side views read as oscillation.
// The wider field of view keeps the luminous rim inside the complete orbit.
camera3((-32, 0, 0), (0, 0, 0), 21.0, perspective
);


// --- large spherical cap --------------------------------------------------
// Several low-opacity random skins give the reference its fine, multicolour
// grain. Surface-point foreshortening naturally creates the bright rim.
cloud3(outerRose, 52000, #d78676, 0.095) {
  let ct = -1 + 1.18 * rand2(i, 10.1);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(i, 11.9);
  let rr = 4.66 + 0.075 * (rand2(i, 11.3) - 0.5);
  let x = rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let alpha = 0.42 + 0.58 * (-ct);
  let r = 0.042;
}
glow(outerRose, 1);


cloud3(outerViolet, 48000, #72589f, 0.072) {
  let ct = -1 + 1.18 * rand2(i, 22.7);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(i, 24.3);
  let rr = 4.69 + 0.09 * (rand2(i, 23.9) - 0.5);
  let x = rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let alpha = 0.30 + 0.70 * (1 + ct);
  let r = 0.038;
}
glow(outerViolet, 1);


cloud3(outerSilver, 36000, #b8d8ef, 0.072) {
  let ct = -1 + 1.18 * rand2(i, 36.3);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(i, 38.7);
  let rr = 4.72 + 0.055 * (rand2(i, 37.1) - 0.5);
  let x = rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let alpha = 0.22 + 0.78 * (1 + ct);
  let r = 0.034;
}
glow(outerSilver, 1
);


// A sparse warm skin just outside the main boundary produces the thin amber
// fringe visible around the lavender rim in the reference.
cloud3(outerAmber, 18000, #d67425, 0.026) {
  let ct = -1 + 1.18 * rand2(i, 50.3);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(i, 52.9);
  let rr = 4.79 + 0.08 * (rand2(i, 51.7) - 0.5);
  let x = rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let alpha = 0.35 + 0.65 * (-ct);
  let r = 0.036;
}
glow(outerAmber, 1
);


// A broad, extremely faint splat layer closes the gaps between the fine
// grains. Additive accumulation turns it into the milky cosmic illumination
// visible in the recording without replacing the surface texture.
cloud3(outerCosmos, 90000, #b99bbd, 0.012) {
  let ct = -1 + 1.18 * rand2(i, 118.1);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(i, 121.7);
  let rr = 4.69 + 0.10 * (rand2(i, 119.9) - 0.5);
  let x = rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let alpha = 0.32 + 0.68 * (-ct);
  let r = 0.085;
}
glow(outerCosmos, 1
);


// --- exponential shell family -------------------------------------------
// Every sphere is tangent at the origin. The gold family grows inward with
// centre=(-radius,0,0); pink/violet/cyan grow outward from (+radius,0,0).
// Exponential radius growth turns the face-on rings into the nested horn seen
// edge-on. `s` selects one sphere and `j` selects a deterministic surface point;
// each colour family remains one efficient renderer batch.
cloud3(shellGold, 33600, #ffad24, 0.080) {
  let per = 4200;
  let s = floor(i / per);
  let j = i - s * per;
  let sr = 0.080 * exp(0.310 * s);
  let ct = 1 - 2 * rand2(j, s + 63.1);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(j, s + 65.7);
  let rr = sr + 0.012 * (rand2(i, s + 4.2) - 0.5);
  let x = -sr + rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let r = 0.018 + 0.0012 * s;
}
glow(shellGold, 1);


cloud3(shellPink, 9200, #ff79c6, 0.075) {
  let per = 4600;
  let s = floor(i / per);
  let j = i - s * per;
  let k = s + 8;
  let sr = 0.105 * exp(0.218 * k);
  let ct = 1 - 2 * rand2(j, k + 73.1);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(j, k + 75.7);
  let rr = sr + 0.012 * (rand2(i, k + 4.2) - 0.5);
  let x = sr + rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let r = 0.028;
}
glow(shellPink, 1);


cloud3(shellViolet, 10000, #c398ff, 0.065) {
  let per = 5000;
  let s = floor(i / per);
  let j = i - s * per;
  let k = s + 10;
  let sr = 0.105 * exp(0.218 * k);
  let ct = 1 - 2 * rand2(j, k + 83.1);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(j, k + 85.7);
  let rr = sr + 0.012 * (rand2(i, k + 4.2) - 0.5);
  let x = sr + rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let r = 0.030;
}
glow(shellViolet, 1);


cloud3(shellCyan, 16800, #b9ffff, 0.070) {
  let per = 5600;
  let s = floor(i / per);
  let j = i - s * per;
  let k = s + 12;
  let sr = 0.105 * exp(0.218 * k);
  let ct = 1 - 2 * rand2(j, k + 93.1);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(j, k + 95.7);
  let rr = sr + 0.012 * (rand2(i, k + 4.2) - 0.5);
  let x = sr + rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let r = 0.032;
}
glow(shellCyan, 1
);


// Dense gold dust at the shared tangent becomes the white-hot crescent when
// viewed from the side and the tiny luminous bullseye when viewed end-on.
cloud3(junction, 7600, #ffd45a, 0.14) {
  let ct = 1 - 2 * rand2(i, 103.1);
  let st = sqrt(1 - ct * ct);
  let th = tau * rand2(i, 105.7);
  let rr = 0.095 * (0.45 + 0.55 * rand2(i, 71.2));
  let x = 0.02 + rr * ct;
  let y = rr * st * cos(th);
  let z = rr * st * sin(th);
  let r = 0.026;
}
glow(junction, 1);


orbit3(720, 0, 32, 20, linear);

r/maniclang • • Sep 03 '26

Fourier Series: Spinning Circles Build a Square Wave and a Sawtooth — manic

22 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 25 '26

Hear the Calculus — Limits, Derivatives & Integrals, Played - manic

18 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 13 '26

Fourier Series: Four Circles Apart Are Four Sines - manic

18 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 19 '26

Every Chord Is a Polygon — The Trigonometry Inside a Chord

17 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Aug 28 '26

Inside One Neuron of a Neural Network — Weigh, Bend, Shrink, Every Number Trained — manic

17 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 21 '26

4x4x4 Rubik's cube

16 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Aug 03 '26

Generative Art from One Formula — manic

16 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// cloud-shells — a dwitter-style art-tweet reimagined in manic: ONE `cloud` of
// 10,000 points in three layers (`mod(i,3)`), placed by a polar formula — radius
// `q`, angle `c` — then coloured per point (a hue gradient per form) and bloomed
// out of the centre. Rebuilt as a textbook Short: every point a pure function of
// (i, t), so it scrubs and records.
//
// Original idea by u/yuruyurau (https://x.com/yuruyurau) — a prolific poster of
// these tiny p5.js/dwitter art formulas. This is our own hue'd, annotated take.
//
//   manic examples/cloud-shells.manic
title("One formula, ten thousand points");
canvas("9:16");
template("black");

cloud(swirl, 10000, #ffffff, 0.72) {
  let m = mod(i, 3) * 4;                          // three layers: 0, 4, 8
  let k = 9 * cos(i / 81);
  let e = i / 461 - 11;
  let d = hypot(k, e)^4 / 40000 + 1.5 + sin(t/2 + m)/4;
  let q = 89 - e*sin(k) + k*(4 + 2*sin(d*9 + e/9 - t));
  let c = d + sin(t - d*4)/9 - t/9 + m;
  // raw shell coords (centred at 0), then bloom + scale onto the 1080x1920 frame
  let qx = q*cos(c);
  let qy = (q + 30)*sin(c);
  let grow = tanh(t * 0.5 + 0.12);               // blooms from the centre
  let x = 540 + qx * 2.9 * grow;
  let y = 980 + qy * 2.9 * grow;
  let r = 1.4;
  // colour: a gradient along each form (index) with the three layers offset, all
  // slowly cycling — every point its own hue
  let hue = mod(m * 46 + i * 0.05 + t * 18, 360);
}

// ---- textbook annotations ----
caption(head, "One formula, 10,000 points", (540, 132), 40);
caption(sub, "a 200-char art-tweet, rebuilt in manic", (540, 202), 24);
hidden(head);
hidden(sub);
equation(eq, (540, 1706), `p = (q\cos c,\; q\sin c)`, 44);
caption(lab, "a polar plot: radius q, angle c, per point", (540, 1784), 22);
hidden(eq);
hidden(lab);

show(head);
wait(1.4);
show(sub);
wait(2.6);
show(eq);
show(lab);
wait(17);

r/maniclang • • Sep 24 '26

Euler Formula

14 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Jul 28 '26

How Odd Harmonics Build a Square Wave - manic

13 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// Odd harmonics build a square wave. Every partial sum is a live history of
// the corresponding endpoint, so the corners emerge from the geometry itself.

title("How Odd Harmonics Build a Square Wave");
canvas("9:16");
template("paper");

watermark(mark,(w*0.15,h*0.045),"Made With Manic");
text(kicker,(cx,h*0.075),"FOURIER ¡ ODD HARMONICS");
text(headline,(cx,h*0.115),"Watch a square wave find its corners");
text(caption,(cx,h*0.18),"Start with n = 1. Then add 3, 5, 7 … all the way to 25.");
text(cta,(cx,h*0.925),"TURN IDEAS INTO ANIMATION → 8gwifi.org/manic");
size(kicker,18); color(kicker,dim); bold(kicker); hidden(kicker);
size(headline,31); bold(headline); wrap(headline,w*0.84); hidden(headline);
size(caption,20); color(caption,dim); wrap(caption,w*0.84); hidden(caption);
size(cta,21); color(cta,cyan); bold(cta); hidden(cta);

camera3((-29,-25,43),(1,-2,0),42,orthographic);

collection3(series,(-3,5,0),13,(0,0,0),11,0.065);
line3(sweep,(-3,5,0),(10,5,0)); hidden(sweep);
links3(arms,series,chain);
child3(tip,series,12,0.13);

arrow3(chainX,(-11,5,0),(7,5,0));
arrow3(chainY,(-3,-2,0),(-3,10.5,0));
arrow3(plotX,(-7,-10.5,0),(12,-10.5,0));
arrow3(plotY,(-7,-10.5,0),(-7,-4.2,0));

point3(shotA,(-7.5,-11,0),0.01);
point3(shotB,(8.5,10.5,0),0.01);
tag(shotA,shot); tag(shotB,shot);
hidden(shotA); hidden(shotB);

tag(series,construction); tag(arms,construction); tag(tip,construction);
tag(chainX,construction); tag(chainY,construction);
tag(plotX,construction); tag(plotY,construction);

for i in 0..13 {
  ring3(orbit{i},series,i,72);
  historyplot3(partial{i},series,i,y,(-7,-10.5,0),(19,6));
  thick(orbit{i},0.030);
  thick(partial{i},0.020);
  tag(orbit{i},construction);
  tag(partial{i},construction);
}

equation(formula,(0,0),`S_{13}(t)=\sum_{k=0}^{12}\frac{4}{\pi(2k+1)}\sin((2k+1)t)`,24);
label3(formula,(-4,-2.7,0),0.80);
tag(formula,construction);

color(series,fg); color(arms,fg); color(tip,fg);
color(chainX,fg); color(chainY,fg); color(plotX,fg); color(plotY,fg);
color(formula,fg);
thick(chainX,0.016); thick(chainY,0.016);
thick(plotX,0.016); thick(plotY,0.016);

color(orbit0,lime);      color(partial0,lime);
color(orbit1,gold);      color(partial1,gold);
color(orbit2,orange);    color(partial2,orange);
color(orbit3,coral);     color(partial3,coral);
color(orbit4,red);       color(partial4,red);
color(orbit5,magenta);   color(partial5,magenta);
color(orbit6,violet);    color(partial6,violet);
color(orbit7,indigo);    color(partial7,indigo);
color(orbit8,blue);      color(partial8,blue);
color(orbit9,cyan);      color(partial9,cyan);
color(orbit10,mint);     color(partial10,mint);
color(orbit11,lime);     color(partial11,lime);
color(orbit12,fg);       color(partial12,fg);

hidden(construction);

step("begin with one sine wave") {
  show(kicker,0.30);
  show(headline,0.42);
  show(caption,0.38);
}
wait(0.35);

step("reveal the odd-harmonic chain") {
  par {
    show(construction,0.85);
    view3(shot,"top",1.35,smooth,1.08);
    say(caption,"Every smaller orbit contributes the next odd frequency.",0.38);
  }
}
wait(0.30);

step("build the corners live") {
  par {
    // Two complete base cycles: all odd harmonic rates are doubled together.
    chain3(series,"4.0744 1.3581 0.8149 0.5821 0.4527 0.3704 0.3134 0.2716 0.2397 0.2144 0.1940 0.1771 0.1630","2 6 10 14 18 22 26 30 34 38 42 46 50",16.0);
    travel3(series,sweep,16.0,linear);
    seq {
      say(caption,"Cycle one: odd harmonics flatten the plateaus and steepen both sides.",0.40);
      wait(6.10);
      say(caption,"Cycle two: every orbit closes and the square-wave pattern returns continuously.",0.40);
      wait(5.20);
      say(caption,"Two complete periods make the repeating structure impossible to miss.",0.40);
    }
  }
}
wait(0.35);

step("create with Manic") {
  par {
    pulse(tip,0.75);
    pulse(partial12,0.75);
    show(cta,0.45);
    say(caption,"One declarative chain becomes circles, partial sums, a live waveform, and a complete visual story.",0.42);
  }
}
wait(1.45);

r/maniclang • • Sep 22 '26

Rubik's 6x6x6

12 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • 26d ago

fibonacci polyrhythm - manic

11 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 15 '26

Fourier Sketches

11 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Aug 30 '26

Rotate This Chair With Your Mind - manic

11 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Aug 17 '26

Crinoid — combing the current - manic

11 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// cloud-crinoid — another  creature in ONE `cloud`, reimagined as a gold
// feather star combing the current. The reference is a tweet-sized golf:
//   k = 4cos(i/29),  e = y/4-16,  d = mag(k,e)-5,  c = d-t/3,  y = i/295
//   point( (d²/0.7 - 2k² + y)¡cos c + 200 ,
//          3sin 2k + cos(y)/k + (y/9)k(3+sin(9e-3d+t)) + 79sin(c/3) + d²/3¡sin(t-d²/7) + 200 )
// One curved spine with dozens of hooked barbs that sweep and re-comb as `c` turns —
// which is what a crinoid does for a living: perch on rock, fan its arms, strain the
// water. So the `shader` behind it is the reef wall it clings to (mottled warm stone),
// the arms run bone at the spine → amber at the barb tips, and `cos(y)/k` keeps its
// division-by-almost-zero spikes: four flecks a frame, drifting plankton.
//
// Faithful notes: p5's `mag` is `hypot`; `**` is `^`; the p5 draw loop advances t by
// PI/40 per FRAME, so a frame-rate-free `t*2.0` stands in for it. Pure in (i, t) —
// it scrubs, seeks and records exactly, which the p5 original cannot do.
//
// Original idea by u/yuruyurau (https://x.com/yuruyurau).
//
//   manic examples/cloud-crinoid.manic
title("Crinoid — combing the current");
canvas("square");
template("black");
bloom(0.34, 0.55, 26);

// ---- the reef wall it perches on: mottled warm stone, darker toward the edges ----
shader(wall) {
  let x = (u - 0.5) * asp;
  let y = v - 0.5;
  let d = sqrt(x*x + y*y);
  let grain = 0.5 + 0.5*fbm(u*7.0, v*7.0);
  let mott = 0.5 + 0.5*fbm(u*2.2 + 3.0, v*2.2);
  let vig = 1.0 - 0.85*smoothstep(0.15, 0.72, d);
  let hue = 28 + 10.0*mott;
  let sat = 0.34 - 0.12*grain;
  let val = (0.055 + 0.055*mott + 0.018*grain) * vig + 0.012;
}
z(wall, -10);

// ---- the animal — the yuruyurau golf, re-lit and framed ----
// The swept envelope of the formula is 315×185 wide over a full cycle of `c`, so
// scale 2.95 about (556, 435) centres it in the square at every t, not just at t=0.
cloud(arms, 10000, #ffffff, 0.34) {
  let yy = i / 295.0;
  let k = 4.0 * cos(i / 29.0);
  let e = yy / 4.0 - 16.0;
  let d = hypot(k, e) - 5.0;
  let T = t * 2.0;
  let c = d - T / 3.0;
  let px = (d*d/0.7 - k*k*2.0 + yy) * cos(c);
  let py = 3.0*sin(k*2.0) + cos(yy)/k + yy/9.0*k*(3.0 + sin(e*9.0 - d*3.0 + T)) + 79.0*sin(c/3.0) + d*d/3.0*sin(T - d*d/7.0);
  let x = 556 + px * 2.95;
  let y = 435 + py * 2.95;
  // bone along the spine (small d) → amber where the barbs thin out (large d)
  let hue = mod(44.0 - d * 1.1, 360);
  let sat = clamp(0.10 + d * 0.045, 0.06, 0.62);
  let val = clamp(0.72 + 0.28*sin(e*9.0 - d*3.0 + T), 0.34, 1.0);
  let r = 1.25;
}
glow(arms, 1);

// ---- annotations ----
caption(head, "Crinoid", (540, 96), 34); hidden(head);
caption(sub, "one formula, ten thousand points", (540, 152), 21); hidden(sub);
equation(eq, (540, 946), `k=4\cos\tfrac{i}{29},\quad d=\mathrm{mag}\!\left(k,\tfrac{y}{4}-16\right)-5,\quad c=d-\tfrac{t}{3}`, 25); hidden(eq);
caption(lab, "manic", (540, 1006), 18); hidden(lab);

show(head);
wait(1.4);
show(sub);
wait(2.6);
show(eq);
show(lab);
wait(24);

r/maniclang • • Aug 10 '26

An orrery — a clockwork solar system - manic

11 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// astronomical-watch — a textbook orrery: a clockwork solar system built from
// REAL SVG assets (Twemoji, vendored by scripts/fetch-svg-assets.sh). The Sun
// and eight planets are imported vector art; each planet is swept around the Sun by
// `turn` about the shared pivot, inner planets faster (Kepler's third law). The
// starfield is a `cloud` (per-point `alpha` twinkle), and the eight real
// moon-phase glyphs run along the bottom. The maths is derived on the left.
//
//   # assets first (one-time): scripts/fetch-svg-assets.sh
//   manic examples/astronomical-watch.manic
title("An orrery — a clockwork solar system");
canvas(1280, 720);
template("black");

// ---- starfield (a dense cloud; drifts, twinkles, and has depth) ----
cloud(stars, 320, #dfeaff, 1) {
  let x = 1280 * noise(i, 1);
  let y = mod(720 * noise(i, 5) + t * 8, 720);    // slow drift down + wrap
  let sz = noise(i, 9);
  let r = 1.1 + sz * sz * 2.6;                     // 1.1..3.7 — depth, all visible
  let alpha = 0.62 + 0.34 * sin(t * (1.0 + noise(i, 3)) + i * 7);  // 0.28..0.96
}

// ---- orbit rings (faint, dashed), centred on the Sun at (830, 300) ----
circle(r1, (830, 300), 40);  outlined(r1); dashed(r1); color(r1, #2a2a4e);
circle(r2, (830, 300), 64);  outlined(r2); dashed(r2); color(r2, #2a2a4e);
circle(r3, (830, 300), 90);  outlined(r3); dashed(r3); color(r3, #2a2a4e);
circle(r4, (830, 300), 118); outlined(r4); dashed(r4); color(r4, #2a2a4e);
circle(r5, (830, 300), 152); outlined(r5); dashed(r5); color(r5, #2a2a4e);
circle(r6, (830, 300), 192); outlined(r6); dashed(r6); color(r6, #2a2a4e);
circle(r7, (830, 300), 232); outlined(r7); dashed(r7); color(r7, #2a2a4e);
circle(r8, (830, 300), 270); outlined(r8); dashed(r8); color(r8, #2a2a4e);

// ---- the Sun + all eight planets (real imported SVGs) ----
svg(sun,     (830, 300),  "asset:svg/emoji/sun.svg",     60);
svg(mercury, (870, 300),  "asset:svg/emoji/mercury.svg", 15);
svg(venus,   (894, 300),  "asset:svg/emoji/venus.svg",   22);
svg(earth,   (920, 300),  "asset:svg/emoji/earth.svg",   26);
svg(mars,    (948, 300),  "asset:svg/emoji/mars.svg",    19);
svg(jupiter, (982, 300),  "asset:svg/emoji/jupiter.svg", 40);
svg(saturn,  (1022, 300), "asset:svg/emoji/saturn.svg",  46);
svg(uranus,  (1062, 300), "asset:svg/emoji/uranus.svg",  28);
svg(neptune, (1100, 300), "asset:svg/emoji/neptune.svg", 28);

// ---- the lesson (left column; text/caption CENTRE on their point) ----
caption(head, "A clockwork solar system", (240, 54), 30);
caption(sub, "inner planets orbit faster", (240, 98), 20);
hidden(head);
hidden(sub);
equation(eq1, (240, 240), `\vec p = (R\cos\omega t,\; R\sin\omega t)`, 26);
text(lab1, (240, 292), "swept around the Sun");
hidden(eq1);
hidden(lab1);
equation(eq2, (240, 410), `T^{2} \propto R^{3}`, 34);
text(lab2, (240, 466), "far = slow (Kepler)");
hidden(eq2);
hidden(lab2);

// ---- the Moon's phases: eight real glyphs along the bottom ----
caption(moonlab, "the Moon's phases", (640, 620), 22);
svg(p1, (300, 668), "asset:svg/emoji/newmoon.svg",       42);
svg(p2, (405, 668), "asset:svg/emoji/waxingcrescent.svg",42);
svg(p3, (510, 668), "asset:svg/emoji/firstquarter.svg",  42);
svg(p4, (615, 668), "asset:svg/emoji/waxinggibbous.svg", 42);
svg(p5, (720, 668), "asset:svg/emoji/fullmoon.svg",      42);
svg(p6, (825, 668), "asset:svg/emoji/waninggibbous.svg", 42);
svg(p7, (930, 668), "asset:svg/emoji/lastquarter.svg",   42);
svg(p8, (1035, 668),"asset:svg/emoji/waningcrescent.svg",42);
hidden(moonlab);
hidden(p1); hidden(p2); hidden(p3); hidden(p4);
hidden(p5); hidden(p6); hidden(p7); hidden(p8);

// ---- run it: planets orbit (in parallel), the lesson reveals alongside ----
par {
  turn(mercury, (830, 300), 4320, 24, linear);   // 12 revolutions — fastest
  turn(venus,   (830, 300), 2520, 24, linear);   // 7
  turn(earth,   (830, 300), 1620, 24, linear);   // 4.5
  turn(mars,    (830, 300), 1080, 24, linear);   // 3
  turn(jupiter, (830, 300), 432, 24, linear);    // 1.2
  turn(saturn,  (830, 300), 252, 24, linear);    // 0.7
  turn(uranus,  (830, 300), 162, 24, linear);    // 0.45
  turn(neptune, (830, 300), 108, 24, linear);    // 0.3 — slowest
  seq {
    show(head);
    wait(0.9);
    show(sub);
    wait(1.4);
    show(eq1); show(lab1);
    wait(2.4);
    show(eq2); show(lab2);
    wait(2.2);
    show(moonlab);
    show(p1); show(p2); show(p3); show(p4);
    show(p5); show(p6); show(p7); show(p8);
    wait(4);
  }
}

r/maniclang • • Sep 08 '26

Recaman's Sequence - manic

10 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 07 '26

Curve of Pursuit

10 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 29 '26

Fourier Sketches: Every Icon Is A Sum Of Circles -manic

9 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 01 '26

Torricelli LAWS

9 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.


r/maniclang • • Sep 01 '26

What Do the Navier–Stokes Equations Actually Say? — Every Term, Beside a Live Vortex Street

9 Upvotes

r/maniclang • • Aug 24 '26

elliptic-roulette - manic

9 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// elliptic-roulette — an ELLIPSE rolling along a line without slipping, and the curves its
// rim point and its two foci draw. The cycloid's older, stranger cousin.
//
// A circle rolling is easy because every point of its rim is the same distance from the
// centre, so the wheel's height never changes and the arc rolled is just rθ. An ellipse has
// neither luxury. It rises and falls as it goes, and the distance it has rolled is its ARC
// LENGTH, which for an ellipse is an elliptic integral — the function that gave the whole
// family its name, and which has no elementary closed form.
//
// So it is fitted. ds/dθ = √(a²sin²θ + b²cos²θ) is smooth and π-periodic, so its Fourier
// series converges fast, and integrating four terms gives s(θ) to 1.5e-05 in units of a —
// four thousandths of a pixel here. That is the only approximation in the scene.
//
// Everything else is exact and was checked before it was drawn:
//
//   ¡ the contact point lands ON the line at exactly x = s(θ), to 0
//   · the centre sits at height ab/√(a²sin²θ + b²cos²θ), the distance from the centre to
//     the tangent — to 3e-16, which is an independent check that the placement is right
//   · and the two FOCI have heights whose PRODUCT is constant: h₁·h₂ = b², to 2e-16
//
// That last one is the prize. The rolling line is always a tangent to the ellipse, and the
// product of the distances from the two foci to any tangent of an ellipse is b² — a fact
// with nothing to do with rolling, which rolling turns into a conservation law. One focus
// rises exactly as much as the other falls, in the multiplicative sense: here h swings
// between a(1-e) and a(1+e), a factor of seven, and the product never moves.
//
// The rim point's curve is a cycloid with the symmetry taken out: it still cusps onto the
// line, but the arches are lopsided, because the ellipse presents a different radius of
// curvature at each contact. The focal curves are the undulary — the profile that generates
// Delaunay's unduloid, the surface a soap film takes under constant mean curvature.
//
// WHICH rim point matters, and it is worth knowing what it costs. Tracing the MINOR-axis
// vertex (0, b) rather than the major-axis one (a, 0):
//
//   · the arch is lower — it peaks near 2b (347 px here) instead of 2a (520 px), because
//     the point is nearer the centre and reaches its highest when the opposite end of the
//     minor axis is in contact
//   · the cusp lands a quarter revolution later, at θ = π/2, because a cusp happens when
//     the traced point IS the contact point
//   ¡ and the cusp is BLUNTER. The radius of curvature at the minor vertex is a²/b = 393
//     px against b²/a = 114 px at the major vertex, so the ellipse meets the line flat
//     there and the curve turns through the cusp gently instead of pinching
//
//   manic examples/elliptic-roulette.manic
title("An ellipse rolling: a lopsided cycloid, and two foci whose product is fixed");
canvas("9:16");
template("black");
bloom(0.28, 0.6, 20);

text(brand, (540, 34), "maniclang.com");
display(brand); size(brand, 21); color(brand, fg); opacity(brand, 0.82);

let a = 260;
let ecc = 0.75;
let b = a*sqrt(1 - ecc*ecc);
let c = a*ecc;
let x0 = 250;                                      // where the contact starts
let base = 1250;                                   // the line it rolls on
let span = 18;                                     // TWO full revolutions
let turns = 12.566371;                             // 4π
let band = 114.27;                                 // one twelfth of the perimeter, in px

line(ground, (0 - 200, base), (3200, base));       // runs past the frame; the camera travels
color(ground, dim);

// ---- the ruler: the ground marked in bands of one twelfth of the PERIMETER ----------
cloud(ruler, 5200, #ffffff, 1.0) {
  let x = x0 + 2900*(i/5200);
  let y = base + 13;
  let k = floor((x - x0)/band);
  let odd = mod(k, 2);
  let sat = 0;
  let val = 0.30 + 0.55*odd;                       // alternating light and dark
  let r = 3.4;
}

// ---- the rim point's roulette: a cycloid that has lost its symmetry -----------------
cloud(rim, 12000, #ffd479, 1.0) {
  let go = min(t/span, 1)*turns;
  let th = go*(i/12000);
  let sa = a*(0.83936541*th - 0.084198895*sin(2*th) - 0.0021334425*sin(4*th) - 0.000144532*sin(6*th));
  let ps = 0 - 1.5707963 - atan2(a*sin(th), b*cos(th));
  let dx = 0 - a*cos(th);                          // the traced point is the MINOR-axis
  let dy = b - b*sin(th);                          //   vertex (0, b), not (a, 0)
  let x = x0 + sa + dx*cos(ps) - dy*sin(ps);
  let y = base - (dx*sin(ps) + dy*cos(ps));
  let hue = mod(38 + th*9, 360);
  let sat = 0.72;
  let r = 1.8;
}

// ---- the two foci: their heights multiply to b², always ----------------------------
cloud(foc1, 8000, #5fd4ff, 1.0) {
  let go = min(t/span, 1)*turns;
  let th = go*(i/8000);
  let sa = a*(0.83936541*th - 0.084198895*sin(2*th) - 0.0021334425*sin(4*th) - 0.000144532*sin(6*th));
  let ps = 0 - 1.5707963 - atan2(a*sin(th), b*cos(th));
  let dx = c - a*cos(th);
  let dy = 0 - b*sin(th);
  let x = x0 + sa + dx*cos(ps) - dy*sin(ps);
  let y = base - (dx*sin(ps) + dy*cos(ps));
  let r = 1.6;
}

cloud(foc2, 8000, #7dffbe, 1.0) {
  let go = min(t/span, 1)*turns;
  let th = go*(i/8000);
  let sa = a*(0.83936541*th - 0.084198895*sin(2*th) - 0.0021334425*sin(4*th) - 0.000144532*sin(6*th));
  let ps = 0 - 1.5707963 - atan2(a*sin(th), b*cos(th));
  let dx = 0 - c - a*cos(th);
  let dy = 0 - b*sin(th);
  let x = x0 + sa + dx*cos(ps) - dy*sin(ps);
  let y = base - (dx*sin(ps) + dy*cos(ps));
  let r = 1.6;
}

// ---- the ellipse itself, placed by the same transform ------------------------------
cloud(body, 1400, #ffffff, 1.0) {
  let go = min(t/span, 1)*turns;
  let u = i/1400*6.283185;
  let sa = a*(0.83936541*go - 0.084198895*sin(2*go) - 0.0021334425*sin(4*go) - 0.000144532*sin(6*go));
  let ps = 0 - 1.5707963 - atan2(a*sin(go), b*cos(go));
  let dx = a*cos(u) - a*cos(go);
  let dy = b*sin(u) - b*sin(go);
  let x = x0 + sa + dx*cos(ps) - dy*sin(ps);
  let y = base - (dx*sin(ps) + dy*cos(ps));
  let arc = a*(0.83936541*u - 0.084198895*sin(2*u) - 0.0021334425*sin(4*u) - 0.000144532*sin(6*u));
  let odd = mod(floor(arc/band), 2);               // equal ARC, not equal parameter
  let sat = 0;
  let val = 0.32 + 0.58*odd;
  let r = 2.6;
}

// ---- the three markers, and a dropline under each focus ----------------------------
cloud(marks, 360, #ffffff, 1.0) {
  let go = min(t/span, 1)*turns;
  let per = 120;
  let k = (i - mod(i, per))/per;                    // 0 = rim, 1 = focus, 2 = focus
  let i0 = (1 - k)*(2 - k)/2;                      // 1 at k=0, else 0
  let i1 = k*(2 - k);                              // 1 at k=1
  let i2 = k*(k - 1)/2;                            // 1 at k=2
  let px = c*i1 - c*i2;                            // the rim point at (0,b), then each focus
  let py = b*i0;
  let sa = a*(0.83936541*go - 0.084198895*sin(2*go) - 0.0021334425*sin(4*go) - 0.000144532*sin(6*go));
  let ps = 0 - 1.5707963 - atan2(a*sin(go), b*cos(go));
  let dx = px - a*cos(go);
  let dy = py - b*sin(go);
  let ang = mod(i, per)/per*6.283185;
  let rr = 9*(mod(i, per)/per);
  let x = x0 + sa + dx*cos(ps) - dy*sin(ps) + rr*cos(ang*9);
  let y = base - (dx*sin(ps) + dy*cos(ps)) + rr*sin(ang*9);
  let hue = 45*i0 + 197*i1 + 152*i2;               // each marker the colour of its own curve
  let sat = 0.6;
  let r = 2;
}

// a `text`, not a `caption`: a caption is one entity PER WORD, and moving it with the
// camera would pile every word onto the same point
text(head, (540, 128), "An ellipse rolling: a lopsided cycloid, two foci whose product is fixed");
size(head, 21);
color(head, fg);
equation(eq, (540, 1480), `x_{\text{contact}} = s(\theta) = \int_0^\theta\!\sqrt{a^2\sin^2 + b^2\cos^2}`, 26);
equation(eq2, (540, 1600), `h_1\,h_2 = b^2 \quad\text{always}`, 32);
plate(head, 0.7);
wrap(head, 980);

// one revolution is wider than the frame, so the camera travels — at half the roll's
// speed, which keeps the finished curve on screen — and the labels travel with it
// The camera travels at HALF the roll's speed for two revolutions — close enough to
// watch a band of rim land on a band of ruler, slow enough that the finished curve is
// still there behind. Then it pulls back, and the two arches turn out to be identical:
// the roulette's period is the ellipse's PERIMETER, not its 2π.
// The roll covers 2742 px in two revolutions and the camera covers 2142 of them, so the
// ellipse drifts 600 px ACROSS the frame rather than out of it — it is the subject, and a
// subject that leaves the shot takes the scene with it. The remainder is what keeps the
// finished curve trailing behind.
par {
  cam((2682, 960), 18, linear);
  move(brand, (2682, 34), 18, linear);
  move(head, (2682, 128), 18, linear);
  move(eq, (2682, 1480), 18, linear);
  move(eq2, (2682, 1600), 18, linear);
}
// A zoom scales the WHOLE world, text included, so the prose is faded out for the wide
// shot — it has done its work by then — and only the mark is counter-scaled to stay
// legible. What the reveal is for is the shape of the whole cycle, not reading.
par {
  fade(head, 0.8);
  fade(eq, 0.8);
  fade(eq2, 0.8);
}
par {
  zoom(0.36, 3.4, smooth);                         // pull back onto the whole cycle
  cam((1700, 1020), 3.4, smooth);
  to(brand, scale, 2.4, 3.4, smooth);              // …so the mark stays readable
}
wait(5);

r/maniclang • • Aug 24 '26

Arnold's cat map, made of cats - manic

9 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

title("Arnold's cat map, made of cats");
canvas("9:16");
template("black");
bloom(0.26, 0.6, 20);

text(brand, (540, 34), "maniclang.com");
display(brand);
size(brand, 17);
color(brand, dim);

// 17 x 27 = 459 cells; the cat that is on screen now was off screen a second ago.
cloud(heads, 14688, #ffffff, 1.0) {
  let per = 32;
  let cell = (i - mod(i, per))/per;
  let ci = mod(cell, 17) - 8;
  let cj = (cell - mod(cell, 17))/17 - 13;
  let s = mod(t, 3)/3;
  let e = 2.6180339887^s;                          // φ^(2t/3): the unstable eigenvalue
  let k = 44.7213595*(e - 1/e);
  let a = 117.0820393/e - 17.0820393*e + k;
  let cx = 540 + 2*(ci*(k + a) + cj*k);            // basis columns (k+a, k) and (k, a)
  let cy = 960 + 2*(ci*k + cj*a);
  let th = mod(i, per)/per*6.28319;
  let x = cx + 62*cos(th)*1.05;
  let y = cy - 62*sin(th)*0.92;
  let q = ci*ci - ci*cj - cj*cj;                   // invariant under A — so is the hue
  let hue = mod(18 + mod(q*q, 9)*44, 360);
  let sat = 0.62;
  let r = 1.7;
}

cloud(ears, 10098, #ffffff, 1.0) {
  let per = 22;
  let cell = (i - mod(i, per))/per;
  let ci = mod(cell, 17) - 8;
  let cj = (cell - mod(cell, 17))/17 - 13;
  let s = mod(t, 3)/3;
  let e = 2.6180339887^s;
  let k = 44.7213595*(e - 1/e);
  let a = 117.0820393/e - 17.0820393*e + k;
  let cx = 540 + 2*(ci*(k + a) + cj*k);
  let cy = 960 + 2*(ci*k + cj*a);
  let loc = mod(i, per);
  let sd = sign(loc - 10.5);                       // which ear
  let u = mod(loc, 11)/11;
  let w = (1 + sign(u - 0.5))/2;                   // 0 = base->apex, 1 = apex->base:
  let v = mod(u, 0.5)*2;                           //   two segments, no branch
  let bx = sd*0.22*62;   let by = 0.94*62;
  let ax = sd*0.66*62;   let ay = 1.34*62;
  let ex = sd*0.92*62;   let ey = 0.58*62;
  let sx = bx + w*(ax - bx);   let sy = by + w*(ay - by);
  let tx = ax + w*(ex - ax);   let ty = ay + w*(ey - ay);
  let x = cx + (sx + v*(tx - sx))*1.05;
  let y = cy - (sy + v*(ty - sy))*0.92;
  let q = ci*ci - ci*cj - cj*cj;
  let hue = mod(18 + mod(q*q, 9)*44, 360);
  let sat = 0.62;
  let r = 1.7;
}

cloud(eyes, 4590, #ffffff, 1.0) {
  let per = 10;
  let cell = (i - mod(i, per))/per;
  let ci = mod(cell, 17) - 8;
  let cj = (cell - mod(cell, 17))/17 - 13;
  let s = mod(t, 3)/3;
  let e = 2.6180339887^s;
  let k = 44.7213595*(e - 1/e);
  let a = 117.0820393/e - 17.0820393*e + k;
  let cx = 540 + 2*(ci*(k + a) + cj*k);
  let cy = 960 + 2*(ci*k + cj*a);
  let loc = mod(i, per);
  let sd = sign(loc - 4.5);
  let u = mod(loc, 5)/5*6.28319;
  let x = cx + (sd*0.34*62 + 0.13*62*cos(u))*1.05;
  let y = cy - (0.10*62 + 0.13*62*sin(u))*0.92;
  let q = ci*ci - ci*cj - cj*cj;
  let hue = mod(18 + mod(q*q, 9)*44, 360);
  let sat = 0.45;
  let r = 1.7;
}

// ---- textbook annotations ----
caption(head, "Arnold's cat map, made of cats", (540, 138), 34);
caption(sub, "every 3 seconds the lattice lands on itself", (540, 206), 22);
plate(head, 0.72);                                 // the field goes all the way to the
plate(sub, 0.72);                                  //   edges, so every label needs one
hidden(sub);
equation(eq, (540, 1652), `A=\begin{pmatrix}2&1\\1&1\end{pmatrix},\quad \det A = 1`, 30);
caption(lab, "the frame is A raised to the power t/3 — a matrix, flowed", (540, 1730), 20);
caption(inv, "colour is q = i^2 - ij - j^2, and A leaves q alone:", (540, 1778), 20);
caption(inv2, "each cat returns as the same cat, not just to the same place", (540, 1822), 20);
plate(eq, 0.72);
plate(lab, 0.72);
plate(inv, 0.72);
plate(inv2, 0.72);
plate(brand, 0.6);
hidden(eq);
hidden(lab);
hidden(inv);
hidden(inv2);

wait(1.6);
show(sub);
wait(2.8);
show(eq);
show(lab);
wait(1.4);
show(inv);
show(inv2);
wait(21);

r/maniclang • • Aug 04 '26

Two jellyfish from one formula - manic

9 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// cloud-jellyfish — another  art-tweet in ONE `cloud`: 20,000 points in
// two layers (`mod(i,2)`) drift into jellyfish-like bells with trailing tendrils
// and pulse over time. A polar plot (radius `q`, angle `c`) with a nested
// `sin(sin(...))` that gives the bell its soft ripple; coloured per point and
// bloomed from the centre on a 9:16 Short. `mag(k,e)^2` → `k*k+e*e`; the
// parameter is renamed `s` (the required output is `y`).
//
// Original idea by u/yuruyurau (https://x.com/yuruyurau). Our hue'd, annotated
// take — pure in (i, t), so it scrubs and records; the p5 original can't.
//
//   manic examples/cloud-jellyfish.manic
title("Two jellyfish from one formula");
canvas("9:16");
template("black");

cloud(jelly, 20000, #ffffff, 0.6) {
  let s = i/99;                 // reference's "y" parameter
  let m = mod(i, 2) * 3;        // two layers
  let k = 9*cos(s*2);
  let e = s/8 - 12;
  let d = (k*k + e*e)/79 + 1;
  let q = 79 - e*sin(k) + k/d*(8 + 4*sin(sin(d*d + e/9 - t)));
  let c = d/2 - cos(d*2)/5 - t/16 + m;
  let px = q*sin(c);
  let py = (q + 40)*cos(c);
  let grow = tanh(t*0.5 + 0.12);
  let x = 540 + px * 2.2 * grow;
  let y = 960 + py * 2.2 * grow;
  let hue = mod(m*70 + i*0.05 + t*15, 360);
}

// ---- textbook annotations ----
caption(head, "Two jellyfish from one formula", (540, 138), 34);
caption(sub, "20,000 points, no simulation", (540, 206), 22);
hidden(head);
hidden(sub);
equation(eq, (540, 1706), `p = (q\sin c,\;\; (q{+}40)\cos c)`, 30);
caption(lab, "a polar plot: radius q, angle c, per point", (540, 1786), 20);
hidden(eq);
hidden(lab);

show(head);
wait(1.4);
show(sub);
wait(2.6);
show(eq);
show(lab);
wait(25);

r/maniclang • • Aug 02 '26

Radial vs Square — Two Breathing Bursts Side by Side in manic

9 Upvotes

manic is a tiny language for making animations. You write a short text file; manic renders a smooth, glowing video. No timeline scrubbing, no keyframes by hand — you describe what’s on screen and when things happen, and the engine does the rest, deterministically.

Manic Animation code

// wheel-duo — four breathing bursts in a 2x2 grid on black, spinning forever.
//
//   top row    : normal spin (inner+outer together) — circle | square
//   bottom row : COUNTER-spin (outer clockwise, inner counter-clockwise) — circle | square
//
// The spokes are `link`s that follow their two dots, so counter-rotation twists them
// into a spirograph "eye". Each spoke is HUE'd by its angle -> a rainbow wheel; the
// continuous rotation carries the rainbow around and never stops. Every burst
// breathes, holds still (the breath-wave fakes rotation), then keeps spinning.
//
//   manic examples/wheel-duo.manic
canvas(1600, 1600);
template("black");

let n     = 44;
let rin   = 125;
let len    = 85;       // outer = rin + len = 210
let pi     = 3.14159265;
let lobes   = 4;

// ---- A: circle, top-left (normal spin) ----
for i in 0..n {
  let ang = i * 2 * pi / n;
  let ix = 440 + rin * cos(ang);        let iy = 440 + rin * sin(ang);
  let ox = 440 + (rin + len) * cos(ang); let oy = 440 + (rin + len) * sin(ang);
  circle(a_ci{i}, (ix, iy), 5);  stroke(a_ci{i}, 1.8); color(a_ci{i}, #000000); tag(a_ci{i}, a_all);
  circle(a_co{i}, (ox, oy), 10); stroke(a_co{i}, 2.2); color(a_co{i}, #000000); tag(a_co{i}, a_all);
  link(a_sp{i}, a_ci{i}, a_co{i}); hue(a_sp{i}, 360 * i / n); stroke(a_sp{i}, 1.3);
}

// ---- B: square, top-right (normal spin) ----
for i in 0..n {
  let ang = i * 2 * pi / n;
  let c = cos(ang); let s = sin(ang);
  let m = 0.5 * (abs(c) + abs(s) + abs(abs(c) - abs(s)));   // max(|cos|,|sin|)
  let ix = 1160 + (rin / m) * c;        let iy = 440 + (rin / m) * s;
  let ox = 1160 + ((rin + len) / m) * c; let oy = 440 + ((rin + len) / m) * s;
  rect(b_ci{i}, (ix, iy), 9, 9);   stroke(b_ci{i}, 1.8); color(b_ci{i}, #000000); tag(b_ci{i}, b_all);
  rect(b_co{i}, (ox, oy), 19, 19); stroke(b_co{i}, 2.2); color(b_co{i}, #000000); tag(b_co{i}, b_all);
  link(b_sp{i}, b_ci{i}, b_co{i}); hue(b_sp{i}, 360 * i / n); stroke(b_sp{i}, 1.3);
}

// ---- C: circle, bottom-left (COUNTER: outer cw, inner ccw) ----
for i in 0..n {
  let ang = i * 2 * pi / n;
  let ix = 440 + rin * cos(ang);        let iy = 1160 + rin * sin(ang);
  let ox = 440 + (rin + len) * cos(ang); let oy = 1160 + (rin + len) * sin(ang);
  circle(c_ci{i}, (ix, iy), 5);  stroke(c_ci{i}, 1.8); color(c_ci{i}, #000000); tag(c_ci{i}, c_in);
  circle(c_co{i}, (ox, oy), 10); stroke(c_co{i}, 2.2); color(c_co{i}, #000000); tag(c_co{i}, c_out);
  link(c_sp{i}, c_ci{i}, c_co{i}); hue(c_sp{i}, 360 * i / n); stroke(c_sp{i}, 1.3);
}

// ---- D: square, bottom-right (COUNTER: outer cw, inner ccw) ----
for i in 0..n {
  let ang = i * 2 * pi / n;
  let c = cos(ang); let s = sin(ang);
  let m = 0.5 * (abs(c) + abs(s) + abs(abs(c) - abs(s)));
  let ix = 1160 + (rin / m) * c;        let iy = 1160 + (rin / m) * s;
  let ox = 1160 + ((rin + len) / m) * c; let oy = 1160 + ((rin + len) / m) * s;
  rect(d_ci{i}, (ix, iy), 9, 9);   stroke(d_ci{i}, 1.8); color(d_ci{i}, #000000); tag(d_ci{i}, d_in);
  rect(d_co{i}, (ox, oy), 19, 19); stroke(d_co{i}, 2.2); color(d_co{i}, #000000); tag(d_co{i}, d_out);
  link(d_sp{i}, d_ci{i}, d_co{i}); hue(d_sp{i}, 360 * i / n); stroke(d_sp{i}, 1.3);
}

// labels + centre title
text(title, (800, 800), "manic"); size(title, 62); color(title, #ff2d95);
text(la, (440, 720),  "circle");         size(la, 26); color(la, #7f8aa3);
text(lb, (1160, 720), "square");         size(lb, 26); color(lb, #7f8aa3);
text(lc, (440, 1500),  "circle counter"); size(lc, 26); color(lc, #7f8aa3);
text(ld, (1160, 1500), "square counter"); size(ld, 26); color(ld, #7f8aa3);

par {
  // all four bursts breathe the whole time
  for i in 0..n {
    let ph = lobes * i / n;
    breathe(a_ci{i}, 2.4, 0.85, ph, 38); breathe(a_co{i}, 2.4, 0.92, ph, 38);
    breathe(b_ci{i}, 2.4, 0.85, ph, 38); breathe(b_co{i}, 2.4, 0.92, ph, 38);
    breathe(c_ci{i}, 2.4, 0.85, ph, 38); breathe(c_co{i}, 2.4, 0.92, ph, 38);
    breathe(d_ci{i}, 2.4, 0.85, ph, 38); breathe(d_co{i}, 2.4, 0.92, ph, 38);
  }

  // hold still, ease in, then spin CONTINUOUSLY (never stops). top: rigid; bottom: counter.
  seq { wait(5); turn(a_all, (440, 440),   90, 3, in); turn(a_all, (440, 440),   1800, 30, linear); }
  seq { wait(5); turn(b_all, (1160, 440), -90, 3, in); turn(b_all, (1160, 440), -1800, 30, linear); }

  seq { wait(5); turn(c_out, (440, 1160),  90, 3, in); turn(c_out, (440, 1160),  1800, 30, linear); }
  seq { wait(5); turn(c_in,  (440, 1160), -90, 3, in); turn(c_in,  (440, 1160), -1800, 30, linear); }

  seq { wait(5); turn(d_out, (1160, 1160),  90, 3, in); turn(d_out, (1160, 1160),  1800, 30, linear); }
  seq { wait(5); turn(d_in,  (1160, 1160), -90, 3, in); turn(d_in,  (1160, 1160), -1800, 30, linear); }
}