u/anish2good

r/GCSEMaths r/MCPservers r/rust_gamedev r/3Blue1Brown r/mathteachers r/shaders r/kubernetes r/JEEAdv25dailyupdates r/devopsGuru r/Indianmathnerds r/CubersButBetter r/deeplearning r/mathmemes r/molecularbiology r/buildinpublic r/Collatz r/Optics r/biology r/PhysicsHelp r/LinearAlgebra r/contentcreation r/securityCTF r/microbiology r/CodingForBeginners r/electronic_circuits r/learnrust r/golang r/AskProgrammers r/computervision r/Jharkhand r/fractals r/zsh r/SideProject r/dotnet r/github r/ShowMeYourSaaS r/u_anish2good r/neuralnetworks r/CasualMath r/computergraphics r/ProgrammingLanguages r/learnmachinelearning r/learnmath r/InternetIsBeautiful r/LaTeX r/genomics r/softwarearchitecture r/RemotionCreators r/selfeducation r/datastructures r/geometrynodes r/PhysicsStudents r/founder r/calculus r/SEO_tool_dev r/ContentCreators r/visualization r/googlecloud r/mathpics r/cpp r/the_calculusguy r/theydidthemath r/programmer r/C_Programming r/MathOlympiad r/bash r/chemhelp r/NoStupidQuestions r/webdev r/Cubers r/PythonLearning r/JeeSimplified r/Mathematica r/Rubiks_Cubes r/Adsense r/charts r/cursedchemistry r/IndiaTechnology r/cprogramming r/webdesign r/maniclang r/generative r/algorithms r/logisim r/StableDiffusionInfo r/PhysicsWallah r/Cplusplus r/learnanimation r/IndiaWeather r/Physics r/startupaccelerator r/microservices r/coolgithubprojects r/generativeAI r/glitchart r/datavisualization r/trigonometry r/lua r/leetcode r/probabilitytheory r/physicsgifs r/thephysicstutor r/SEO r/thermodynamics r/mathpuzzles r/arduino r/java r/manim

Turn selected web-page text, equations, and diagrams into an editable Manic animations

manic is a declarative language for visual explanation. You write a readable text file; Manic turns it into a smooth, deterministic animation. No timeline scrubbing and no hand-managed keyframes — you describe the objects, the relationships, and the story beats while the engine handles motion, continuity, layout, rendering, and repeatable output.

Manic powers visual explanations, animated lessons, simulations, technical diagrams, and creator-ready videos from one readable language. Use it for mathematics, olympiad geometry, calculus, linear algebra, statistics, probability, algorithms and data structures; physics and optics; machine learning and transformer internals; systems architecture and flowcharts; fractals, tilings, fields, and particle processes; SVG artwork and bundled assets; narrated lessons; or polished Reels and Shorts. The same language moves naturally between responsive 2D, textbook-style spatial diagrams, and genuine 3D  scenes.

More information about manic can be found at r/maniclang with animation code

u/anish2good — 14 hours ago

Register in Parrallel - manic

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

// Figure 11.10 — resistors in parallel
//
// NCERT Class 10, Electricity. Three resistors share one voltage; the current
// splits at X and recombines at Y. Every amp and volt on screen is from a
// Modified Nodal Analysis solve: R1=2 kΩ, R2=3 kΩ, R3=6 kΩ across 6 V gives
// 3 mA / 2 mA / 1 mA, and 1/Rp = 1/R1 + 1/R2 + 1/R3 = 1 kΩ. Cut one branch and
// the other two keep their current, because each parallel branch is its own Ohm.


title("Figure 11.10 — resistors in parallel");
canvas("16:9");
template("paper");


let u = 42;
let figx = 470;
let figy = 392;
let ox = figx - 7*u;
let oy = figy - 4*u;


text(hdr, (cx, 42), "Figure 11.10");
display(hdr);
size(hdr, 22);
color(hdr, dim);
bold(hdr);


text(cap, (cx, 78), "resistors in parallel");
display(cap);
size(cap, 30);
color(cap, ink);


support(rule, (cx, 108), 320, "down");


circuit(fig, (figx, figy), `
  dc-voltage 2 8 0 8 v=1.5 name=B1
  dc-voltage 4 8 2 8 v=1.5 name=B2
  dc-voltage 6 8 4 8 v=1.5 name=B3
  dc-voltage 8 8 6 8 v=1.5 name=B4
  ground     8 8
  wire       0 8 0 2
  wire       0 2 0 0
  wire       0 0 3 0
  resistor   3 0 11 0 r=2k name=R1
  wire       11 0 14 0
  wire       14 0 14 2
  wire       0 2 3 2
  resistor   3 2 11 2 r=3k name=R2
  wire       11 2 14 2
  wire       0 2 0 4
  wire       0 4 3 4
  resistor   3 4 11 4 r=6k name=R3
  wire       11 4 14 4
  wire       14 4 14 2
  wire       14 2 14 8
  wire       14 8 11 8 name=AM
  switch     11 8 8 8 closed=1 name=K
`, u, 0);


current(fig, 2.4, circle, crimson, 4);
color(fig.R1, crimson);
color(fig.R2, indigo);
color(fig.R3, teal);


probe(fig, (0, 2), (7*u - 8, 4*u + 36));
probe(fig, K, (3*u, 44));


text(labX, (ox - 20, oy + 2*u), "X");
text(labY, (ox + 14*u + 20, oy + 2*u), "Y");
text(labL, (ox + 3*u, oy - 18), "L");
text(labM, (ox + 11*u, oy - 18), "M");
text(labP, (ox + 3*u, oy + 2*u - 18), "P");
text(labQ, (ox + 11*u, oy + 2*u - 18), "Q");
text(labS, (ox + 3*u, oy + 4*u - 18), "S");
text(labT, (ox + 11*u, oy + 4*u - 18), "T");
text(labK, (ox + 9.5*u, oy + 8*u + 28), "K");
size(labX, 20); size(labY, 20);
size(labL, 16); size(labM, 16);
size(labP, 16); size(labQ, 16);
size(labS, 16); size(labT, 16);
size(labK, 20);
color(labX, ink); color(labY, ink);
color(labL, dim); color(labM, dim);
color(labP, dim); color(labQ, dim);
color(labS, dim); color(labT, dim);
color(labK, ink);
hidden(labX); hidden(labY);
hidden(labL); hidden(labM);
hidden(labP); hidden(labQ);
hidden(labS); hidden(labT);
hidden(labK);


equation(r1n, (ox + 7*u, oy - 28), `R_1`, 22);
equation(r2n, (ox + 7*u, oy + 2*u - 28), `R_2`, 22);
equation(r3n, (ox + 7*u, oy + 4*u - 28), `R_3`, 22);
color(r1n, crimson); color(r2n, indigo); color(r3n, teal);
hidden(r1n); hidden(r2n); hidden(r3n);


text(bplus, (ox + 6, oy + 8*u - 28), "+");
text(bminus, (ox + 8*u - 6, oy + 8*u - 28), "-");
size(bplus, 22); size(bminus, 22);
color(bplus, gold); color(bminus, gold);
hidden(bplus); hidden(bminus);


line(vleadL, (ox, oy + 6*u), (ox + 7*u - 28, oy + 6*u));
line(vleadR, (ox + 7*u + 28, oy + 6*u), (ox + 14*u, oy + 6*u));
circle(vmeter, (ox + 7*u, oy + 6*u), 24);
color(vmeter, void);
outline(vmeter, fg);
stroke(vmeter, 2);
z(vmeter, 2);
text(vlet, (ox + 7*u, oy + 6*u), "V");
size(vlet, 22);
color(vlet, cyan);
z(vlet, 3);
text(vplus, (ox + 7*u - 38, oy + 6*u - 18), "+");
text(vminus, (ox + 7*u + 38, oy + 6*u - 18), "-");
size(vplus, 16); size(vminus, 16);
color(vplus, cyan); color(vminus, cyan);
untraced(vleadL); untraced(vleadR);
hidden(vmeter); hidden(vlet); hidden(vplus); hidden(vminus);
color(vleadL, dim); color(vleadR, dim);
stroke(vleadL, 1.5); stroke(vleadR, 1.5);
tag(vleadL, meters); tag(vleadR, meters);
tag(vmeter, meters); tag(vlet, meters);
tag(vplus, meters); tag(vminus, meters);


circle(ameter, (ox + 12.5*u, oy + 8*u), 22);
color(ameter, void);
outline(ameter, fg);
stroke(ameter, 2);
z(ameter, 2);
text(alet, (ox + 12.5*u, oy + 8*u), "A");
size(alet, 20);
color(alet, gold);
z(alet, 3);
text(aminus, (ox + 12.5*u - 34, oy + 8*u - 16), "-");
text(aplus, (ox + 12.5*u + 34, oy + 8*u - 16), "+");
size(aminus, 14); size(aplus, 14);
color(aminus, gold); color(aplus, gold);
hidden(ameter); hidden(alet); hidden(aminus); hidden(aplus);
tag(ameter, meters); tag(alet, meters);
tag(aminus, meters); tag(aplus, meters);


arrow(iL, (ox - 36, oy + 5.5*u), (ox - 36, oy + 0.6*u));
arrow(iR, (ox + 14*u + 36, oy + 0.6*u), (ox + 14*u + 36, oy + 5.5*u));
color(iL, crimson); color(iR, crimson);
stroke(iL, 2.2); stroke(iR, 2.2);
equation(iLab, (ox - 36, oy + 3*u - 8), `I`, 22);
equation(iRab, (ox + 14*u + 36, oy + 3*u - 8), `I`, 22);
color(iLab, crimson); color(iRab, crimson);
untraced(iL); untraced(iR);
hidden(iLab); hidden(iRab);


arrow(i1a, (ox + 0.4*u, oy + 0*u - 14), (ox + 2.4*u, oy + 0*u - 14));
arrow(i2a, (ox + 0.4*u, oy + 2*u - 14), (ox + 2.4*u, oy + 2*u - 14));
arrow(i3a, (ox + 0.4*u, oy + 4*u - 14), (ox + 2.4*u, oy + 4*u - 14));
color(i1a, crimson); color(i2a, indigo); color(i3a, teal);
stroke(i1a, 1.8); stroke(i2a, 1.8); stroke(i3a, 1.8);
equation(i1n, (ox + 1.4*u, oy - 32), `I_1`, 18);
equation(i2n, (ox + 1.4*u, oy + 2*u - 32), `I_2`, 18);
equation(i3n, (ox + 1.4*u, oy + 4*u - 32), `I_3`, 18);
color(i1n, crimson); color(i2n, indigo); color(i3n, teal);
untraced(i1a); untraced(i2a); untraced(i3a);
hidden(i1n); hidden(i2n); hidden(i3n);


equation(eqV, (1020, 168), `V = 6\,\mathrm{V}`, 26);
equation(eqI, (1020, 228), `I = I_1 + I_2 + I_3`, 24);
equation(eqI1, (1020, 292), `I_1 = 3\,\mathrm{mA}`, 24);
equation(eqI2, (1020, 344), `I_2 = 2\,\mathrm{mA}`, 24);
equation(eqI3, (1020, 396), `I_3 = 1\,\mathrm{mA}`, 24);
equation(eqRp, (1020, 480), `\dfrac{1}{R_p} = \dfrac{1}{R_1}+\dfrac{1}{R_2}+\dfrac{1}{R_3}`, 22);
equation(eqRpv, (1020, 560), `R_p = 1\,\mathrm{k}\Omega`, 26);
color(eqV, cyan);
color(eqI, ink);
color(eqI1, crimson); color(eqI2, indigo); color(eqI3, teal);
color(eqRp, ink); color(eqRpv, teal);
hidden(eqV); hidden(eqI);
hidden(eqI1); hidden(eqI2); hidden(eqI3);
hidden(eqRp); hidden(eqRpv);


text(take, (1020, 640), "lift one branch — the others keep their current");
hidden(take);
size(take, 18);
color(take, crimson);
wrap(take, 360);


rect(pbox, (ox + 7*u, oy + 2*u), 13.2*u, 5.2*u);
outlined(pbox);
color(pbox, indigo);
stroke(pbox, 1.6);
hidden(pbox);


framebox(ring3, fig.R3, 12);
hidden(ring3);


wait(0.5);
run(fig, 8.0);


par {
  show(labX, 0.35);
  show(labY, 0.35);
  show(labK, 0.35);
  show(bplus, 0.35);
  show(bminus, 0.35);
}
par {
  show(r1n, 0.3);
  show(r2n, 0.3);
  show(r3n, 0.3);
  show(labL, 0.3);
  show(labM, 0.3);
  show(labP, 0.3);
  show(labQ, 0.3);
  show(labS, 0.3);
  show(labT, 0.3);
}
par {
  draw(iL, 0.45);
  draw(iR, 0.45);
  show(iLab, 0.35);
  show(iRab, 0.35);
}
say(cap, "close the key — current around the loop");
run(fig, 3.5);


par {
  draw(vleadL, 0.5);
  draw(vleadR, 0.5);
  show(vmeter, 0.45);
  show(vlet, 0.35);
  show(vplus, 0.35);
  show(vminus, 0.35);
  show(pbox, 0.5);
  show(eqV, 0.5);
}
say(cap, "one voltage across every branch");
run(fig, 3.0);
fade(pbox, 0.4);


