Circuits — schematics that work
A netlist in, a working schematic out: Modified Nodal Analysis under easy builtins, so every voltage, current and glow on screen was solved rather than drawn. The schematic draws itself one component at a time and each PREFIX of it is solved, so nothing flows until the loop closes; cut takes a component out of the circuit and what is left is re-solved from scratch. See the circuits guide.
Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video).
circuit-broken-filament
NCERT Class 9, Fig. 3.17, Q3 — if the filament of one of the lamps is broken, will
the other glow? Two lamps in series share one current, and each lamp’s halo carries
P = V·I, so when cut breaks one filament BOTH go dark and the meter falls to 0.00 mA on
its own. The answer is nowhere in the file. cut decides the physics and the scene decides
the theatre: since the question is about a broken filament rather than a lamp removed from
its holder, the symbol comes back with show and a slash marks the break.
// Class 9 · NCERT Science, Fig. 3.17, question 3
//
// "If the filament of one of the lamps is broken, will the other glow?
// Justify your answer."
//
// The answer is not asserted anywhere in this file. Two lamps sit in series across a cell; the
// solver gives them one shared current, and each lamp's halo carries P = V·I on its opacity, so
// they light because they are dissipating power. `cut` takes one filament out of the circuit —
// the circuit that is left is re-analysed and re-solved from scratch — and both lamps go dark
// because both are now dissipating nothing. The meter falls to zero on its own.
//
// A series circuit is one path. Break it anywhere and you have broken it everywhere.
title("if one filament breaks");
canvas("16:9");
template("paper");
text(brand, (640, 40), "maniclang.com");
display(brand);
size(brand, 20);
color(brand, dim);
// ── the schematic, drawn the way the book draws it: two lamps, one cell, one loop ──
//
// And no ground symbol, exactly as the book draws it: a ground only NAMES the reference node, and
// this loop has nothing to reference. The kit elects the cell's negative terminal.
circuit(fig, (560, 330), `
lamp 1 0 5 0 r=120 name=L1
lamp 5 0 9 0 r=120 name=L2
wire 9 0 9 4
wire 9 4 6 4
dc-voltage 6 4 4 4 v=6
wire 4 4 1 4
wire 1 4 1 0
`, 62, 0, 0);
current(fig, 1.4, circle, crimson, 4);
probe(fig, L1, (-14, -52));
// The halo behind a lamp is the lamp's own colour by default; on a cream page a warm one reads as
// light. It is an ordinary entity tagged {id}.glow, so a core verb restyles it.
color(fig.glow, orange);
// The object beside the symbol, because a beginner meeting ⊗ for the first time has no reason
// to know it means a lamp. Vector art, so it draws and fades like anything else.
svg(bulb, (1090, 250), "assets/svg/heroicons/light-bulb.svg", 118);
color(bulb, gold);
hidden(bulb);
text(l1, (1090, 340), "the lamp");
hidden(l1);
size(l1, 22);
color(l1, dim);
// ── the question, and then the answer ──
text(q, (640, 120), "one filament breaks — does the other still glow?");
hidden(q);
size(q, 30);
color(q, ink);
// The break itself. `cut` decides the PHYSICS; the theatre is the author's, and this question is
// about a broken filament rather than a lamp taken out of its holder — so the symbol stays on the
// page and a slash marks the break. The lamp is still there. It just cannot carry anything.
// L1's symbol sits at (436, 206): a circuit is placed by the CENTRE of its bounding box, so a
// grid point lands at `origin + p × unit` where `origin = centre − span/2 − lo × unit`. Here that
// is (250, 206), and L1 spans grid x 1→5, so its middle is x = 250 + 3 × 62.
line(break1, (418, 188), (454, 224));
hidden(break1);
color(break1, crimson);
stroke(break1, 3);
text(a1, (1075, 470), "one path,");
hidden(a1);
size(a1, 26);
color(a1, crimson);
text(a2, (1075, 508), "one current");
hidden(a2);
size(a2, 26);
color(a2, crimson);
text(a3, (1075, 560), "break it anywhere");
hidden(a3);
size(a3, 26);
color(a3, ink);
text(a4, (1075, 598), "and it is broken");
hidden(a4);
size(a4, 26);
color(a4, ink);
text(a5, (1075, 636), "everywhere");
hidden(a5);
size(a5, 26);
color(a5, ink);
// ── the beats ──
show(q, 0.6);
wait(0.3);
// the loop closes and both lamps come up together: one current, so one brightness
run(fig, 6.0);
par {
show(bulb, 0.7);
show(l1, 0.5);
}
wait(0.6);
par {
show(a1, 0.4);
show(a2, 0.4);
}
wait(1.0);
// break the filament of one lamp
cut(fig, L1, 1.0);
par {
// the lamp comes back to the page — it was never removed, only broken
show(fig.L1, 0.4);
// `show`, not `draw`: `hidden` is an OPACITY, and `draw` animates trace — a hidden entity
// traced all the way in is still invisible
show(break1, 0.35);
fade(bulb, 0.6);
fade(l1, 0.6);
}
run(fig, 4.0);
wait(0.4);
par {
show(a3, 0.4);
show(a4, 0.4);
show(a5, 0.4);
}
wait(1.4);
// mend it, and the whole loop lights again
par {
fade(break1, 0.4);
reconnect(fig, L1, 1.0);
}
run(fig, 4.0);
wait(0.9);
circuit-does-current-flow
The teaching beat end to end: a battery, a switch, a lamp and the wires between them
drawn in a teacher’s order, the lamp lighting the moment the last wire closes the loop
(because that prefix was solved), a zoom+cam+framebox close-up, then cut on the
return wire — dots stop, lamp dark, probe to zero — and reconnect.
// Does current flow?
//
// The oldest question in electricity, answered by a circuit that draws itself and then gets
// taken apart. Every number on screen is solved: the schematic is built one component at a
// time and each PREFIX of it is analysed, so nothing moves until the loop closes — and when
// the return wire is cut, the lamp's current goes to zero because the circuit that is left
// was solved from scratch, not because anything was dimmed.
//
// Nothing here is a circuit-specific animation verb. The drawing, the framing and the
// close-up are Manic's core kit; the physics is Modified Nodal Analysis.
title("does current flow?");
canvas("16:9");
template("black");
text(brand, (640, 44), "maniclang.com");
display(brand);
size(brand, 22);
color(brand, dim);
circuit(loop, (600, 400), `
dc-voltage 0 6 0 0 v=9
wire 0 0 4 0
switch 4 0 7 0 closed=1
wire 7 0 11 0
lamp 11 0 11 6 r=180 name=L1
wire 11 6 6 6 name=RET
wire 6 6 0 6
`, 58);
// The animator's dial on the current: a little quicker than the circuit's own pace, because a
// short film wants pace. `speed` scales every branch by the same factor, so the ratio between
// branches — the only thing dot speed is allowed to mean — is untouched. `current` also takes a
// shape (circle, square, diamond), a colour (any palette name or #rrggbb) and a size.
current(loop, 1.6);
probe(loop, L1, (70, -6));
// the highlight box is declared up front and hidden — a ctor belongs above the timeline
framebox(ring, loop.L1, 10);
hidden(ring);
text(q1, (640, 118), "one loop, one lamp");
hidden(q1);
size(q1, 30);
color(q1, cyan);
text(q2, (640, 118), "cut the return: still a lamp, no current");
hidden(q2);
size(q2, 30);
color(q2, magenta);
text(q3, (640, 118), "put it back");
hidden(q3);
size(q3, 30);
color(q3, lime);
// the schematic draws itself in netlist order, and the current starts by itself when the
// last wire closes the loop
show(q1, 0.5);
run(loop, 7.0);
wait(0.4);
// look closely at the one component that is doing the work
par {
zoom(1.7, 1.2);
cam((820, 420), 1.2);
show(ring, 0.8);
}
wait(1.0);
par {
zoom(1.0, 1.0);
cam((640, 360), 1.0);
fade(ring, 0.6);
}
// take the return wire out — and the lamp goes dark because there is nothing to light it
par {
fade(q1, 0.4);
show(q2, 0.4);
}
cut(loop, RET, 0.9);
run(loop, 3.5);
wait(0.6);
// and back
par {
fade(q2, 0.4);
show(q3, 0.4);
}
reconnect(loop, RET, 0.9);
run(loop, 3.5);
wait(0.8);
circuit-kit-promo
Six circuits on a textbook page, each erasing itself, drawing itself back, naming itself
and running its own current — Ohm’s law, a divider, RC, RL, a rectifier and an LED — then
the whole board alive at once. Six current(...) styles: shape, colour and pace, with the
proportionality between branches untouched.
// 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("black");
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 ──
//
// No `ground` line anywhere: a ground is a REFERENCE, not a component. MNA needs one node to
// measure from, and with none written the kit elects the source's negative terminal — so a single
// loop solves identically either way, and the symbol would only be clutter. Write one when a
// circuit has several sources or loops and you want to say which node is zero.
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
`, 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
`, 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
`, 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
`, 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
`, 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
`, 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);