Optics — light as geometry
Easy builtins with the real physics underneath — Snell’s law and Sellmeier dispersion — so the bending and the colours are earned, not painted. Each is static geometry that animates by a parameter sweep: call run(id).
Each block is the whole file — copy it into x.manic and run manic x.manic (live) or --record out (video).
refraction
Snell’s law you can watch: a ray crossing from air into glass bends toward the normal,
and run sweeps the incidence angle so the refracted ray swings — the live in/out
read-outs are the true angles. Start in the denser medium and it shows total internal
reflection past the critical angle (refract).
// ============================================================================
// refraction.manic — Snell's law you can watch (real optics)
// ----------------------------------------------------------------------------
// The `optics` kit's first builtin: `refract` draws a light ray meeting the
// boundary between two media and bending. `run` SWEEPS the incidence angle, so
// the refracted ray genuinely swings — and the "in"/"out" read-outs are the
// true Snell angles, not painted. Here: air (n=1.00) into crown glass (n=1.52).
// ============================================================================
title("Refraction — Snell's Law");
canvas("16:9");
// air on top, glass below; no fixed angle → run(r) sweeps it
refract(r, (640, 380), 1.0, 1.52);
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "a ray of light, crossing from air into glass…", 0.5);
wait(0.5);
par {
run(r, 7);
seq {
say(cap, "shallow angle — it barely bends", 0.3);
wait(2.2);
say(cap, "steeper in → it bends MORE, toward the normal", 0.3);
wait(2.6);
say(cap, "sin(in) / sin(out) is fixed — THAT ratio is the refractive index", 0.3);
}
}
wait(1.2);
lens
A converging lens: a parallel beam bends to meet at the focal point F (ideal thin lens —
every ray passes through F). run sweeps the focal length, so the focus slides in as the
lens gets stronger (lens).
// ============================================================================
// lens.manic — a converging lens, and where it focuses (real optics)
// ----------------------------------------------------------------------------
// The `optics` kit's `lens` builtin: a beam of parallel rays is bent by a
// converging lens to meet at the focal point F. `run` SWEEPS the focal length,
// so you watch the focus slide in toward the lens — a shorter focal length is
// a stronger lens. Ideal thin lens (every parallel ray passes through F).
// ============================================================================
title("Converging Lens");
canvas("16:9");
lens(l, (620, 360));
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "parallel light meets a converging lens…", 0.5);
wait(0.5);
par {
run(l, 7);
seq {
say(cap, "every ray bends to the SAME point — the focus, F", 0.3);
wait(2.6);
say(cap, "shorter focal length = a stronger lens → the focus slides IN", 0.3);
wait(2.4);
say(cap, "past F the rays cross and spread apart again", 0.3);
}
}
wait(1.2);
prism
White light into a prism, out as a RAINBOW — each colour traced through both faces with
its own refractive index (real Sellmeier dispersion), so blue bends more than red because
glass genuinely slows blue more. run sweeps the incidence angle and the fan widens (prism).
// ============================================================================
// prism.manic — white light into a rainbow (real dispersion)
// ----------------------------------------------------------------------------
// The `optics` kit's `prism`: white light enters a triangular prism and splits
// into a spectrum. Each colour is traced through both faces with its OWN
// refractive index (real Sellmeier dispersion) — blue bends more than red
// because glass genuinely slows blue more. `run` sweeps the incidence angle,
// so the fan swings and its spread widens.
// ============================================================================
title("Prism — White Light Splits");
canvas("16:9");
prism(p, (560, 400), "sf11"); // dense flint — a wide, vivid spectrum
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "one beam of white light, into a prism…", 0.5);
wait(0.5);
par {
run(p, 7);
seq {
say(cap, "it comes out as a RAINBOW — the colours were always there", 0.3);
wait(2.6);
say(cap, "blue bends more than red: glass really does slow blue more", 0.3);
wait(2.4);
say(cap, "steeper in → the spectrum fans WIDER (real Sellmeier dispersion)", 0.3);
}
}
wait(1.2);
achromat
The optics capstone — chromatic aberration and its fix: a single lens focuses blue nearer
than red, so white light never comes to one point; run sweeps in the achromatic doublet
and the colours snap back to a single sharp focus (achromat).
// ============================================================================
// achromat.manic — chromatic aberration, and the fix (real optics)
// ----------------------------------------------------------------------------
// The optics kit's capstone. A single lens can't focus all colours to one
// point: it bends blue more than red (its index is higher for blue — real
// dispersion), so blue focuses NEARER and red FARTHER. `run` then sweeps in the
// achromatic doublet (crown + flint) and the two foci slide back together to a
// single sharp point — the trick every camera and telescope lens uses.
// ============================================================================
title("The Achromatic Doublet");
canvas("16:9");
achromat(ac, (540, 360));
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "one simple lens — but the colours don't agree…", 0.5);
wait(0.6);
par {
run(ac, 7);
seq {
say(cap, "blue focuses NEARER, red FARTHER — chromatic aberration", 0.3);
wait(2.6);
say(cap, "now add a flint element behind it — the achromatic doublet", 0.3);
wait(2.2);
say(cap, "…and the colours snap back to ONE sharp focus", 0.3);
}
}
wait(1.2);
refraction-paper
Snell’s law as a template("paper") TEXTBOOK figure: inked media labels, the normal, and
the law itself, with a camera easing in on the bending point as run sweeps the angle —
the geometric builtins suit paper (refract + annotation + camera).
// ============================================================================
// refraction-paper.manic — Snell's law as a textbook figure (annotated)
// ----------------------------------------------------------------------------
// The `refract` sim dressed as a physics-class page: `template("paper")` inks
// it on cream, labelled media + normal + the law itself, and a gentle camera
// push on the bending point while `run` sweeps the incidence angle. Elevation
// lens: textbook annotation + camera. (Paper suits the geometry; the coloured
// dispersion demos stay on a dark bench where light glows.)
// ============================================================================
title("Refraction — Snell's Law");
canvas("16:9");
template("paper");
refract(r, (660, 400), 1.0, 1.52);
color(r.incident, red); // gold washes out on cream — ink the incoming ray red
color(r.thetai, red);
// ---- textbook annotations (screen-fixed) ----
text(mAir, (150, 300), "air n = 1.00"); color(mAir, dim); size(mAir, 22); sticky(mAir);
text(mGla, (150, 500), "glass n = 1.52"); color(mGla, dim); size(mGla, 22); sticky(mGla);
text(nlab, (708, 250), "normal"); color(nlab, dim); size(nlab, 18); sticky(nlab);
text(law, (cx, 92), "n1 · sin(in) = n2 · sin(out)"); color(law, fg); size(law, 30); bold(law); sticky(law);
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "light bends as it crosses into glass — Snell's law", 0.5);
wait(0.5);
par {
run(r, 8);
seq {
par { cam((660, 400), 2.2, smooth); zoom(1.18, 2.2, smooth); } // ease in on the boundary
say(cap, "steeper in → it bends more, toward the normal", 0.3);
wait(3.0);
say(cap, "…but sin(in) / sin(out) never changes — that ratio IS n", 0.3);
}
}
wait(1.2);
lens-paper
The converging lens inked on paper and narrated by a TYPEWRITER caption (a different
elevation lens): labelled parallel rays and focal point F while run slides the focus
(lens + type).
// ============================================================================
// lens-paper.manic — a converging lens as a textbook figure (annotated)
// ----------------------------------------------------------------------------
// The `lens` sim inked on paper, narrated by a TYPEWRITER caption (a different
// elevation lens from refraction-paper's camera), with labelled parallel rays
// and focal point while `run` sweeps the focal length. Ideal thin lens.
// ============================================================================
title("Converging Lens");
canvas("16:9");
template("paper");
lens(l, (640, 360));
// ---- textbook annotations (screen-fixed) ----
text(inlab, (150, 250), "parallel rays"); color(inlab, dim); size(inlab, 22); sticky(inlab);
text(flab, (980, 300), "focal point F"); color(flab, dim); size(flab, 22); sticky(flab);
arrow(farr, (980, 320), (900, 356)); color(farr, dim); sticky(farr);
// a typewriter caption performs the narration (untraced → type reveals it)
text(cap, (cx, h - 48), "a lens bends parallel light to a single point"); color(cap, fg); size(cap, 26); bold(cap); untraced(cap); sticky(cap);
// ================= THE SCENE =================
type(cap, 1.6); // type the line out
wait(0.4);
par {
run(l, 8);
seq {
wait(0.6);
say(cap, "every ray meets at the focus, F", 0.3);
wait(2.8);
say(cap, "a shorter focal length is a stronger lens — F slides in", 0.3);
}
}
wait(1.2);
prism-cinematic
The prism on a dark optics bench where the spectrum GLOWS (a rainbow washes out on paper):
the colour names pop in word-by-word (wordpop) as the fan spreads and the camera flies
toward it — KINETIC-TYPE elevation (prism).
// ============================================================================
// prism-cinematic.manic — white light into a rainbow (annotated, dark bench)
// ----------------------------------------------------------------------------
// The `prism` sim on a dark optics bench, where the spectrum GLOWS (a rainbow
// washes out on paper — light is a dark-background subject). Elevation lens:
// KINETIC TYPE — the colour names pop in word-by-word (`wordpop`) as the fan
// spreads, plus a camera push toward the exit spectrum.
// ============================================================================
title("Prism — White Light Splits");
canvas("16:9");
prism(p, (560, 400), "sf11");
// the colour names, popped in one at a time over the fan
caption(names, "red orange yellow green blue violet", (cx + 40, 150), 26, gold);
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "one beam of white light, into a prism…", 0.5);
wait(0.5);
par {
run(p, 8);
seq {
par { cam((820, 430), 2.6, smooth); zoom(1.22, 2.6, smooth); } // fly toward the spectrum
say(cap, "…out as a rainbow — the colours were always hidden inside", 0.3);
wordpop(names, 0.22); // the colours pop in
wait(1.6);
say(cap, "blue bends most, red least — real Sellmeier dispersion", 0.3);
wait(1.6);
}
}
par { cam((cx, 400), 1.2, smooth); zoom(1.0, 1.2, smooth); }
wait(1.0);
achromat-cinematic
The achromat with the CAMERA magnifying the focal region so the red/blue split is dramatic,
a bracelabel marking the aberration gap that closes as run sweeps in the doublet and
the colours merge (achromat + camera + brace).
// ============================================================================
// achromat-cinematic.manic — chromatic aberration & its fix (annotated, dark)
// ----------------------------------------------------------------------------
// The `achromat` capstone on a dark bench, where the coloured foci glow.
// Elevation lens: the CAMERA magnifies the focal region so the red/blue split
// is dramatic, and a `bracelabel` marks the aberration gap — which then closes
// as `run` sweeps in the doublet and the colours merge to one point.
// ============================================================================
title("The Achromatic Doublet");
canvas("16:9");
achromat(ac, (540, 360));
// bracket the axial gap between the two foci (fades as they merge)
bracelabel(gap, (852, 406), (908, 406), "CA", 20); color(gap, red);
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "one simple lens — but the colours refuse to agree…", 0.5);
wait(0.6);
par {
run(ac, 8);
seq {
par { cam((880, 360), 2.4, smooth); zoom(1.7, 2.4, smooth); } // magnify the focal region
say(cap, "blue focuses NEARER, red FARTHER — chromatic aberration", 0.3);
wait(2.8);
say(cap, "now cement a flint element behind it — the achromat", 0.3);
fade(gap, 0.7); // the gap closes
wait(2.0);
say(cap, "…and the colours snap back to ONE sharp focus", 0.3);
}
}
par { cam((cx, 360), 1.3, smooth); zoom(1.0, 1.3, smooth); }
wait(1.2);
lens-system
A REAL multi-element lens, ray-traced through its actual spherical surfaces (not the ideal
thin lens): the fast singlet reveals SPHERICAL ABERRATION — draw sketches the rays, then
run sweeps a sensor plane and the live spot read-out dips but never reaches a point,
because the outer rays focus short (lenssystem, presets singlet/doublet/triplet).
// ============================================================================
// lens-system.manic — a REAL lens, ray-traced (best of physics + manic)
// ----------------------------------------------------------------------------
// `lenssystem` traces a parallel beam through the ACTUAL spherical surfaces of
// a lens (not the ideal thin lens of `lens`). Here the fast singlet reveals
// SPHERICAL ABERRATION: sketch the rays on with `draw`, then `run` sweeps a
// sensor plane along the axis while the live spot read-out dips to its minimum
// — which never reaches a point, because the outer rays focus short. Then swap
// the caption's promise: that's the flaw a doublet/triplet is built to fix.
// ============================================================================
title("Spherical Aberration — a Real Lens Traced");
canvas("16:9");
lenssystem(ls, (620, 380), "singlet");
text(cap, (cx, h - 42), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "a parallel beam into a real, fast single lens…", 0.5);
draw(ls.rays, 2.2); // the rays sketch through the glass
wait(0.3);
par {
run(ls, 6); // the sensor plane sweeps the axis
seq {
say(cap, "a sensor hunts for focus — watch the spot size", 0.3);
wait(2.4);
say(cap, "the outer rays focus SHORT — it never reaches a point", 0.3);
wait(2.0);
say(cap, "that blur is spherical aberration (a doublet/triplet fixes it)", 0.3);
}
}
wait(1.2);
ray-fan
Reading an aberration: rayfan plots each ray’s error at focus against where it entered the
lens. A flat line is a perfect lens — the singlet’s cubic S-CURVE is textbook spherical
aberration (the edges bend too much), which a doublet flattens (rayfan).
// ============================================================================
// ray-fan.manic — the signature of an aberration (real optics analysis)
// ----------------------------------------------------------------------------
// A ray-fan plot: for each ray, how far off focus does it land (y) against
// where it entered the lens (x, edge → edge)? `rayfan` traces the real
// spherical surfaces. A perfect lens is a flat line at zero; the singlet's
// cubic S-CURVE is textbook spherical aberration — the outer rays bend too
// much. (The doublet/triplet flatten it, drawn to the same scale.)
// ============================================================================
title("Ray-Fan — Reading an Aberration");
canvas("16:9");
rayfan(rf, (640, 340), "singlet");
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "each ray: how far off focus (y) vs where it entered (x)…", 0.5);
draw(rf.curve, 2.2);
wait(0.5);
say(cap, "a flat line would be a perfect lens — this ISN'T flat", 0.3);
wait(2.0);
say(cap, "that S-curve is spherical aberration: the edges bend too much", 0.3);
wait(1.4);
spot-diagram
Lens quality as a picture: spotdiagram plots where a ray bundle actually lands at focus.
A fast single lens smears into a blur disc (the circle of least confusion), while a cemented
doublet collapses to a point — both to the same scale, RMS 4 px → under 1 px (spotdiagram).
// ============================================================================
// spot-diagram.manic — a blur vs a point (lens quality, real optics)
// ----------------------------------------------------------------------------
// Where does a bundle of rays actually LAND at focus? `spotdiagram` traces the
// real spherical surfaces and plots the hits. A fast single lens smears into a
// disc — the circle of least confusion from SPHERICAL ABERRATION — while a
// cemented doublet collapses to a point. The green dot is the ideal (point)
// focus; the RMS read-out is the blur radius. Both drawn to the SAME scale.
// ============================================================================
title("Spot Diagram — Blur vs Point");
canvas("16:9");
spotdiagram(bad, (370, 380), "singlet");
spotdiagram(good, (910, 380), "doublet");
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "where do the rays actually land at focus?", 0.5);
wait(0.4);
say(cap, "a fast SINGLE lens → a blur disc (spherical aberration)", 0.3);
draw(bad.dots, 1.8);
wait(0.6);
say(cap, "a cemented DOUBLET → very nearly a point", 0.3);
draw(good.dots, 1.4);
wait(0.6);
say(cap, "same rays, same scale — RMS 4 px collapses to under 1 px", 0.3);
wait(1.2);
lens-prescription
Type your OWN lens: lenssystem takes a design by name ("plano-convex", "doublet", …)
OR a custom PRESCRIPTION — the designer’s surface table "radius thickness glass [conic] [aperture] | …" — traced through the true surfaces with real Sellmeier glass (lenssystem).
// ============================================================================
// lens-prescription.manic — type your OWN lens (real prescription)
// ----------------------------------------------------------------------------
// `lenssystem` takes a lens two ways: pick a real design by NAME
// ("plano-convex" / "meniscus" / "doublet" / "triplet" / …), or write a CUSTOM
// prescription — the lens designer's surface table — as "radius thickness glass"
// per surface, separated by "|". Here: a cemented crown+flint achromat, traced
// through its actual spherical surfaces with real glass (Sellmeier).
// ============================================================================
title("A Lens, By Prescription");
canvas("16:9");
// surface table: radius thickness glass (| between surfaces; "flat"/"air" allowed)
lenssystem(l, (600, 380), "160 26 bk7 | -140 8 f2 | -420 0 air");
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "a lens written as a prescription: radius · thickness · glass", 0.5);
draw(l.rays, 2.2); // trace the real surfaces
wait(0.4);
par {
run(l, 6); // sweep the sensor to best focus
seq {
say(cap, "crown + flint, cemented — the same table a designer would type", 0.3);
wait(2.6);
say(cap, "traced through the true spherical surfaces (real Sellmeier glass)", 0.3);
wait(2.0);
}
}
wait(1.0);
aspheric-lens
How an ASPHERE kills spherical aberration: a spherical surface can’t focus a wide beam to a
point (a blur, RMS 1.5 px), but reshaping it to the right conic — one constant in the
prescription — collapses every ray to a point (RMS 0.1 px). Two real ray-traced lenses,
spherical vs aspheric, side by side (lenssystem conic + spotdiagram).
// ============================================================================
// aspheric-lens.manic — how an asphere kills spherical aberration
// ----------------------------------------------------------------------------
// A prescription surface has a CONIC constant. A plain spherical lens can't
// focus a wide beam to a point — the edges bend too much (spherical aberration,
// a blur). Reshape that surface into the right ellipse/hyperbola (a conic ≠ 0)
// and every ray meets at ONE point. Two real ray-traced lenses, side by side:
// same shape, one spherical, one aspheric.
// ============================================================================
title("Aspheric Correction");
canvas("16:9");
spotdiagram(sph, (360, 380), "plano-convex"); // spherical surface → a blur
spotdiagram(asp, (912, 380), "aspheric"); // conic surface (K≈−0.55) → a point
text(cap, (cx, h - 46), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "same lens shape — where do the rays land at focus?", 0.5);
wait(0.4);
say(cap, "a SPHERICAL surface can't focus the edges — a blur (RMS 1.5 px)", 0.3);
draw(sph.dots, 1.6);
wait(0.6);
say(cap, "reshape it to an ASPHERE (conic surface) — a point (RMS 0.1 px)", 0.3);
draw(asp.dots, 1.4);
wait(0.6);
say(cap, "one conic constant in the prescription — spherical aberration, gone", 0.3);
wait(1.2);
off-axis
The hard test — light 8° OFF the axis. fieldspot traces a full 2-D pupil in 3-D: a single
lens flares into a COMA comet, while a doublet holds the spot near the Airy disk (the
diffraction limit). Real field aberration only a 3-D trace shows (fieldspot).
// ============================================================================
// off-axis.manic — coma & astigmatism, off the axis (3-D ray-traced)
// ----------------------------------------------------------------------------
// On the axis a lens can be sharp — but light coming in at an ANGLE is a harder
// test. `fieldspot` traces a full 2-D pupil in 3-D at a field angle and plots
// where the rays land. A single lens flares into a COMA comet; a doublet holds
// the spot near the Airy disk (the diffraction limit). Same 8° field, side by
// side — real off-axis aberration only a 3-D trace reveals.
// ============================================================================
title("Off-Axis — Coma vs a Corrected Lens");
canvas("16:9");
fieldspot(bad, (350, 380), "singlet", 8); // one element → a coma comet
fieldspot(good, (930, 380), "doublet", 8); // doublet → tight, near diffraction
text(cap, (cx, h - 44), ""); color(cap, fg); size(cap, 24); bold(cap); display(cap); sticky(cap);
// ================= THE SCENE =================
say(cap, "now aim the light 8° OFF the axis — the hard test", 0.5);
wait(0.4);
say(cap, "a single lens smears into a COMA comet (RMS ~7 px)", 0.3);
draw(bad.dots, 1.6);
wait(0.6);
say(cap, "the doublet holds it near the Airy disk — the diffraction limit", 0.3);
draw(good.dots, 1.4);
wait(0.6);
say(cap, "the circle is Airy: shrink the blur to it and you're diffraction-limited", 0.3);
wait(1.2);