par {
  show(ameter, 0.4);
  show(alet, 0.35);
  show(aminus, 0.3);
  show(aplus, 0.3);
}
say(cap, "the ammeter reads the total");
run(fig, 2.6);


par {
  draw(i1a, 0.4);
  draw(i2a, 0.4);
  draw(i3a, 0.4);
  show(i1n, 0.3);
  show(i2n, 0.3);
  show(i3n, 0.3);
}
par {
  show(eqI, 0.45);
  show(eqI1, 0.45);
  show(eqI2, 0.45);
  show(eqI3, 0.45);
}
say(cap, "it splits: 3 mA, 2 mA, 1 mA");
run(fig, 4.0);


par {
  show(eqRp, 0.5);
  show(eqRpv, 0.5);
}
say(cap, "so the three together are 1 kΩ");
run(fig, 3.2);


par {
  show(ring3, 0.4);
  fade(eqI3, 0.4);
}
cut(fig, R3, 0.9);
par {
  show(fig.R3, 0.35);
  show(take, 0.5);
}
say(cap, "open one branch — the other two do not notice");
run(fig, 4.5);
wait(0.6);


par {
  fade(ring3, 0.35);
  fade(take, 0.35);
  reconnect(fig, R3, 0.9);
  show(eqI3, 0.4);
}
say(cap, "put it back: I = I1 + I2 + I3 again");
run(fig, 4.0);
wait(0.8);
u/anish2good — 14 hours ago
▲ 3 r/maniclang+1 crossposts

circuits - manic

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

// The circuit kit, in six circuits.
//
// A contact sheet that comes alive: six real circuits on screen from the first frame, then each
// one erases itself, draws itself back, names itself and runs its own current — a different
// shape, colour and pace for every panel. The last beat runs all six at once.
//
// Every dot on screen is a charge integral of a solved branch current, so the six panels are
// running at honestly different speeds because their currents differ, not because six numbers
// were typed. Nothing here is a circuit-specific animation verb: erase, draw, show and par are
// Manic's core kit.

title("six circuits");
canvas("16:9");
template("paper");

text(brand, (640, 40), "maniclang.com");
display(brand);
size(brand, 22);
color(brand, dim);

// ── the board: complete from t = 0, so the first frame is already the whole kit ──

circuit(ohm, (250, 240), `
  dc-voltage 0 4 0 0 v=9
  resistor   0 0 4 0 r=1k
  wire       4 0 4 4
  wire       4 4 0 4
  ground     0 4
`, 40, 1, 0);

circuit(divider, (640, 240), `
  dc-voltage 0 4 0 0 v=9
  resistor   0 0 4 0 r=3k
  resistor   4 0 4 4 r=1k
  wire       4 4 0 4
  ground     0 4
`, 40, 1, 0);

circuit(rc, (1030, 240), `
  dc-voltage 0 4 0 0 v=5
  resistor   0 0 4 0 r=1k
  capacitor  4 0 4 4 c=10u
  wire       4 4 0 4
  ground     0 4
`, 40, 1, 0);

circuit(rl, (250, 512), `
  dc-voltage 0 4 0 0 v=5
  resistor   0 0 4 0 r=100
  inductor   4 0 4 4 l=10m
  wire       4 4 0 4
  ground     0 4
`, 40, 1, 0);

circuit(rect, (640, 512), `
  ac-voltage 0 4 0 0 v=5 f=60
  diode      0 0 3 0
  resistor   3 0 3 4 r=1k
  wire       3 4 0 4
  ground     0 4
`, 40, 1, 0);

circuit(led, (1030, 512), `
  dc-voltage 0 4 0 0 v=5
  resistor   0 0 3 0 r=330
  led        3 0 3 4
  wire       3 4 0 4
  ground     0 4
`, 40, 1, 0);

// ── a different current in every panel: shape, colour, pace ──
//
// Inks chosen for the `paper` template: on cream, gold and amber wash out, so these are the
// darker end of the palette — the colours a textbook would actually print in.

current(ohm, 1, circle, crimson, 3);
current(divider, 1.5, circle, indigo, 3);
current(rc, 1.2, square, green, 3);
current(rl, 2, diamond, purple, 4);
current(rect, 1.6, diamond, orange, 4);
current(led, 2.5, circle, magenta, 4);

// ── the names, which arrive as each panel takes its turn ──

text(n1, (250, 352), "Ohm's law");
hidden(n1);
size(n1, 24);
color(n1, crimson);

text(n2, (640, 352), "voltage divider");
hidden(n2);
size(n2, 24);
color(n2, indigo);

text(n3, (1030, 352), "RC charging");
hidden(n3);
size(n3, 24);
color(n3, green);

text(n4, (250, 624), "RL current rise");
hidden(n4);
size(n4, 24);
color(n4, purple);

text(n5, (640, 624), "half-wave rectifier");
hidden(n5);
size(n5, 24);
color(n5, orange);

text(n6, (1030, 624), "LED + series resistor");
hidden(n6);
size(n6, 24);
color(n6, magenta);

// ── six beats: clear to nothing, draw, name, run ──
//
// The erase has to go to a real zero state, and that means addressing the RIGHT tags. `erase` is
// a stroke verb — it traces a shape out — so on the bare circuit id it would take the component
// strokes away and leave the value labels and the charge dots sitting there, and a text entity
// under `trace` reveals PART of its characters ("10mH" erasing down to "1"). So the strokes are
// erased, and everything that is not a stroke is faded.

wait(0.8);

par {
  erase(ohm.parts, 0.35);
  fade(ohm.labels, 0.3);
  fade(ohm.charge, 0.2);
}
par {
  draw(ohm.parts, 0.85);
  show(ohm.labels, 0.5);
}
show(n1, 0.3);
run(ohm, 2.0);

par {
  erase(divider.parts, 0.35);
  fade(divider.labels, 0.3);
  fade(divider.charge, 0.2);
}
par {
  draw(divider.parts, 0.85);
  show(divider.labels, 0.5);
}
show(n2, 0.3);
run(divider, 2.0);

par {
  erase(rc.parts, 0.35);
  fade(rc.labels, 0.3);
  fade(rc.charge, 0.2);
}
par {
  draw(rc.parts, 0.85);
  show(rc.labels, 0.5);
}
show(n3, 0.3);
run(rc, 2.0);

par {
  erase(rl.parts, 0.35);
  fade(rl.labels, 0.3);
  fade(rl.charge, 0.2);
}
par {
  draw(rl.parts, 0.85);
  show(rl.labels, 0.5);
}
show(n4, 0.3);
run(rl, 2.0);

par {
  erase(rect.parts, 0.35);
  fade(rect.labels, 0.3);
  fade(rect.charge, 0.2);
}
par {
  draw(rect.parts, 0.85);
  show(rect.labels, 0.5);
}
show(n5, 0.3);
run(rect, 2.0);

par {
  erase(led.parts, 0.35);
  fade(led.labels, 0.3);
  fade(led.charge, 0.2);
}
par {
  draw(led.parts, 0.85);
  show(led.labels, 0.5);
}
show(n6, 0.3);
run(led, 2.0);

// ── and the whole board alive at once ──

wait(0.3);
par {
  run(ohm, 5.0);
  run(divider, 5.0);
  run(rc, 5.0);
  run(rl, 5.0);
  run(rect, 5.0);
  run(led, 5.0);
}
wait(0.7);
u/anish2good — 16 hours ago
▲ 2 r/maniclang+1 crossposts

Spirals Nature Keeps Reusing — Fibonacci, Vogel, Fermat, Curlicue & the Uzumaki — manic

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

// spiral-families — the six spirals nature keeps reusing, side by side, each one a single
// closed-form formula and about five thousand points of light.
//
//   Fibonacci      r = a·φ^(2θ/π)        nautilus shells, galaxies
//   Vogel          θ = n · 137.5°        sunflower seeds, pinecones
//   Archimedean    r = a + bθ            watch springs, coiled rope
//   Fermat         r = a·√θ              optical lenses (both arms)
//   Logarithmic    r = a·e^(bθ)          hurricanes (three arms)
//   Curlicue       φ = 2πφ·n²            fractal art
//
// Every panel is one `cloud`: position, size and colour are closed-form functions of the
// point index `i` and live time `t`, so each spiral genuinely turns yet the whole plate stays
// a pure function of `t` — it scrubs and records exactly. The unfurl is not a keyframe
// either: each point's opacity is `saturate((t − start)·rate − i/N)`, so the light travels
// out from the centre because of arithmetic, not animation.
//
// Two honest notes. A LOGARITHMIC spiral has arc length proportional to radius, so the
// Fibonacci and hurricane panels sample uniformly in RADIUS — that is what makes their
// windings even instead of piling up at the rim. And the curlicue here is the quadratic-angle
// form: a cloud formula is pure in `(i, t)`, so it cannot accumulate the running sum of unit
// steps the classical curlicue is built from.
//
//   manic examples/spiral-families.manic
title("Six Spirals Nature Keeps Reusing — manic");
canvas("16:9");
template("black");
bloom(0.38, 0.46, 26);

// the mark, above everything, for the whole film
text(brand, (640, 28), "maniclang.com");
display(brand); size(brand, 19); color(brand, cyan); opacity(brand, 0.8); plate(brand, 0.5); z(brand, 100);

text(ttl, (640, 70), "Six spirals nature keeps reusing");
display(ttl); size(ttl, 30); bold(ttl); color(ttl, fg); hidden(ttl);

// A background that obeys the same law the panels do: the level sets of (angle − ln r / b)
// ARE logarithmic spirals, so this is one giant log spiral used as wallpaper. Its eye sits
// below the frame, so the plate gets broad sweeping arms instead of a bullseye behind the
// grid, and the very top stays clean where the mark and the title live. Kept in a 0.02–0.10
// brightness band on purpose: it has to elevate the six spirals, never compete with them.
shader(bg) {
  let x = (u - 0.5)*asp*1.25;
  let y = v + 0.62;
  let rr = length(x, y) + 0.02;
  let a = atan2(y, x);
  let ph = a - log(rr)/0.42;
  let arms = 0.5 + 0.5*sin(2.0*ph + t*0.16);
  let fine = 0.5 + 0.5*sin(5.0*ph - t*0.09);
  let swirl = 0.68*arms + 0.32*fine;
  let grain = 0.5 + 0.5*fbm(x*3.4 + t*0.02, y*3.4);
  let top = smoothstep(0.0, 0.3, v);
  let hue = 238 - 34.0*swirl;
  let sat = 0.76 - 0.22*swirl;
  let val = 0.016 + 0.078*swirl*top + 0.013*grain*top;
}
z(bg, -10);

// UZUMAKI — how far the whole plate has been drawn into a single spiral. Every panel's cloud
// reads this parameter BY NAME, so the finale is not six separate animations: it is one number,
// and each swarm swirls toward the centre because its own formula says so.
parameter(pull, (150, 690), 0, 0, 1, "uzumaki", 2); hidden(pull.widget);

shader(vortex) {
  let x = (u - 0.5)*asp;
  let y = v - 0.5;
  let rr = length(x, y) + 0.02;
  let a = atan2(y, x);
  // a violent domain warp: the ANGLE itself is kneaded by noise, so the arms tear as they turn
  let w = 0.6*snoise(x*3.2 + t*0.15, y*3.2 - t*0.1);
  let ph = a + w - log(rr)/0.17;
  let arms = 0.5 + 0.5*sin(4.0*ph + t*1.1);
  let core = gaussian(rr, 0.17);
  let edge = saturate(1.25 - rr*1.15);
  let hue = 292 - 46.0*arms + 34.0*core;
  let sat = 0.86 - 0.34*core;
  let val = (0.05 + 0.52*arms*arms + 0.55*core)*edge;
  let alpha = pull*saturate(0.12 + 1.15*arms*arms + core)*edge;
}
z(vortex, -5);

// ============================== panel furniture ==============================
// three columns, two rows: names above each spiral, its formula under the name, and what
// grows that way underneath the light
text(n1, (235, 116), "Fibonacci"); text(n2, (640, 116), "Vogel");
text(n3, (1045, 116), "Archimedean"); text(n4, (235, 398), "Fermat");
text(n5, (640, 398), "Logarithmic"); text(n6, (1045, 398), "Curlicue");
display(n1); display(n2); display(n3); display(n4); display(n5); display(n6);
size(n1, 22); size(n2, 22); size(n3, 22); size(n4, 22); size(n5, 22); size(n6, 22);
bold(n1); bold(n2); bold(n3); bold(n4); bold(n5); bold(n6);
hue(n1, 45); hue(n2, 92); hue(n3, 190); hue(n4, 215); hue(n5, 320); hue(n6, 272);
hidden(n1); hidden(n2); hidden(n3); hidden(n4); hidden(n5); hidden(n6);

equation(f1, (235, 150), `r = a\,\varphi^{2\theta/\pi}`, 21);
equation(f2, (640, 150), `\theta_n = n \cdot 137.5^{\circ}`, 21);
equation(f3, (1045, 150), `r = a + b\,\theta`, 21);
equation(f4, (235, 432), `r = a\sqrt{\theta}`, 21);
equation(f5, (640, 432), `r = a\,e^{b\theta}`, 21);
equation(f6, (1045, 440), `z_n = \sum_{m<n} e^{i\pi\varphi m^2}`, 16);
hue(f1, 45); hue(f2, 92); hue(f3, 190); hue(f4, 215); hue(f5, 320); hue(f6, 272);
hidden(f1); hidden(f2); hidden(f3); hidden(f4); hidden(f5); hidden(f6);

text(w1, (235, 366), "nautilus shells · galaxies");
text(w2, (640, 366), "sunflower seeds · pinecones");
text(w3, (1045, 366), "watch springs · coiled rope");
text(w4, (235, 648), "optical lenses");
text(w5, (640, 648), "hurricanes");
text(w6, (1045, 648), "fractal art");
display(w1); display(w2); display(w3); display(w4); display(w5); display(w6);
size(w1, 17); size(w2, 17); size(w3, 17); size(w4, 17); size(w5, 17); size(w6, 17);
color(w1, dim); color(w2, dim); color(w3, dim);
color(w4, dim); color(w5, dim); color(w6, dim);
hidden(w1); hidden(w2); hidden(w3); hidden(w4); hidden(w5); hidden(w6);

// ============================== 1 · FIBONACCI ==============================
// the golden spiral: every quarter turn multiplies the radius by φ = 1.618…, which is a
// logarithmic spiral with b = ln(φ)/(π/2) = 0.3063. Sampled uniformly in RADIUS, because a
// log spiral's arc length grows with its radius.
cloud(s1, 5200, gold, 0.85) {
  let u = i/5200;
  let rr = 1.2 + 76*u;
  let th = log(rr/0.04)/0.3063 + 0.16*t;
  let px = 235 + rr*cos(th);
  let py = 258 - rr*sin(th);
  let dx = px - 640;
  let dy = py - 360;
  let dd = hypot(dx, dy)*(1 - 0.30*pull);
  let aa = atan2(dy, dx) + pull*2.6;
  let sx = 640 + dd*cos(aa);
  let sy = 360 + dd*sin(aa);
  // The destination is a CHAOTIC spiral, not a tidy one. Two hashes give every point its own
  // pitch, its own arm and its own phase, and drifting noise kneads the radius — so the six
  // families do not line up into one clean curve, they collapse into a maelstrom that is
  // still, everywhere, logarithmic. Deterministic chaos: no rand(), just fract(sin(i)).
  let h1 = fract(sin(i*12.9898)*43758.545);
  let h2 = fract(sin(i*78.233)*12345.678);
  let arm = floor(h2*5)*1.2566;
  let pitch = 0.20 + 0.26*h1;
  let trr = 10 + 244*u + 34*snoise(u*7.0 + h2*9.0, t*0.25);
  let tth = log(max(trr, 8)/0.05)/pitch + arm + 0.5*t + 2.4*h1;
  let x = (1 - pull)*sx + pull*(640 + trr*cos(tth));
  let y = (1 - pull)*sy + pull*(360 - trr*sin(tth));
  let r = 0.9 + 1.5*u;
  let hue = 38 + 26*u;
  let sat = 0.85;
  let val = 0.72 + 0.28*u;
  let alpha = saturate((t - 1.0)*2.4 - u*1.9);
}
glow(s1, 2);

// ============================== 2 · VOGEL ==============================
// phyllotaxis: seed n at 137.5° from the last and √n out. No two seeds crowd, which is why
// sunflowers, pinecones and pineapples all settle on this one.
cloud(s2, 1500, lime, 0.9) {
  let n = i + 1;
  let u = i/1500;
  let rr = 78*sqrt(n/1500);
  let th = n*2.39996 + 0.16*t;
  let px = 640 + rr*cos(th);
  let py = 258 - rr*sin(th);
  let dx = px - 640;
  let dy = py - 360;
  let dd = hypot(dx, dy)*(1 - 0.30*pull);
  let aa = atan2(dy, dx) + pull*2.6;
  let sx = 640 + dd*cos(aa);
  let sy = 360 + dd*sin(aa);
  // The destination is a CHAOTIC spiral, not a tidy one. Two hashes give every point its own
  // pitch, its own arm and its own phase, and drifting noise kneads the radius — so the six
  // families do not line up into one clean curve, they collapse into a maelstrom that is
  // still, everywhere, logarithmic. Deterministic chaos: no rand(), just fract(sin(i)).
  let h1 = fract(sin(i*12.9898)*43758.545);
  let h2 = fract(sin(i*78.233)*12345.678);
  let arm = floor(h2*5)*1.2566;
  let pitch = 0.20 + 0.26*h1;
  let trr = 10 + 244*u + 34*snoise(u*7.0 + h2*9.0, t*0.25);
  let tth = log(max(trr, 8)/0.05)/pitch + arm + 0.5*t + 2.4*h1;
  let x = (1 - pull)*sx + pull*(640 + trr*cos(tth));
  let y = (1 - pull)*sy + pull*(360 - trr*sin(tth));
  let r = 1.3 + 1.4*u;
  let hue = 76 + 40*u;
  let sat = 0.8;
  let val = 0.7 + 0.3*u;
  let alpha = saturate((t - 2.0)*2.4 - u*1.9);
}
glow(s2, 2);

// ============================== 3 · ARCHIMEDEAN ==============================
// equal spacing every turn — the coil of a watch spring or a rope on a deck. Sampled
// uniformly in θ, since that IS the defining regularity.
cloud(s3, 5200, cyan, 0.85) {
  let u = i/5200;
  let th = u*37.7;
  let rr = 3.5 + 1.98*th;
  let px = 1045 + rr*cos(th + 0.16*t);
  let py = 258 - rr*sin(th + 0.16*t);
  let dx = px - 640;
  let dy = py - 360;
  let dd = hypot(dx, dy)*(1 - 0.30*pull);
  let aa = atan2(dy, dx) + pull*2.6;
  let sx = 640 + dd*cos(aa);
  let sy = 360 + dd*sin(aa);
  // The destination is a CHAOTIC spiral, not a tidy one. Two hashes give every point its own
  // pitch, its own arm and its own phase, and drifting noise kneads the radius — so the six
  // families do not line up into one clean curve, they collapse into a maelstrom that is
  // still, everywhere, logarithmic. Deterministic chaos: no rand(), just fract(sin(i)).
  let h1 = fract(sin(i*12.9898)*43758.545);
  let h2 = fract(sin(i*78.233)*12345.678);
  let arm = floor(h2*5)*1.2566;
  let pitch = 0.20 + 0.26*h1;
  let trr = 10 + 244*u + 34*snoise(u*7.0 + h2*9.0, t*0.25);
  let tth = log(max(trr, 8)/0.05)/pitch + arm + 0.5*t + 2.4*h1;
  let x = (1 - pull)*sx + pull*(640 + trr*cos(tth));
  let y = (1 - pull)*sy + pull*(360 - trr*sin(tth));
  let r = 1.0 + 1.1*u;
  let hue = 184 + 24*u;
  let sat = 0.8;
  let val = 0.72 + 0.28*u;
  let alpha = saturate((t - 3.0)*2.4 - u*1.9);
}
glow(s3, 2);

// ============================== 4 · FERMAT ==============================
// r = a√θ, and the real thing has BOTH arms — `mod(i,2)` picks one, so the panel shows the
// full双 curve. Equal AREA per turn, which is why lens and mirror designers use it.
cloud(s4, 5200, cyan, 0.85) {
  let u = i/5200;
  let arm = mod(i, 2)*pi;
  let th = u*30;
  let rr = 14.2*sqrt(th);
  let px = 235 + rr*cos(th + arm + 0.16*t);
  let py = 540 - rr*sin(th + arm + 0.16*t);
  let dx = px - 640;
  let dy = py - 360;
  let dd = hypot(dx, dy)*(1 - 0.30*pull);
  let aa = atan2(dy, dx) + pull*2.6;
  let sx = 640 + dd*cos(aa);
  let sy = 360 + dd*sin(aa);
  // The destination is a CHAOTIC spiral, not a tidy one. Two hashes give every point its own
  // pitch, its own arm and its own phase, and drifting noise kneads the radius — so the six
  // families do not line up into one clean curve, they collapse into a maelstrom that is
  // still, everywhere, logarithmic. Deterministic chaos: no rand(), just fract(sin(i)).
  let h1 = fract(sin(i*12.9898)*43758.545);
  let h2 = fract(sin(i*78.233)*12345.678);
  let arm = floor(h2*5)*1.2566;
  let pitch = 0.20 + 0.26*h1;
  let trr = 10 + 244*u + 34*snoise(u*7.0 + h2*9.0, t*0.25);
  let tth = log(max(trr, 8)/0.05)/pitch + arm + 0.5*t + 2.4*h1;
  let x = (1 - pull)*sx + pull*(640 + trr*cos(tth));
  let y = (1 - pull)*sy + pull*(360 - trr*sin(tth));
  let r = 1.0 + 1.0*u;
  let hue = 206 + 26*u;
  let sat = 0.82;
  let val = 0.7 + 0.3*u;
  let alpha = saturate((t - 4.0)*2.4 - u*1.9);
}
glow(s4, 2);

// ============================== 5 · LOGARITHMIC ==============================
// the same law as Fibonacci with a fatter pitch, and three arms — a hurricane's rainbands.
// Again sampled uniformly in radius; the bright core is the eye.
cloud(s5, 5400, magenta, 0.85) {
  let u = i/5400;
  let arm = mod(i, 3)*2.0944;
  let rr = 1.0 + 77*u;
  let th = log(rr/1.6)/0.30 + arm + 0.34*t;
  let px = 640 + rr*cos(th);
  let py = 540 - rr*sin(th);
  let dx = px - 640;
  let dy = py - 360;
  let dd = hypot(dx, dy)*(1 - 0.30*pull);
  let aa = atan2(dy, dx) + pull*2.6;
  let sx = 640 + dd*cos(aa);
  let sy = 360 + dd*sin(aa);
  // The destination is a CHAOTIC spiral, not a tidy one. Two hashes give every point its own
  // pitch, its own arm and its own phase, and drifting noise kneads the radius — so the six
  // families do not line up into one clean curve, they collapse into a maelstrom that is
  // still, everywhere, logarithmic. Deterministic chaos: no rand(), just fract(sin(i)).
  let h1 = fract(sin(i*12.9898)*43758.545);
  let h2 = fract(sin(i*78.233)*12345.678);
  let arm = floor(h2*5)*1.2566;
  let pitch = 0.20 + 0.26*h1;
  let trr = 10 + 244*u + 34*snoise(u*7.0 + h2*9.0, t*0.25);
  let tth = log(max(trr, 8)/0.05)/pitch + arm + 0.5*t + 2.4*h1;
  let x = (1 - pull)*sx + pull*(640 + trr*cos(tth));
  let y = (1 - pull)*sy + pull*(360 - trr*sin(tth));
  let r = 0.9 + 1.4*u;
  let hue = 300 + 40*u;
  let sat = 0.78;
  let val = 0.95 - 0.3*u;
  let alpha = saturate((t - 5.0)*2.4 - u*1.9);
}
glow(s5, 2);

// ============================== 6 · CURLICUE ==============================
// The REAL curlicue, not a stand-in: z_n is the running sum of unit steps, each turned by
// π·s·m². A `cloud` cannot do this — its formulas are pure in (i, t) and cannot accumulate —
// but a build-time `sum` reduction over the loop index computes the exact partial sum, so the
// path is drawn as 360 real segments. The golden fraction makes the classic branching,
// self-similar clusters; nothing here is random and nothing is recursive.
for n in 0..360 {
  line(s6{n},
       (975 + 6.5*sum(m in 0..n : cos(pi*0.618034*m*m)),
        566 - 6.5*sum(m in 0..n : sin(pi*0.618034*m*m))),
       (975 + 6.5*sum(m in 0..n+1 : cos(pi*0.618034*m*m)),
        566 - 6.5*sum(m in 0..n+1 : sin(pi*0.618034*m*m))));
  hue(s6{n}, 258 + n/11);
  untraced(s6{n});
  tag(s6{n}, s6);
}
glow(s6, 2);

// ---- the uzumaki finale ----
svg(maki1, (250, 366), "asset:svg/emoji/1f365.svg", 74); hidden(maki1);
svg(maki2, (1030, 366), "asset:svg/emoji/1f365.svg", 74); hidden(maki2);
text(uzulab, (640, 648), "UZUMAKI");
display(uzulab); size(uzulab, 38); bold(uzulab); color(uzulab, fg); plate(uzulab, 0.62); z(uzulab, 50); hidden(uzulab);

// ================================= the film =================================
show(ttl, 1.0);
wait(0.5);

// each panel introduces itself as its own light arrives — the name, the formula and what grows
// that way are already on screen, so the film does not narrate them
stagger(1.0) {
  par { show(n1, 0.5); show(f1, 0.5); show(w1, 0.4); }
  par { show(n2, 0.5); show(f2, 0.5); show(w2, 0.4); }
  par { show(n3, 0.5); show(f3, 0.5); show(w3, 0.4); }
  par { show(n4, 0.5); show(f4, 0.5); show(w4, 0.4); }
  par { show(n5, 0.5); show(f5, 0.5); show(w5, 0.4); }
  par { show(n6, 0.5); show(f6, 0.5); show(w6, 0.4); }
}
draw(s6, 2.4, smooth);
wait(1.0);
// they all turn, so the dwell is not dead time
wait(4.0);
wait(3.6);

// ============================== UZUMAKI ==============================
par {
  fade(n1, 0.7); fade(n2, 0.7); fade(n3, 0.7); fade(n4, 0.7); fade(n5, 0.7); fade(n6, 0.7);
  fade(f1, 0.7); fade(f2, 0.7); fade(f3, 0.7); fade(f4, 0.7); fade(f5, 0.7); fade(f6, 0.7);
  fade(w1, 0.6); fade(w2, 0.6); fade(w3, 0.6); fade(w4, 0.6); fade(w5, 0.6); fade(w6, 0.6);
  fade(ttl, 0.8);
}
wait(1.4);
// one number does all of this: each swarm reads `pull` and swirls in on its own account,
// and the curlicue path swings round with them
par {
  to(pull, value, 1, 4.6, smooth);
  turn(s6, (640, 360), 80, 4.6, smooth);
  to(s6, opacity, 0.2, 4.6, smooth);
}
wait(1.8);
// the merged spiral gets a beat on its own, then steps back so the word can sit on it
par {
  to(s1, opacity, 0.17, 1.0); to(s2, opacity, 0.17, 1.0); to(s3, opacity, 0.17, 1.0);
  to(s4, opacity, 0.17, 1.0); to(s5, opacity, 0.17, 1.0);
}
par { show(maki1, 0.7); show(maki2, 0.7); }
show(uzulab, 0.9);
wait(2.8);

// ================================= endcard =================================
par {
  fade(maki1, 0.6); fade(maki2, 0.6);
  fade(uzulab, 0.7);
  to(pull, value, 0.42, 1.6, smooth);
}
wait(2.8);
u/anish2good — 1 day ago
▲ 1 r/maniclang+2 crossposts

Manic MCP Server is here — connect Claude, Cursor, or any local AI and let it write, validate, and render Manic

Your AI can now speak Manic.

We just shipped the Manic MCP Server — a Model Context Protocol server that plugs Manic into any MCP-capable AI: Claude Code, Claude Desktop, Cursor, Windsurf, OpenAI's Codex CLI and Agents SDK, Zed, Cline, or a fully local model running in LM Studio.

The idea is simple: your model does the writing. The server hands it the complete Manic authoring guide (the same system prompt Manic's own AI uses), and from there the model authors real .manic source itself — then validates it, saves it into your Manic project, and renders finished videos through the hosted platform.

Five tools:

  • manic_authoring_guide — the full language guide, no auth needed
  • manic_check — free validation with exact line/column diagnostics
  • manic_render — hosted render to mp4 / gif / webm / mov / still
  • manic_render_status — poll the job, get the video URL (or the exact compile error to fix)
  • manic_save_to_project — the file lands in your Manic project, editable in Manic Create and Workbench

The loop your AI runs:

guide → write .manic → check → fix → check → render → video 🎬

Setup is one config block (create an API key at app.maniclang.com/account with scopes check, render:create, jobs:read, projects:read, projects:write):

  {
    "mcpServers": {
      "manic": {
        "command": "npx",
        "args": ["-y", "@maniclang/mcp-server"],
        "env": { "MANIC_API_KEY": "mk_live_…" }
      }
    }
  }

Then ask your AI something like:

"Read the Manic authoring guide, then create a 20-second animation of Dijkstra's algorithm exploring a small graph — visited nodes glow, the frontier pulses. Validate it, fix any errors, render it as landscape mp4, and give me the video link." …and watch it come back with a video.

On cost: because your own model is the author, this spends zero Manic AI credits. Checking and saving are free; only renders use your plan's exports. Tip: a still render is a fast, cheap layout check before committing to a full video.

Local-model fans: this is the easiest way to give Llama / Qwen / Gemma real animation powers — the model runs entirely on your machine, and only validation and rendering touch the platform.

Links:

reddit.com
u/anish2good — 1 day ago
▲ 18 r/maniclang+1 crossposts

How the Moon Moves Every Ocean - manic

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

// tides — how the Moon moves every ocean on Earth, and why the popular sentence is wrong.
//
// "The Moon's gravity pulls on the oceans" predicts ONE bulge and one high tide a day. The
// sea gives two. The fix is the whole subject: what raises a tide is not the pull but the
// DIFFERENCE in pull across the planet — a gradient, and a gradient has two ends.
//
//   ACT I    the fact: two highs a day, every day
//   ACT II   the wrong picture, stated properly before it is demolished
//   ACT III  the gradient — subtract the pull on Earth's centre and a QUADRUPOLE is left.
//            The ocean is 12,000 parcels of water `advect`ed through it, so the two bulges
//            are computed, not drawn. Then the planet turns under them and one coast passes
//            through both: two high tides a day, from one Moon
//   ACT IV   sideways, not up: 1.1 micrometres per second squared, a ten-millionth of
//            gravity. Nothing is lifted; water is herded
//   ACT V    the Sun pulls 179x harder and loses, because tides go as 1/d^3. Add the two
//            and the spring/neap fortnight EMERGES from two cosines
//   ACT VI   the honest part: the real ocean is not two bulges gliding around
//
// The whole film is one stage — the view down on the north pole, so Earth's rotation is an
// ordinary in-plane `turn` and the water can stay where the Moon put it. The tidal field is
// one formula: with the Moon along +x it is (2x, -y). Water cannot leave the surface, so what
// moves it is the TANGENTIAL part — the radial component projected out, F - (F.r)r — which is
// exactly why a tide is a horizontal shove and not a lift.
//
//   manic examples/tides.manic
title("Tides — How the Moon Moves Every Ocean — manic");
canvas("16:9");
template("black");
bloom(0.3, 0.52, 22);

// ---- the mark, up top and above everything, for the whole film ----
text(brand, (640, 32), "maniclang.com");
display(brand); size(brand, 19); color(brand, cyan); opacity(brand, 0.72); z(brand, 100);

// ---- type ----
text(ttl, (640, 92), "How the Moon Moves Every Ocean");
display(ttl); size(ttl, 46); bold(ttl); color(ttl, fg); hidden(ttl);
text(sub, (640, 146), "and why \"it pulls the water up\" is the wrong answer");
display(sub); size(sub, 22); color(sub, dim); hidden(sub);
text(cap, (640, 668), ""); display(cap); size(cap, 23); color(cap, fg); hidden(cap);
text(act, (1060, 624), ""); display(act); size(act, 19); color(act, gold); hidden(act);

// ================================ THE STAGE ================================
// Earth seen from over the north pole, 300 px across, so its surface is at radius 150.
svg(earth, (640, 360), "asset:svg/emoji/1f30e.svg", 300);
circle(orbit, (640, 360), 300); outlined(orbit); outline(orbit, dim);
opacity(orbit, 0.45); hidden(orbit);
svg(moon, (940, 360), "asset:svg/emoji/1f315.svg", 82);
text(moonlab, (940, 470), "the Moon — one lap: 27.3 days");
display(moonlab); size(moonlab, 17); color(moonlab, dim); hidden(moonlab);
// one coast, riding the surface
dot(coast, (640, 210), 9); color(coast, gold); hidden(coast);
text(coastlab, (640, 176), "one coast");
display(coastlab); size(coastlab, 17); color(coastlab, gold); hidden(coastlab);

// a tide gauge: the Moon's own constituent, 12.4206 hours, over two days
coords(gauge, (210, 468), (0, 48), (-1.4, 1.4), 19, 54, 0, 12, 1, "hours", "");
color(gauge, dim); hidden(gauge);
plot(trace, (210, 468), 19, 54, "cos(x/12.4206*tau)", (0, 48));
color(trace, cyan); untraced(trace); hidden(trace);
text(twice, (640, 262), "two highs, every day");
display(twice); size(twice, 21); color(twice, gold); hidden(twice);

// ---- ACT II — the wrong picture ----
for k in 0..7 {
  arrow(pull{k}, (556, 240 + k*40), (700, 240 + k*40));
  color(pull{k}, gold);
  untraced(pull{k});
  tag(pull{k}, pulls);
}
// r(t) = R(1 + e·cos t): one bulge facing the Moon — what "it pulls the water" predicts
param(wrong, (640, 360), 150, 150,
  "(1 + 0.14*cos(t))*cos(t)", "(1 + 0.14*cos(t))*sin(t)", (0, tau));
color(wrong, gold); untraced(wrong);
text(wronglab, (640, 566), "one bulge  ⇒  one high tide a day");
display(wronglab); size(wronglab, 21); color(wronglab, gold); hidden(wronglab);
text(nope, (640, 606), "✗  the sea gives two");
display(nope); size(nope, 21); color(nope, magenta); hidden(nope);

// ---- ACT III — the gradient ----
dot(pnear, (790, 360), 7); color(pnear, gold); hidden(pnear);
dot(pmid, (640, 360), 7); color(pmid, fg); hidden(pmid);
dot(pfar, (490, 360), 7); color(pfar, gold); hidden(pfar);
arrow(gnear, (790, 360), (916, 360)); color(gnear, gold); untraced(gnear);
arrow(gmid, (640, 360), (750, 360)); color(gmid, fg); untraced(gmid);
arrow(gfar, (490, 360), (587, 360)); color(gfar, gold); untraced(gfar);
text(g1, (640, 182), "one pull, unequally felt — gravity falls off as 1/d²");
display(g1); size(g1, 20); color(g1, dim); hidden(g1);
arrow(tnear, (790, 360), (862, 360)); color(tnear, magenta); untraced(tnear);
arrow(tfar, (490, 360), (418, 360)); color(tfar, magenta); untraced(tfar);
text(g2, (640, 182), "subtract the pull on the centre — the whole planet already falls with it");
display(g2); size(g2, 20); color(g2, magenta); hidden(g2);

// THE TIDAL FIELD, tangential part only: F - (F·r̂)r̂ with F = (2x, -y). The epsilon keeps the
// planet's centre finite, where the projection is undefined.
vectorfield(tide, (640, 360), 470, 290,
  "0.42*(2*x - x*(2*x*x - y*y)/(x*x + y*y + 0.02))",
  "0.42*(-y - y*(2*x*x - y*y)/(x*x + y*y + 0.02))", 15);
color(tide, dim); opacity(tide, 0.55); hidden(tide);

// the ocean: a shell of water on the surface, which the field herds
cloud(sea, 12000, cyan, 0.75) {
  let a = (i/12000)*tau;
  let w = mod(i, 7) - 3;
  let x = 640 + (153 + w*1.7)*cos(a);
  let y = 360 + (153 + w*1.7)*sin(a);
  let r = 1.5;
  let hue = 192 + 10*w;
}
glow(sea, 2); hidden(sea);
// and the shape all that herding is heading for: the equilibrium tide, a prolate ellipsoid
// r(t) = R(1 + e(3cos²t − 1)/2) — with e drawn about 200,000x too big to be visible at all
parameter(phi, (150, 600), 0, 0, 0.4, "the Moon has moved on", 2); hidden(phi.widget);
param(bulge, (640, 360), 150, 150,
  "(1 + 0.12*(3*cos(t-p)*cos(t-p) - 1)/2)*cos(t)",
  "(1 + 0.12*(3*cos(t-p)*cos(t-p) - 1)/2)*sin(t)", (0, tau));
bind(phi, bulge, formula,
  "(1 + 0.12*(3*cos(t-p)*cos(t-p) - 1)/2)*cos(t)",
  "(1 + 0.12*(3*cos(t-p)*cos(t-p) - 1)/2)*sin(t)");
color(bulge, gold); untraced(bulge);
text(twolab, (640, 578), "two bulges  ⇒  two high tides a day  ✓");
display(twolab); size(twolab, 21); color(twolab, gold); hidden(twolab);
text(spinlab, (640, 606), "");
display(spinlab); size(spinlab, 20); color(spinlab, cyan); hidden(spinlab);

// ---- ACT IV — how gentle it is ----
equation(tiny, (640, 244), `a_{\text{tide}}=\frac{2GMr}{d^3}=1.1\times10^{-6}\ \mathrm{m/s^2}`, 30);
color(tiny, magenta); hidden(tiny);
equation(vsg, (640, 322), `\frac{a_{\text{tide}}}{g}\approx 10^{-7}`, 32);
color(vsg, gold); hidden(vsg);
text(gentle, (640, 400), "it could not lift a puddle — but it can shove an ocean sideways for six hours");
display(gentle); size(gentle, 20); color(gentle, dim); hidden(gentle);

// ---- ACT V — the Sun loses, and the beat ----
equation(sunpull, (640, 240), `\frac{F_{\odot}}{F_{\text{Moon}}} = 179`, 30);
color(sunpull, gold); hidden(sunpull);
equation(suntide, (640, 328), `\frac{a_{\odot}}{a_{\text{Moon}}} = 0.46`, 30);
color(suntide, cyan); hidden(suntide);
text(cube, (640, 404), "a pull goes as 1/d², a difference in pull as 1/d³ — and the Sun is 390× farther");
display(cube); size(cube, 20); color(cube, dim); hidden(cube);

// two constituents added: the Moon's 12.4206 h and the Sun's 12.000 h. Nothing here sets a
// fortnight — the 14.77-day spring/neap envelope is what two cosines DO.
coords(month, (160, 470), (0, 720), (-1.7, 1.7), 1.34, 46, 0, 168, 1, "hours", "");
color(month, dim); hidden(month);
plot(beat, (160, 470), 1.34, 46, "cos(x/12.4206*tau) + 0.46*cos(x/12*tau)", (0, 720));
color(beat, cyan); untraced(beat); hidden(beat);
text(springlab, (635, 330), "spring");
display(springlab); size(springlab, 18); color(springlab, gold); hidden(springlab);
text(neaplab, (397, 330), "neap");
display(neaplab); size(neaplab, 18); color(neaplab, magenta); hidden(neaplab);
text(fortnight, (640, 602), "14.77 days, spring to spring — and nobody typed that number in");
display(fortnight); size(fortnight, 20); color(fortnight, gold); hidden(fortnight);

// ---- ACT VI — the honest part ----
text(truth1, (640, 244), "The real ocean is not two bulges gliding around a smooth planet.");
display(truth1); size(truth1, 24); color(truth1, fg); hidden(truth1);
text(truth2, (640, 312), "Continents are in the way. Basins ring. The tide turns around fixed nodes.");
display(truth2); size(truth2, 21); color(truth2, dim); hidden(truth2);
text(fundy, (400, 400), "Bay of Fundy:  16 m");
display(fundy); size(fundy, 22); color(fundy, gold); hidden(fundy);
text(med, (890, 400), "much of the Mediterranean:  ~0");
display(med); size(med, 22); color(med, magenta); hidden(med);
text(truth3, (640, 480), "The Moon writes the forcing. The coastline decides the tide.");
display(truth3); size(truth3, 22); color(truth3, cyan); hidden(truth3);

// ================================= ACT I =================================
show(ttl, 0.9);
show(sub, 0.7);
wait(1.3);
show(cap, 0.3);
say(cap, "Every coast on Earth does this twice a day, and has done for four billion years.");
par { fade(ttl, 0.8); fade(sub, 0.8); }
show(earth, 0.9);
par { show(orbit, 0.6); show(moon, 0.7); show(moonlab, 0.5); }
wait(0.4);
say(cap, "One Moon, one lap of us every 27.3 days. Everything that follows comes from that.");
// a rigidly turned label arrives upside down, so it steps off for the lap
fade(moonlab, 0.4);
par {
  turn(moon, (640, 360), 360, 4.2, smooth);
  turn(earth, (640, 360), 90, 4.2, smooth);
}
show(moonlab, 0.4);
wait(0.4);
say(cap, "A tide gauge on any coast, two days of it: high, low, high, low, high.");
// the gauge needs the whole width, so the planet steps out for a moment
par { fade(earth, 0.7); fade(moon, 0.6); fade(moonlab, 0.4); fade(orbit, 0.5); }
// while the stage is dark the Moon takes up its working position, far off to the right,
// where the tidal field's formula puts it
move(moon, (1150, 360), 0.01);
move(moonlab, (1150, 436), 0.01);
say(moonlab, "the Moon");
show(gauge, 0.6);
show(trace, 0.4);
draw(trace, 2.0, smooth);
show(twice, 0.5);
wait(1.4);
say(cap, "Two a day. That number is the whole puzzle — and the usual explanation gets it wrong.");
wait(2.2);

// ================================= ACT II =================================
say(act, "II · the wrong picture");
show(act, 0.4);
par { fade(gauge, 0.6); fade(trace, 0.6); fade(twice, 0.5); }
say(cap, "The Moon's gravity pulls on the oceans. So far, so true.");
par { show(earth, 0.8); show(moon, 0.6); show(moonlab, 0.4); }
wait(0.5);
stagger(0.07) {
  for k in 0..7 {
    draw(pull{k}, 0.5);
  }
}
wait(1.2);
say(cap, "Pull the water toward the Moon and it heaps up on the near side. One heap.");
draw(wrong, 1.2, smooth);
show(wronglab, 0.5);
wait(1.8);
say(cap, "Which is one high tide a day. The sea gives two. Something is missing.");
show(nope, 0.6);
wait(2.2);

// ================================= ACT III =================================
say(act, "III · the difference, not the pull");
par { fade(pulls, 0.6); fade(wrong, 0.6); fade(wronglab, 0.5); fade(nope, 0.5); }
say(cap, "Three places: the near side, the centre, the far side.");
par { show(pnear, 0.4); show(pmid, 0.4); show(pfar, 0.4); }
wait(0.7);
say(cap, "Gravity weakens with distance, so those three pulls are not the same size.");
show(g1, 0.5);
stagger(0.22) {
  draw(gnear, 0.6);
  draw(gmid, 0.6);
  draw(gfar, 0.6);
}
wait(1.8);
say(cap, "But the planet is already falling around its centre. Subtract that pull from all three.");
par { fade(g1, 0.5); fade(gnear, 0.5); fade(gmid, 0.5); fade(gfar, 0.5); }
show(g2, 0.6);
par { draw(tnear, 0.7); draw(tfar, 0.7); }
wait(1.8);
say(cap, "What is left points AWAY at both ends. A gradient has two ends. There is the two.");
wait(2.2);
say(cap, "Do that at every point at once, and this is the field the Moon leaves behind.");
par { fade(g2, 0.5); fade(tnear, 0.5); fade(tfar, 0.5); fade(pnear, 0.4); fade(pmid, 0.4); fade(pfar, 0.4); }
show(tide, 0.9);
wait(1.4);
say(cap, "Now put twelve thousand parcels of water on the surface and let the field push them.");
show(sea, 0.8);
wait(0.5);
advect(sea, tide, 6.5, 0.55);
wait(0.4);
say(cap, "Nothing was placed by hand. The water went where the arrows converge — and there are two.");
draw(bulge, 1.4, smooth);
show(twolab, 0.6);
wait(2.2);
// the payoff: the water stays where the Moon put it, and the planet turns underneath
say(cap, "The bulges belong to the Moon, not to the planet. So turn the planet underneath them.");
// the field's arrows are fixed to the Moon's OLD direction, so they bow out before it moves
par { show(coast, 0.5); show(coastlab, 0.4); fade(sea, 0.8); fade(tide, 0.8); }
wait(0.9);
say(spinlab, "one rotation = one day");
show(spinlab, 0.4);
// one day: Earth turns once, and the Moon does not wait — it moves on 360/27.3 = 13.2 degrees,
// taking the tide's axis with it
par {
  fade(coastlab, 0.5);
  turn(earth, (640, 360), 360, 7.0, linear);
  turn(coast, (640, 360), 360, 7.0, linear);
  turn(moon, (640, 360), 13.2, 7.0, linear);
  to(phi, value, 0.23, 7.0, linear);
}
say(cap, "One coast, one day, two bulges crossed. Two high tides — and the water never travelled.");
wait(2.2);
// and the reason tide tables slip: the coast is back where it started, the Moon is not
say(cap, "But look: the coast is home and the Moon has moved on thirteen degrees.");
say(spinlab, "the Moon moved on 13° while the planet turned once");
wait(2.0);
say(cap, "So the coast has to chase it — about fifty minutes more of turning, every single day.");
par {
  turn(earth, (640, 360), 13.2, 1.6, smooth);
  turn(coast, (640, 360), 13.2, 1.6, smooth);
}
say(spinlab, "one tidal day = 24 h 50 min");
wait(2.4);

// ================================= ACT IV =================================
say(act, "IV · sideways, not up");
// the caption turns over with the stage, so no frame is left empty under a stale line
say(cap, "One more correction, and it is the one that surprises people. Look how gentle this is.");
par {
  fade(coast, 0.4); fade(spinlab, 0.4); fade(bulge, 0.6); fade(twolab, 0.5);
  fade(earth, 0.8); fade(moon, 0.6); fade(moonlab, 0.4);
}
show(tiny, 0.8);
wait(1.6);
show(vsg, 0.7);
say(cap, "A ten-millionth of the gravity holding that ocean down.");
wait(1.8);
show(gentle, 0.6);
say(cap, "Nothing gets lifted. Water gets nudged SIDEWAYS for six hours, and arrives.");
wait(2.4);

// ================================= ACT V =================================
say(act, "V · the Sun loses");
par { fade(tiny, 0.7); fade(vsg, 0.7); fade(gentle, 0.6); }
say(cap, "The Sun pulls Earth a hundred and seventy-nine times harder than the Moon does.");
show(sunpull, 0.8);
wait(1.6);
say(cap, "And raises less than half the tide, because a DIFFERENCE falls off faster than a pull.");
show(suntide, 0.8);
show(cube, 0.6);
wait(2.4);
par { fade(sunpull, 0.6); fade(suntide, 0.6); fade(cube, 0.5); }
say(cap, "So the ocean answers two clocks: 12.42 hours for the Moon, 12.00 for the Sun.");
show(month, 0.6);
show(beat, 0.4);
draw(beat, 3.0, smooth);
wait(0.6);
say(cap, "Add them. Where the two clocks agree the tides run big; where they fight, small.");
par { show(springlab, 0.5); show(neaplab, 0.5); }
wait(1.6);
say(cap, "Spring tides, neap tides — a fortnight apart, out of two cosines and nothing else.");
show(fortnight, 0.7);
wait(2.4);

// ================================= ACT VI =================================
say(act, "VI · the honest part");
par {
  fade(month, 0.6); fade(beat, 0.6); fade(springlab, 0.4); fade(neaplab, 0.4);
  fade(fortnight, 0.6);
}
show(truth1, 0.8);
say(cap, "Everything so far is the FORCING. It is not the tide you can go and watch.");
wait(1.8);
show(truth2, 0.7);
say(cap, "Water cannot chase the Moon around a planet with two continents in the way.");
wait(1.8);
par { show(fundy, 0.6); show(med, 0.6); }
say(cap, "One bay rings like an organ pipe and swings sixteen metres. Another barely moves.");
wait(2.2);
show(truth3, 0.8);
wait(2.4);

// ================================= ENDCARD =================================
par {
  fade(truth1, 0.7); fade(truth2, 0.6); fade(truth3, 0.7);
  fade(fundy, 0.5); fade(med, 0.5); fade(cap, 0.6); fade(act, 0.5);
}
text(end1, (640, 318), "The Moon does not lift the sea.");
display(end1); size(end1, 40); bold(end1); color(end1, fg); hidden(end1);
text(end2, (640, 380), "It pulls one side harder than the other.");
display(end2); size(end2, 30); color(end2, cyan); hidden(end2);
text(end3, (640, 458), "and the ocean charges interest: 3.8 cm of Moon a year, paid out of Earth's spin");
display(end3); size(end3, 19); color(end3, dim); hidden(end3);
text(end4, (640, 522), "— manic");
display(end4); size(end4, 24); color(end4, gold); hidden(end4);
show(end1, 0.9);
show(end2, 0.8);
show(end3, 0.7);
show(end4, 0.6);
wait(2.6);
u/anish2good — 1 day ago
▲ 1 r/maniclang+1 crossposts

Dominos — One Push - manic

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("Dominos — One Push");
canvas("16:9");
template("black");
bloom(0.36, 0.52, 26);

// ---- the room: a dark tabletop, light falling from above ----
shader(room) {
  let x = (u - 0.5) * asp;
  let y = v - 0.5;
  let d = sqrt(x*x + y*y);
  let pool = 1.0 - smoothstep(0.06, 0.78, d);
  let grain = 0.5 + 0.5*fbm(u*9.0, v*9.0);
  let hue = 214 - 8.0*pool;
  let sat = 0.55 - 0.25*pool;
  let val = 0.012 + 0.055*pool + 0.012*grain*pool;
}
z(room, -10);

// ---- type ----
text(ttl, (640, 96), "Dominos — One Push");
display(ttl); size(ttl, 46); bold(ttl); color(ttl, fg); hidden(ttl);
text(sub, (640, 152), "and nothing here decides how fast it travels");
display(sub); size(sub, 22); color(sub, dim); hidden(sub);
text(cap, (640, 656), ""); display(cap); size(cap, 23); color(cap, fg); hidden(cap);
text(note, (640, 618), ""); display(note); size(note, 19); color(note, gold); hidden(note);

// ---- ACT I — the row (side on: you can watch each slab turn) ----
dominos(row, (470, 386), 16, 45, 7.5438, 9.38, 2, 0.62, 7);
color(row, cyan);
color(row.ground, dim);   // the table is furniture, not neon
color(row.d0, gold);
hidden(row.dominos);
framebox(mark, row.d0, 7); color(mark, gold); untraced(mark);

// ---- ACT II — the geometry, beside the row ----
equation(contact, (988, 330), `\theta_c=\arcsin\frac{s}{h}=12^{\circ}`, 27);
color(contact, gold); hidden(contact);
equation(balance, (988, 404), `\theta_b=\arctan\frac{t}{h}=9.5^{\circ}`, 27);
color(balance, magenta); hidden(balance);
text(geo1, (988, 462), "tip past 9.5° or it stands back up");
display(geo1); size(geo1, 18); color(geo1, dim); hidden(geo1);
text(geo2, (988, 492), "reach 12° and the next one goes");
display(geo2); size(geo2, 18); color(geo2, dim); hidden(geo2);

// ---- ACT III — the same physics, any shape (top down) ----
dominopath(spiral, (640, 380), 12.6, 12.6, "t*cos(t)", "t*sin(t)", 92, (1.4, 18.6), 45, 7.5438, 1.7, 0.7, 8);
hidden(spiral.dominos); untraced(spiral.path);

dominopath(heart, (640, 366), 12.2, 12.2,
  "16*sin(t)^3", "13*cos(t)-5*cos(2*t)-2*cos(3*t)-cos(4*t)", 56, (0, 6.2832), 45, 7.5438, 1.7, 0.7, 8);
color(heart, magenta);
hidden(heart.dominos); untraced(heart.path);

// ---- ACT IV — twelve chains: gap across, per-impact loss down ----
dominos(cellrow, (0, 0), 10, 45, 7.5438, 9.38, 1.05, 0.6, 6); hidden(cellrow);
sweep(grid, cellrow, spacing, (9, 46), transfer, (0.95, 0.35), (640, 404), 4, 3, 250, 128, 0, 0);
hidden(grid);

// ============================ ACT I ============================
show(ttl, 0.8);
show(sub, 0.7);
show(cap, 0.3);
wait(0.5);
say(cap, "Sixteen slabs, forty-five millimetres tall, nine point four apart.");
show(row.ground, 0.5);
stagger(0.055) {
  for i in 0..16 {
    show(row.d{i}, 0.22);
  }
}
wait(0.4);
draw(mark, 0.5);
say(cap, "One nudge, on that one.");
pulse(row.d0, 0.6);
wait(0.3);
par {
  fade(ttl, 0.8);
  fade(sub, 0.8);
}
run(row, 4.4);
wait(0.5);
say(cap, "The wave crossed the row. Its speed was never written down.");
wait(1.6);

// ============================ ACT II ============================
say(cap, "Each slab has to climb past its own balance angle before gravity takes over.");
show(balance, 0.7);
show(geo1, 0.5);
wait(1.8);
say(cap, "Then it only has to reach the next one — twelve degrees, for these dominos.");
show(contact, 0.7);
show(geo2, 0.5);
wait(2.2);
say(cap, "Two angles, and the whole cascade follows.");
wait(1.8);

// ============================ ACT III ============================
par {
  fade(row, 0.8);
  fade(mark, 0.5);
  fade(contact, 0.7);
  fade(balance, 0.7);
  fade(geo1, 0.5);
  fade(geo2, 0.5);
}
say(cap, "Nothing about that argument needs a straight line. Seen from above —");
draw(spiral.path, 1.3, smooth);
show(note, 0.4);
say(note, "r = t");
stagger(0.016) {
  for i in 0..92 {
    show(spiral.d{i}, 0.16);
  }
}
wait(0.5);
say(cap, "Ninety-two dominos on a spiral, standing at equal spacing along the curve.");
run(spiral, 5.4);
wait(0.9);

par { fade(spiral, 0.8); fade(spiral.path, 0.6); }
say(cap, "Change the formula. Keep the physics.");
draw(heart.path, 1.1, smooth);
say(note, "x = 16 sin³t,  y = 13 cos t − 5 cos 2t − 2 cos 3t − cos 4t");
size(note, 17);
stagger(0.022) {
  for i in 0..56 {
    show(heart.d{i}, 0.18);
  }
}
wait(0.4);
say(cap, "A closed curve, so the wave runs all the way round and meets where it began.");
run(heart, 5.0);
wait(1.4);

// ============================ ACT IV ============================
par {
  fade(heart, 0.8);
  fade(heart.path, 0.6);
  fade(note, 0.5);
}
say(cap, "So what does set the speed? Only the geometry — here it is, twelve times over.");
par {
  show(grid.chrome, 0.6);
  show(grid.headers, 0.7);
}
show(grid.cells, 0.9);
wait(0.5);
run(grid, 7.5);
wait(0.8);
say(cap, "Wider gaps run faster — until the gap is taller than the domino. Then nothing arrives.");
wait(2.2);
say(cap, "Bottom left dies too: that surface loses too much at every impact.");
wait(2.4);

// ============================ ENDCARD ============================
par {
  fade(grid, 0.9);
  fade(cap, 0.7);
}
text(end1, (640, 344), "One push. The rest is geometry.");
display(end1); size(end1, 46); bold(end1); color(end1, fg); hidden(end1);
text(end2, (640, 424), "— manic");
display(end2); size(end2, 26); color(end2, cyan); hidden(end2);
show(end1, 0.9);
show(end2, 0.7);
wait(2.4);
u/anish2good — 2 days ago

Krill — ten thousand points, one closed form - manic

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-krill — another  creature in ONE `cloud`: a compact, densely bristled
// swimmer, pale and cold-lit. It is the same golf as `cloud-crinoid.manic` with three
// constants changed — worth knowing, because these tweet-sized formulas are a parameter
// space you can walk, not a single drawing:
//
//   crinoid      e = y/4 - 16    d = mag(k,e) - 5    + d²/3·sin(t - d²/7)
//   this one     e = y/5 - 13    d = mag(k,e) - 4    + d²/3·cos(t - d²/9)
//
// Everything else is identical — k = 4cos(i/29), c = d - t/3, y = i/295, and
//   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) + … + 200 )
// Shortening the spine (`-4`) and slowing the body taper (`y/5`) pulls the long sweeping
// arms in: where the crinoid fans wide, this is compact, dense and coiled — a stubbier
// relative on the same skeleton. The phase change from `sin` to `cos` in the last term
// re-times the breathing against the sweep, so the two never move alike.
//
// Staged as a specimen plate rather than a scene: no reef wall, no warm stone — a cold
// dark ground, a pale bone-blue body, and the three changed constants typeset below it.
//
// Faithful notes: p5's `mag` is `hypot`, `**` is `^`; the p5 draw loop advances t by
// PI/60 per FRAME, so a frame-rate-free `t*1.333` stands in for it (the crinoid's
// PI/40 became `t*2.0`, so the two run at the same relative pace). `cos(y)/k` keeps its
// division-by-almost-zero flecks. Pure in (i, t), so it scrubs, seeks and records.
//
// Original idea by u/yuruyurau (https://x.com/yuruyurau).
//
//   manic examples/cloud-krill.manic
title("Krill — ten thousand points, one closed form");
canvas("square");
template("black");
bloom(0.3, 0.55, 22);

// ---- a cold, near-empty field: this is a plate, not a habitat ----
shader(field) {
  let x = (u - 0.5) * asp;
  let y = v - 0.5;
  let d = sqrt(x*x + y*y);
  let vig = 1.0 - 0.9*smoothstep(0.1, 0.75, d);
  let hue = 214;
  let sat = 0.5;
  let val = 0.028 * vig + 0.008;
}
z(field, -10);

// ---- the animal — swept envelope is 222×459, so ×2.2 about (540,540) fills the plate ----
cloud(body, 10000, #ffffff, 0.34) {
  let yy = i / 295.0;
  let k = 4.0 * cos(i / 29.0);
  let e = yy / 5.0 - 13.0;
  let d = hypot(k, e) - 4.0;
  let T = t * 1.333;
  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*cos(T - d*d/9.0);
  let x = 540 + px * 2.2;
  let y = 540 + py * 2.2;
  // bone-blue: pale at the spine, cooling toward the barb tips
  let hue = mod(206.0 + d * 2.2, 360);
  let sat = clamp(0.06 + d * 0.05, 0.04, 0.5);
  let val = clamp(0.74 + 0.26*sin(e*9.0 - d*3.0 + T), 0.36, 1.0);
  let r = 1.25;
}
glow(body, 1);

// ---- annotations ----
caption(head, "Krill", (540, 96), 34); hidden(head);
caption(sub, "ten thousand points, one closed form", (540, 152), 21); hidden(sub);
equation(eq, (540, 950), `k=4\cos\tfrac{i}{29},\quad d=\mathrm{mag}\!\left(k,\tfrac{y}{5}-13\right)-4,\quad c=d-\tfrac{t}{3}`, 24); hidden(eq);
caption(lab, "manic", (540, 1010), 18); hidden(lab);

show(head);
wait(1.4);
show(sub);
wait(2.6);
show(eq);
show(lab);
wait(24);
u/anish2good — 3 days ago

Two, in ink — a pair from one formula - manic

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-ink-pair — another  creature in ONE `cloud`, reimagined as a brush
// study: two fish circling on paper. The reference is a tweet-sized golf:
//   k = 5cos(i/44),  e = y/2-15,  d = mag(k,e)/3,  c = d/2 - t/3 + (i%2)·3,  y = i/253
//   point( (79 + d² + k²)·sin c + 200 + d³/4·cos(3t - d²/4) ,
//          99cos(c/2) + 4sin 2k + y/(77 sin(e/2) + 1e-4)·k·e + 200 )
// The whole trick is `(i%2)·3`: every other point is phase-shifted by 3 radians, so ONE
// formula draws TWO animals — a mirrored pair, orbiting a shared centre as `c` turns.
// The near-vertical dotted streaks are the original's own doing: `77 sin(e/2)` passes
// through zero, and the author's `+1e-4` guard bounds the blow-up instead of removing
// it. We keep the guard and the streaks; they fall like rain behind the pair.
//
// So this one is staged on PAPER rather than in the dark: `template("paper")`, no bloom,
// ink pooling dark along the bodies (`alpha` falls off with `d`) and drying to almost
// nothing at the fin tips. p5's semi-transparent `stroke(w,96)` was always ink.
//
// Faithful notes: p5's `mag` is `hypot`, `%` is `mod`, `**` is `^`; the p5 draw loop
// advances t by PI/80 per FRAME, so a frame-rate-free `t*1.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-ink-pair.manic
title("Two, in ink — a pair from one formula");
canvas("square");
template("paper");

// ---- the pair — `mod(i,2)` splits one formula into two animals ----
// Framing is the original's own, scaled: p5 draws into 400×400 about (200,200), so
// ×2.7 about (540,540) reproduces it at 1080, rain-streaks running off frame and all.
cloud(pair, 10000, #14141a, 0.95) {
  let yy = i / 253.0;
  let k = 5.0 * cos(i / 44.0);
  let e = yy / 2.0 - 15.0;
  let d = hypot(k, e) / 3.0;
  let T = t * 1.0;
  let c = d/2.0 - T/3.0 + mod(i, 2) * 3.0;
  let px = (79.0 + d*d + k*k) * sin(c) + (d^3)/4.0 * cos(T*3.0 - d*d/4.0);
  let py = 99.0*cos(c/2.0) + 4.0*sin(k*2.0) + yy/(77.0*sin(e/2.0) + 0.0001) * k * e;
  let x = 540 + px * 2.7;
  let y = 540 + py * 2.7;
  // ink pools along the body, dries out toward the fins
  let alpha = clamp(0.9 - d * 0.07, 0.16, 0.92);
  let r = 1.3;
}

// ---- annotations ----
caption(head, "Two, in ink", (540, 96), 34); hidden(head);
caption(sub, "one formula, split by mod(i,2)", (540, 152), 21); hidden(sub);
equation(eq, (540, 946), `c=\tfrac{d}{2}-\tfrac{t}{3}+(i\bmod 2)\cdot 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);
u/anish2good — 3 days ago
▲ 40 r/maniclang+1 crossposts

Crinoid — combing the current - manic

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);
u/anish2good — 3 days ago

Volume - solid - revolution - manic

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

// volumes-solid-of-revolution — Plate IX of the Problem Book, built from the
// classic slide: y = sqrt(x) rotated about the x-axis on [0, 4]; find the
// volume. Fig (a): the region + one red strip R(x) = sqrt(x). Fig (b): the
// solid, with that same strip now a red DISC. The slide's equation chain is
// replayed verbatim by rewrite: pi R^2 (thickness) -> pi ∫ x dx -> 8 pi.
// (revolve3 spins about the vertical axis, so we stand the x-axis upright -
//  then roll3(90) lays the finished solid back down, just like the book.)
title("Volumes - Solid of Revolution");
canvas("16:9");
template("paper");
masthead("The Problem Book", "Plate IX");

// ---------- HUD ----------
text(head, (cx, 96), "Volumes - Solid of Revolution"); display(head); cursor(head);
text(sub, (cx, 140), `$y=\sqrt{x}$ rotated about the x-axis on [0, 4] - find the volume of the solid`); size(sub, 26); color(sub, teal); hidden(sub);
text(cap, (cx, h - 40), ""); size(cap, 26);
text(figl, (455, 640), ""); size(figl, 24); color(figl, dim);
counter(vol, (w - 185, 130), 0, 2, "V = ", ""); color(vol, gold); hidden(vol);

// the slide's derivation, one living equation
equation(eqV, (955, 250), `V_{\text{disk}} \;=\; \pi R^{2}\,(\text{thickness})`, 30); hidden(eqV);

// ---------- fig (a): the region and one strip ----------
let gx = 240;
let gy = 520;
arrow(xax, (gx - 60, gy), (gx + 4*110 + 60, gy)); untraced(xax); stroke(xax, 3); tag(xax, f2);
arrow(yax, (gx, gy), (gx, 195)); untraced(yax); stroke(yax, 3); tag(yax, f2);
text(xl0, (gx, gy + 26), "0"); size(xl0, 22); color(xl0, dim); hidden(xl0); tag(xl0, f2);
text(xl4, (gx + 440, gy + 26), "4"); size(xl4, 22); color(xl4, dim); hidden(xl4); tag(xl4, f2);
plot(rq, (gx, gy), 110, 150, "sqrt(x)", (0, 4));
untraced(rq); stroke(rq, 4); color(rq, blue); tag(rq, f2);
dot(rider, (gx, gy), 7); color(rider, gold); glow(rider, 8); hidden(rider); tag(rider, f2);
area(arq, rq, 0, 0); color(arq, cyan); opacity(arq, 0.3); tag(arq, f2);
// the representative strip at x = 2.25 (declared low, rises into place)
rect(strip, (gx + 2.25*110, gy - 1.5*150/2 + 24), 14, 1.5*150);
color(strip, red); opacity(strip, 0.85); hidden(strip); tag(strip, f2);
text(rlab, (gx + 2.25*110 + 128, gy - 140), `$R(x)=\sqrt{x}$`); size(rlab, 26); color(rlab, red); hidden(rlab); tag(rlab, f2);

// ---------- fig (b): the solid ----------
camera3((5.2, -6.2, 3.6), (0, 0, 1.5), 42);
curve3(prof, "sqrt(t)", "0", "t", (0, 4)); untraced(prof); thick(prof, 0.04); color(prof, blue);
revolve3(ghost, (0, 0, 0), "sqrt(t)", (0, 4), 64);
finish3(ghost, "wire=1"); color(ghost, teal); opacity(ghost, 0.35); untraced(ghost);
revolve3(solid, (0, 0, 0), "sqrt(t)", (0, 4), 64);
finish3(solid, "shading=smooth depth=0.2 shadow=0.2"); color(solid, gold); opacity(solid, 0.9); hidden(solid);
// the same strip, now a red disc at x = 2.25 with R = 1.5
prism3(slice, (0, 0, 2.25), 48, 1.5, 0.16); color(slice, red); hidden(slice);
arrow3(rarr, (0, 0, 2.25), (1.5, 0, 2.25)); thick(rarr, 0.03); color(rarr, red); untraced(rarr);
text(rlab3, (cx, cy), `$R(x)=\sqrt{x}$`); size(rlab3, 26); color(rlab3, red); hidden(rlab3);
pin3(rlab3, (0.9, 0, 2.6), (0, -28));
text(ax4, (cx, cy), "x = 4"); size(ax4, 22); color(ax4, dim); hidden(ax4);
pin3(ax4, (0, 0, 4.2), (0, -14));

// ================= timeline =================

// ---- the problem
type(head, 1.1);
show(sub, 0.6);
say(figl, "Fig. (a) - the region and one strip", 0.3);
par { draw(xax, 0.6); draw(yax, 0.6); show(xl0, 0.5); show(xl4, 0.5); }
show(rider, 0.2);
par { draw(rq, 1.3); travel(rider, rq, 1.3, smooth); }
fade(rider, 0.3);
say(cap, `the region under $y=\sqrt{x}$, from 0 to 4`, 0.5);
to(arq, x, 4, 1.6, smooth);
par { show(strip, 0.4); shift(strip, (0, -24), 0.4, out); show(rlab, 0.5); }
pulse(strip, 0.8);
say(cap, `one strip at $x$, height $R(x)=\sqrt{x}$ - now SPIN it around the x-axis`, 0.5);
cue(whoosh);

// ---- the derivation begins
show(eqV, 0.6);
wait(0.9);
cue(tick);
rewrite(eqV, `V \;=\; \pi\!\int_{0}^{4}\big(\sqrt{x}\,\big)^{2}\,dx`, 0.9);
say(cap, "each strip sweeps a disc: area pi R squared, thickness dx", 0.5);
cue(tick);
rewrite(eqV, `V \;=\; \pi\!\int_{0}^{4} x\,dx`, 0.9);
say(cap, "square the square root - the integrand collapses to plain x", 0.5);
wait(0.8);

// ---- fig (b): the solid generated
say(figl, "Fig. (b) - the solid generated", 0.3);
fade(f2, 0.7);
draw(prof, 1.2);
par { turn3(prof, (0, 0, 2), z, 360, 2.4, smooth); draw(ghost, 2.4); }
show(solid, 1.2);
show(ax4, 0.5);
par { show(slice, 0.5); draw(rarr, 0.5); show(rlab3, 0.5); }
pulse(slice, 0.9);
say(cap, `the same strip, returned as a red DISC of radius $\sqrt{x}$`, 0.5);
wait(0.6);

// ---- evaluate: the number arrives
show(vol, 0.3);
cue(tick);
rewrite(eqV, `V \;=\; \pi\,\tfrac{x^{2}}{2}\,\Big|_{0}^{4}`, 0.9);
par { to(vol, value, 25.13, 2.0, smooth); fade(ghost, 1.5); }
cue(tick);
rewrite(eqV, `\boxed{\;V \;=\; \tfrac{\pi}{2}\,(16-0) \;=\; 8\pi \ \text{units}^{3}\;}`, 1.0);
cue(chime);
flash(slice, red);
say(cap, `$V = 8\pi \approx 25.13$ cubic units - exactly`, 0.5);
wait(0.8);

// ---- lay it down, like the book
say(cap, "and to match the textbook: lay the axis back down", 0.5);
roll3(90, 1.6, smooth);
breathe(eqV, 3, 0.05, 0, 5);
say(cap, "one strip, spun into a solid - volumes by discs", 0.6);
wait(2.4);
u/anish2good — 7 days ago

Ugly spheres -glsl - manic

Code

// glsl-spheres-field — a raymarched, infinitely-scrolling field of wobbling metaballs
// with floor/ceiling planes and step-count + occlusion shading, run in manic via the
// raw glsl() path. Reimagined from an E.C.H Shadertoy that sampled a noise TEXTURE on
// iChannel0 for per-sphere radii — manic's glsl() only injects iTime/iResolution/iMouse
// (+ iCam* with camera3) and has NO iChannel/texture channels, so that one lookup is
// swapped for a procedural hash (same intent: a stable [0,1] pseudo-random per sphere).
// See CAPABILITIES.md → "no Shadertoy input channels".
//
//   manic examples/glsl-spheres-field.manic
canvas("16:9");
template("black");

glsl(scene, `
const int MAX_STEPS = 200;
const int NUM_SPHERES = 12;

float sphere(vec3 pos, float radius, vec3 smpl) {
    return length(pos - smpl) - radius;
}

float plane(vec3 dir, float offset, vec3 smpl) {
    return dot(dir, smpl) + offset;
}

float dfDist(vec3 smpl) {
    float T1 = 10.0;
    float T2 = 2.0 * T1;

    float result = 10000.0;

    smpl.y += sin(smpl.z * 0.2 + iTime) * sin(iTime * 1.33)
              + sin(smpl.x * 0.3 + iTime) * sin(iTime * 3.22)
              + sin(smpl.x * 0.5 + smpl.z * 0.22 + iTime) * sin(iTime * 2.22 + smpl.z * 0.1);
    float o = floor((smpl.z + T1) / T2);
    smpl.x += o * 7.0;
    smpl.xz = mod(smpl.xz + T1, T2) - T1;

    for (int i = 0; i < NUM_SPHERES; i++) {
        float t = float(i) / float(NUM_SPHERES);
        float n = t + iTime * 0.25 + o * 0.5;
        vec3 pos = vec3(sin(n * 5.0) * 5.0, cos(n * 3.0) * 9.0, cos(n * 2.0) * 3.0 + 5.0);
        // was: texture(iChannel0, vec2(t*t)).x * 2.0 + 1.4  — no iChannel in manic.
        // Procedural hash of the same seed (t*t) → a stable [0,1] per-sphere random.
        float radius = fract(sin(t * t * 91.7) * 43758.5453) * 2.0 + 1.4;
        result = min(result, sphere(pos, radius, smpl));
    }

    result = min(result, plane(vec3(0, -1, 0), 10.0, smpl));
    result = min(result, plane(vec3(0, 1, 0), 10.0, smpl));

    return result;
}

vec3 dfNormal(vec3 smpl) {
    const float E = 0.04;

    float d0 = dfDist(smpl);
    float dX = dfDist(smpl + vec3(E, 0, 0));
    float dY = dfDist(smpl + vec3(0, E, 0));
    float dZ = dfDist(smpl + vec3(0, 0, E));

    return normalize(vec3(dX - d0, dY - d0, dZ - d0));
}

float dfOcclusion(vec3 smpl, vec3 normal) {
    float N = 1.0;
    return clamp(dfDist(smpl + normal * N) / N, 0.0, 1.0);
}

float trace(inout vec3 pos, vec3 dir, out vec3 normal) {
    int steps = 0;
    for (int i = 0; i < MAX_STEPS; i++) {
        steps++;
        float d = dfDist(pos);
        pos += d * dir * 1.0;

        if (d < 0.001) {
            break;
        }
    }

    normal = dfNormal(pos);
    return float(steps) / float(MAX_STEPS);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec3 opos = vec3(4.5, sin(iTime * 0.4) * 3.0 + 2.0, -7.0 + iTime * 3.0);
    vec3 pos = opos;
    vec3 dir = normalize(vec3((fragCoord.x - iResolution.x * 0.5) / iResolution.y, fragCoord.y / iResolution.y - 0.5, 1.0));
    vec3 normal;

    float steps = trace(pos, dir, normal);
    float occ = dfOcclusion(pos, normal);
    float fogAmt = 1.0 - exp(-distance(opos, pos) * 0.01);
    vec3 fogCol = vec3(0.2, 0.14, 0.18);

    vec3 diffuse = vec3(0.4, 0.5, 0.6) * dot(normal, normalize(vec3(1.0, 0.3, -1.0)));
    vec3 ambient = vec3(0.4, 0.2, 0.1);
    vec3 color = (ambient + diffuse) * vec3(1.0 - steps) + pow(1.0 - occ, 1.5) * vec3(1.0, 0.9, 0.8) * 0.8;

    color = mix(color, fogCol, fogAmt);
    color = (1.0 - exp(-color * 1.5)) * 1.3;
    fragColor = vec4(color, 1.0);
}
`);

wait(12);
u/anish2good — 7 days ago
▲ 76 r/maniclang+3 crossposts

How a neural network learns: gradient descent - manic

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("How a neural network learns: gradient descent");
canvas("16:9");
template("black");

camera3((6, -8.5, 5.5), (0, 0, 0.7), 40);
axes3(world, (0, 0, 0), 2.4);
surface3(f, "exp(-x*x + cos(2*y))", (-2, 2), (-2, 2), 34);
opacity(f, 0.42);

slice3(sx, f, x, 0.6, 0.4, #ff3ea5);   // hold x → cross-section along y (∂f/∂y)
slice3(sy, f, y, 0.4, 0.6, #ffb020);   // hold y → cross-section along x (∂f/∂x)
gradient3(grad, f, 0.6, 0.4);
descend3(desc, f, 0.7, 0.35, 0.25, 45, #7cff3e);

equation(feq, (232, 96), `f(x,y)=e^{-x^2+\cos 2y}`, 28);
equation(ceq, (232, 150), `\text{cost } C(w_1,\dots,w_n)`, 24); color(ceq, dim);
equation(geq, (1040, 632), `\nabla f=\begin{bmatrix}\partial f/\partial x\\ \partial f/\partial y\end{bmatrix}`, 28);
text(narr, (640, 668), "A network's cost is one number over millions of weights.", 24);

hidden(sx); hidden(sx.slope);
hidden(sy); hidden(sy.slope);
hidden(grad); hidden(geq); hidden(narr);
hidden(desc); hidden(desc.ball);
untraced(desc); untraced(desc.ball);   // start un-drawn so the descent can roll on

// A MOVIE camera runs concurrently with the story: it swings around and up so the
// stacked slices + gradient are never hidden behind the peak, then drops low to
// follow the ball rolling into the valley. (orbit3 composes in `par`.)
par {
  seq {
    orbit3(-18, 26, 9.5, 5.0, smooth);   // establish: front-left, slightly high
    orbit3(40, 36, 9.2, 6.5, smooth);    // swing right & rise to reveal the STACK
    orbit3(62, 22, 8.4, 6.0, smooth);    // drop low, come round to watch the descent
    orbit3(74, 24, 8.4, 4.5, smooth);    // settle on the valley
  }
  seq {
    wait(0.5);
    show(narr);
    show(ceq);
    wait(2.2);

    say(narr, "Picture it as a landscape over just two of them.");
    wait(2.0);

    say(narr, "To read ∂f/∂y, hold x constant — that slices the surface to one curve.");
    par { show(sx); show(sx.slope); }
    wait(2.4);

    say(narr, "Hold y constant instead, and this slice's steepness is ∂f/∂x.");
    par { show(sy); show(sy.slope); }
    wait(2.4);

    say(narr, "Stack the two partials and you get the gradient — steepest ascent.");
    par { show(grad); show(geq); }
    wait(2.8);

    say(narr, "To LEARN, step the other way: downhill, along −∇f.");
    par { show(desc); show(desc.ball); }
    par { draw(desc, 3.0); draw(desc.ball, 3.0); }   // the ball rolls, the trail draws
    wait(0.6);

    say(narr, "It rolls into a valley — a minimum of the cost. That is learning.");
    pulse(desc.ball);
    wait(2.6);
  }
}
u/anish2good — 6 days ago
▲ 10 r/maniclang+4 crossposts

A fractal dawn, and a murmuration - manic

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

// glsl-fractal-nebula — a twigl-style 3D fractal fold as a soft gold dawn field
// with blue frost-ferns (raw `glsl`, accumulated into a LOCAL vec3 — the `o.rgb +=`
// in-loop idiom miscompiles on the Metal backend), and a murmuration `cloud`
// sweeping across it: a per-pixel field and a particle system sharing one frame.
// Both pure in (pixel/i, t), so the whole scene scrubs and records.
//
//   manic examples/glsl-fractal-nebula.manic
title("A fractal dawn, and a murmuration");
canvas("16:9");
template("black");

glsl(scene, `
mat3 rotate3D(float angle, vec3 axis){
    axis = normalize(axis);
    float s = sin(angle), c = cos(angle), oc = 1.0 - c;
    return mat3(
        oc*axis.x*axis.x + c,        oc*axis.x*axis.y - axis.z*s, oc*axis.z*axis.x + axis.y*s,
        oc*axis.x*axis.y + axis.z*s, oc*axis.y*axis.y + c,        oc*axis.y*axis.z - axis.x*s,
        oc*axis.z*axis.x - axis.y*s, oc*axis.y*axis.z + axis.x*s, oc*axis.z*axis.z + c
    );
}
vec3 hsv(float h, float s, float v){
    vec3 rgb = clamp(abs(mod(h*6.0 + vec3(0.0,4.0,2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
    return v * mix(vec3(1.0), rgb, s);
}

void mainImage(out vec4 o, in vec2 FC){
    vec2  r = iResolution.xy;
    float t = iTime;

    vec3  col = vec3(0.0);            // accumulate here, not into o
    float i = 0., g = 0., e = 0., s = 0.;
    for(int n = 0; n < 98; n++){
        i += 1.0;
        vec3 p = vec3((FC.xy-.5*r)/r.y*5. + vec2(0,9), g)
               * rotate3D(-1.1 - cos(t*.15)*.1, vec3(1, 11.+sin(t)*.15, -1.5));
        s = 2.;
        for(int j = 0; j < 19; j++){
            s *= e = 7.1/dot(p, p*.51);
            p = vec3(.08,4,-1) - abs(abs(p)*e - vec3(3,4,3));
        }
        g += p.y/s;
        s = log2(s)/exp(e);
        col += .01 - hsv(.1, g*.016 - e*.3, s/2e2);   // original's o.rgb += …, into the local
    }

    o = vec4(col, 1.0);
}
`);

// a murmuration sweeping across the still dawn field — a cohesive blob of birds
// (golden-angle scatter, denser core) whose centre sweeps a path, stretched along
// motion and banked into each turn, breathing organically. Pure in (i, t).
cloud(flock, 9000, #e8f6ff, 0.8) {
  let s = i/9000;                              // 0..1 through the flock
  let ang = i*2.39996;                         // golden-angle scatter
  let rad = sqrt(s);                           // wispy toward the edge
  let sw = ang + rad*3*sin(t*0.5) + t*0.6;     // the interior swirls (shape-shifting)
  let taper = 1 - 0.45*s;                      // tail thins out
  let ex = rad*cos(sw)*235*taper;              // elongated along motion…
  let ey = rad*sin(sw)*88*taper;               // …narrower across
  let turb = 70*rad*rad;                       // tendrils: turbulence grows at the edge
  let bx = ex + turb*sin(i*0.7 + t*2.2);
  let by = ey + turb*cos(i*0.9 + t*1.9);
  let phase = t*0.45;
  let cx = 640 + 330*sin(phase);               // the flock sweeps left↔right…
  let cy = 250 + 80*sin(phase*1.6 + 0.7);      // …rising and dipping
  let bank = 0.6*cos(phase);                   // and banks into each turn
  let rx = bx*cos(bank) - by*sin(bank);
  let ry = bx*sin(bank) + by*cos(bank);
  let grow = tanh(t*0.6 + 0.1);
  let x = cx + rx*grow;
  let y = cy + ry*grow;
  let hue = mod(210 + s*14 + t*5, 360);
}

wait(12);
u/anish2good — 7 days ago
▲ 16 r/maniclang+2 crossposts

Area of a circle = πr² - manic

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

// Area = πr², as a SWARM — and the LIMIT that finishes the proof.
// The same particles fill a disc, flow into a lumpy wedge-strip, then the humps
// MULTIPLY and FLATTEN (4 → 8 → 16 wedges …) until the edge is straight: a
// πr × r rectangle. Nothing is added or removed — the count is the area (πr²),
// conserved the whole way. One `cloud`, all formula-driven.
//
//   manic examples/art-circle-area-proof.manic
title("Area of a circle = πr²");
canvas("16:9");
template("black");

// on-screen heading, top-centre, held throughout
text(hdr, (640, 74), "Circle Area of Proof — Manic", 32);

cloud(swarm, 3200) {
  // ---- uniform grid index → (fx, fy) in the unit square ---------------------
  let cols = 80;
  let ci = mod(i, cols);
  let ri = (i - ci) / cols;              // integer row 0..39
  let fx = ci / 79;                       // 0..1 across the width
  let fy = ri / 39;                       // 0..1 top → bottom
  // a little hash jitter so the grid reads as a filled field, not a lattice
  let jx = (mod(sin(i * 12.9898) * 43758.5453, 1) - 0.5) * 7;
  let jy = (mod(sin(i * 78.2330) * 43758.5453, 1) - 0.5) * 7;

  // ---- destination: a parallelogram with SCALLOPED (wedge) edges ------------
  let wdt = 565; let hlf = 90;            // base πr ≈ 565, height r = 180
  let x0 = 313; let yc = 340; let slnt = 90;
  // refinement s: 0 (few coarse wedges) → 1 (many fine wedges → rectangle)
  let s = 0.5 * (1 + tanh((t - 5.6) * 0.7));
  let nh = 2 + 6 * s;                      // humps per edge: 2 → 8
  let amp = 48 * (1 - s) + 2;              // hump depth: 50 → 2 (flattens)
  let wv = amp * cos(6.2831853 * nh * fx);
  let topE = yc - hlf - wv;               // top edge bulges up at the humps
  let botE = yc + hlf + wv;               // bottom edge bulges down
  let sx = x0 + fx * wdt + (1 - fy) * slnt + jx;
  let sy = topE + fy * (botE - topE) + jy;

  // ---- start: a uniform disc of the SAME area (golden-angle sunflower) ------
  let gr = sqrt((i + 0.5) / 3200);
  let ang = i * 2.399963;
  let dx = 640 + 180 * gr * cos(ang);
  let dy = 340 + 180 * gr * sin(ang);

  // ---- blend disc → strip, then the strip refines to a rectangle ------------
  let b = 0.5 * (1 + tanh((t - 3.2) * 1.1));
  let x = dx * (1 - b) + sx * b;
  let y = dy * (1 - b) + sy * b;

  let hue = mod(330 - gr * 140, 360);      // Manic neon: magenta core → cyan rim
  let sat = 0.9;
  let val = 0.6;                           // <1 shows the hue; additive glow re-brightens overlaps
  // varied radius > 2.5px → true ROUND discs (≤2.5px render as squares), with size grain
  let rnd = mod(sin(i * 91.7) * 43758.5453, 1);
  let r = 2.8 + 2.2 * rnd;
}

// additive glow: dense/overlapping points accumulate into light — soft nebula cores
glow(swarm, 4);

text(cap, (640, 630), "π r² particles — a disc's worth.", 24);
hidden(cap);

wait(0.6);
show(cap);
wait(1.8);

say(cap, "Cut into wedges and re-lay them — a lumpy strip.");
wait(2.2);

say(cap, "More wedges, finer and finer — the humps flatten…");
wait(2.6);

say(cap, "…in the limit, a πr × r rectangle. Area = π r².");
wait(2.4);
u/anish2good — 8 days ago
▲ 2 r/maniclang+1 crossposts

Sine · derivative · area in art form - manic

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

// Sine, its derivative, its Riemann area & a riding tangent — all PARTICLE ART.
// 12000 dots in five families, one formula, no plot/coords/riemann built-ins:
//   0  SINE ribbon         (cyan)   — a thick glowing wave-swarm
//   1  COSINE ribbon        (gold)   — the derivative, cos x = the slope
//   2  coordinate GRID      (faint)  — the x/y plane in dots
//   3  RIEMANN columns      (magenta)— particles fill the strips under the wave
//   4  riding TANGENT swarm  (white)  — a line that tilts to cos x as it sweeps
// The families CROSS-FADE in beats so each idea reads on its own, then a finale.
//
//   manic examples/art-calculus-sine.manic
title("Sine · derivative · area");
canvas("16:9");
template("black");

text(hdr, (640, 70), "Sine · its Derivative · its Area — Manic", 30);

cloud(field, 12000) {
  let g = floor(i / 2400);                       // family 0..4
  let m0 = clamp(1 - max(g,   -g),   0, 1);
  let m1 = clamp(1 - max(g-1, 1-g),  0, 1);
  let m2 = clamp(1 - max(g-2, 2-g),  0, 1);
  let m3 = clamp(1 - max(g-3, 3-g),  0, 1);
  let m4 = clamp(1 - max(g-4, 4-g),  0, 1);
  let li = mod(i, 2400);
  let loc = li / 2399;

  let ox = 640; let oy = 384;
  let sx = 92; let sy = 118;
  let mx = (loc * 2 - 1) * 3.14159;              // math x ∈ [-π, π]
  let ph = mx + t * 0.9;                          // the wave travels (gentle)
  let sp = rand(i) + rand(i + 4051) - 1;          // -1..1, dense near 0
  let asp = max(sp, -sp);

  // 0/1 — thick sine & cosine ribbons
  let wvX  = ox + mx * sx;
  let sinY = oy - (sin(ph) + sp * 0.24) * sy;
  let cosY = oy - (cos(ph) + sp * 0.24) * sy;

  // 2 — faint particle grid
  let gridX = ox + (mod(li, 52) / 51 * 2 - 1) * 320;
  let gridY = oy - (floor(li / 52) / 51 * 2 - 1) * 178;

  // 3 — Riemann columns: 16 strips, particles fill axis → sin height (signed)
  let bi = floor(loc * 16);
  let barMX = 0.0 - 3.14159 + (bi + 0.5) / 16 * 6.28318;
  let barH = sin(barMX + t * 0.9);
  let rmX = ox + barMX * sx + (rand(i + 11) - 0.5) * (6.28318 / 16 * sx * 0.78);
  let rmY = oy - rand(i + 23) * barH * sy;

  // 4 — a tangent line that sweeps and tilts to the slope cos(x0)
  let x0 = 0.0 - 2.3 + mod(t * 0.4, 1) * 4.6;
  let ss = (loc * 2 - 1) * 0.9;
  let tanH = sin(x0 + t * 0.9) + cos(x0 + t * 0.9) * ss;   // value + slope·offset
  let tgX = ox + (x0 + ss) * sx;
  let tgY = oy - tanH * sy + (rand(i + 77) - 0.5) * 8;

  let x = (m0 + m1) * wvX + m2 * gridX + m3 * rmX + m4 * tgX;
  let y = m0 * sinY + m1 * cosY + m2 * gridY + m3 * rmY + m4 * tgY;

  // ---- beats: each idea rises, then clears for the next ----------------------
  let rmA = clamp((t - 4.5) * 1.0, 0, 1) * clamp((10.0 - t) * 1.0, 0, 1); // area 4.5–10
  let tgA = clamp((t - 9.5) * 1.0, 0, 1);                                 // tangent 9.5→end
  let cosDim = 1 - 0.55 * rmA;                    // derivative steps back while area shows

  let hue = m0 * 192 + m1 * 46 + m2 * 210 + m3 * 328 + m4 * 50;
  let sat = m0 * 1.0 + m1 * 1.0 + m2 * 0.3 + m3 * 0.8 + m4 * 0.3;
  let core = 0.5 * (1 - 0.7 * asp);
  let val = m0 * core + m1 * core + m2 * 0.14 + m3 * 0.32 + m4 * 0.75;
  let alpha = m0 * 1.0 + m1 * cosDim + m2 * 0.45 + m3 * rmA * 0.8 + m4 * tgA;
  let r = m2 * 1.8 + (m0 + m1) * (2.4 + rand(i + 88))
        + m3 * (2.2 + rand(i + 5)) + m4 * (2.7 + rand(i + 9));
}

glow(field, 2);

text(cap, (640, 700), "cyan sin x   ·   gold cos x — its slope.", 22);
hidden(cap);

wait(1.0);
show(cap);
wait(3.4);

say(cap, "Riemann strips — the area under the wave, in dots.");
wait(5.0);

say(cap, "The sum clears; a tangent rides — its tilt IS cos x.");
wait(4.0);

say(cap, "A function, its slope, its area — one swarm.");
wait(3.2);
u/anish2good — 8 days ago

Manic Rhyme

Ahem.

Forget about Python dependency hell, Where pip install is a cursed little spell. If you want your vectors to dance and to play, Just fire up Manic and get on your way!

No timeline dragging, no keyframe despair, Just pure, clean syntax that's light as the air. It’s declarative, slick, and completely untamed, A 3b1b masterpiece, perfectly framed.

reddit.com
u/anish2good — 9 days ago