Under a sodium lamp, two dippers in a ripple tank cross their ripples into bright crests and still slate lines exactly where the path difference says, then a plane wave through a double slit rebuilds the same fringes as a meter fills in and a strobe freezes the water.
Written and rendered by AI CSS Animations Oct 19, 2026
Thomas Young showed that light behaves as a wave in experiments with two narrow sources in the early 1800s, explaining the bright and dark bands by comparison with water waves crossing on a pond. A ripple tank makes that comparison literal: a shallow glass-bottomed tray of water lit from above, where each crest focuses light into a bright line on the screen below and a strobe can freeze the motion. Where waves from two in-step sources arrive with paths differing by whole wavelengths they add into bright fringes, and where they differ by half a wavelength they cancel into still water. Physics classrooms and science explainers have used it for generations.
Make a 10-second physics explainer in a ripple tank: two dippers make an interference pattern of bright and dark fringes, then a plane wave through a double slit makes the same pattern again, measured on a fringe meter.
<inputs>
If I don't give you something, use the default and don't ask.
- Series: "Bench Physics · No. 4", from Harlowe Instruments (fictional).
- Title: "Two sources, one pattern"
- Captions, in order: "Two dippers, in step." · "Where a crest meets a trough, the water stays still." · "A double slit turns one wave into two sources." · "Bright where the paths differ by whole wavelengths: d sin θ = mλ."
- Waves: wavelength λ = 120px, frequency f = 2.5 Hz, so speed c = 300px/s; source spacing d = 3λ = 360px.
- Length: 10 seconds.
</inputs>
<direction>
- Palette, a sodium lamp over the tank: bench #1B2026; tank frame brushed aluminium #A7ADB2 with a #D9DDE0 top edge; the water as a shadowgraph ramp, troughs #121A24, still water #4D5D66, crests #FFD27A, the brightest crest cores #FFF1C9; dippers, barrier and bars graphite #2E3338 with #6D747B highlights; nodal-line overlays dashed pale cyan #9FE3F0, the complement of the sodium light; path lines white; type #EDEFF1, secondary #9AA6AE, accent #FFD27A.
- Layout: the water fills x 120–1380, y 210–950 inside a 14px frame; the fringe meter panel fills x 1440–1800, y 210–950, its y axis aligned with the tank's; title row above the tank, captions below it.
- Geometry (stage pixels): dippers S₁ = (420, 400) and S₂ = (420, 760); later a barrier occupies x 414–426 with slits 40px wide centered on the same two points; a straight dipper sits along x = 140; the meter samples the line x = 1340.
- The field, analytic and evaluated per pixel, with k = 2π/λ, ω = 2πf and g(r) = √(λ ÷ max(r, λ/2)) for circular spreading:
- dipper waves: hᵢ = g(rᵢ)·sin(k·rᵢ − ω(t − 0.4))·E_d(t − rᵢ/c), with E_d(τ) = smoothstep(0.4, 0.8, τ)·(1 − smoothstep(3.6, 3.9, τ)). Every ripple carries the envelope of the moment it was made, so the pattern grows outward from the dippers and later drains outward behind a back front once they stop;
- plane wave, for x < 420: h_p = 0.8·sin(k(x − 140) − ω(t − 3.6))·E_p(t − (x − 140)/c), plus its reflection from the barrier h_r = 0.8·sin(k(700 − x) − ω(t − 3.6))·E_p(t − (700 − x)/c), so a standing wave builds in front of the barrier; E_p(τ) = smoothstep(3.6, 3.9, τ);
- slit waves, for x > 426: h_sᵢ = 0.85·g(rᵢ)·sin(k(280 + rᵢ) − ω(t − 3.6))·E_p(t − (280 + rᵢ)/c)·(1 + cos φᵢ)/2, rᵢ and φᵢ measured from slit i, φᵢ from the +x axis. Both slits receive the plane wave in step, so they act as two sources in step, exactly like the dippers.
- h is the sum of whatever terms apply at that pixel and time. A soft absorbing beach, the outer 30px along the top, bottom and right walls, fades the waves to zero so nothing reflects there.
- The shadowgraph look: s = clamp(h ÷ 1.2, −1, 1), I = 0.18 + 0.82·((s + 1)/2)^2.6, mapped through the ramp (I = 0.18 troughs, 0.32 still water, 1.0 crests, the top 8% the hot core): thin bright crest lines on broad darker troughs, still water at a calm slate. Tune the 1.2 within ±20% so the far fringes still read bright while only the crests near the sources reach the hot core. Add a faint fixed glass texture (2%) and a 6% vignette toward the walls.
- Hardware, drawn on top: a graphite bar along x = 420 holding two round dippers (16px discs with a highlight) that touch the water at S₁ and S₂; at 3.6–4.0 s they lift away (scale 1.08, shadow widening, fading out) while the barrier slides down from the tank's top edge with its two slits; the straight dipper, a 24px graphite bar along x 128–152, fades in at 3.3 s.
- Overlays: from 2.6 to 3.6 s, the dark fringes right of the dippers as dashed pale-cyan curves, the exact hyperbolas r₁ − r₂ = ±½λ, ±1½λ and ±2½λ (6px dashes, 2px), with the label "still water: paths differ by ½λ"; and a path demo: from P = (1018.7, 800), where r₁ = 6λ and r₂ = 5λ exactly, white 2px lines to S₁ and S₂ tagged "6λ" and "5λ": a bright point.
- Fringe meter (7.4–8.6 s): the time-averaged intensity along x = 1340 for two sources at the slits, I(y) = |g(r₁)e^{ik·r₁} + g(r₂)e^{ik·r₂}|², drawn as a filled sodium area growing right from a 1px baseline at x 1460, at most 300px wide; each y fills only once the slit waves have reached it, so the plot fills from the center outward. Bright fringes fall at y ≈ 580 (m = 0), 252 and 908 (m = ±1); dark ones at y ≈ 422 and 738. Tags "m = 0" and "m = ±1" beside the peaks and "still" beside the dips, in B612 Mono 400, 20px. Under the panel at baseline 990: "Wave energy along x = 1340" in Atkinson Hyperlegible 400, 20px, secondary.
- Strobe: at 8.6 s the field's clock stops (every field term uses min(t, 8.6)), like a strobe at 2.5 Hz freezing the waves, and a chip "Strobe 2.5 Hz · waves frozen" appears at the top right, right-aligned at (1800, 160).
- Type: the kicker in B612 Mono 400, 20px, tracked 0.12em, uppercase, accent, at (120, 92); the title in Atkinson Hyperlegible 700, 52px, #EDEFF1, at (120, 160); the readout "λ = 120 px · f = 2.5 Hz · d = 3λ" in B612 Mono 400, 22px, secondary, right-aligned at (1380, 160); captions in Atkinson Hyperlegible 400, 30px, #EDEFF1, at baseline (120, 1010), one at a time, crossfading with an 8px rise over 0.4 s; overlay labels in B612 Mono 400, 20px.
- Easing: hardware on cubic-bezier(0.45, 0, 0.2, 1); text rises 12px as it fades in over 0.5 s on cubic-bezier(0.22, 1, 0.36, 1).
- Premium: waves that obey one wavelength and one speed everywhere, nodal lines exactly where the path difference says, a slit pattern that rebuilds the dipper pattern, a meter that matches the water. Cheap: concentric rings that ignore each other, a blue gradient water texture, fringes drawn by hand.
Banned: a wave frequency above 2.5 Hz, full-frame brightness changes, glow or bloom, blue water clip-art, overlays that disagree with the field, anything flashing more than 3 times per second.
</direction>
<structure>
- 0.0–0.4s: the still tank under the sodium light, dippers poised; the kicker, title and readout rise in.
- 0.4–2.6s: the dippers start in step; rings spread and cross; still lines fan out between bright bands as the pattern grows toward the walls; caption 1 at 0.8 s.
- 2.6–3.6s: caption 2; the dashed still-water hyperbolas and the 6λ/5λ path demo draw in.
- 3.6–4.6s: the overlays clear; the dippers lift and the barrier slides down; the old pattern drains outward behind its back front; the straight dipper sends plane waves to the right; caption 3 at 4.0 s.
- 4.6–7.4s: the plane wave reaches the slits at 4.53 s; half-rings spread from both slits and rebuild the same fringes while a standing wave fills the space in front of the barrier. The 6.0 s frame: plane waves left, the barrier, the new pattern about 440px out from the slits, the last of the old one draining at the far right.
- 7.4–8.6s: the fringe meter fills from its center outward; caption 4.
- 8.6–10.0s: the strobe freezes the waves; hold.
For other lengths, stretch the time before each overlay; keep the wave speed and at least 1 s of hold.
</structure>
<build>
- One self-contained index.html (inline CSS and JS), a 1920×1080 (16:9) stage scaled to fit the window, designed for 60fps. No image, video, texture or model files: every shape and texture is generated in code.
- window.seek(t), t in seconds, computes every visual from t alone: no CSS transitions or CSS animations, no setTimeout/setInterval, no state carried between frames, randomness only from a seeded PRNG. Anything cumulative is solved analytically from t or precomputed once at load at a fixed step. Past the end, seek clamps to the last frame.
- Without ?render in the URL, drive seek from requestAnimationFrame so it plays in a browser (loop after a 1s end hold); with ?render, don't start that loop.
- Google Fonts via <link> are fine; await document.fonts.load() for each face before defining window.seek.
- Engine: Canvas: one WebGL canvas (preserveDrawingBuffer: true, drawn once per seek), its backing store sized from CSS size × devicePixelRatio, whose fragment shader sums the analytic terms per pixel; hardware, overlays and the meter on a 2D canvas above; type is HTML.
- Every term is a closed-form function of position and t; the meter's I(y) is computed once at load.
</build>
<check>
Before showing me, step through one frame per second (t = 0…10): screenshot each and look at it. Fix overlaps, clipped or cramped text, and anything off-style or Banned. Check the dashed hyperbolas run exactly along the still lines in the water, P lies on a bright line, the meter's peaks line up with the bright bands where they cross x = 1340, the slit pattern matches the dipper pattern, the barrier never shows waves passing through it except at the slits, the 6.0 s frame and the final frozen frame are both complete compositions, and no frame's average brightness jumps. Confirm seek(10) and seek(11) match, so the final frame holds.
</check> Ours, and free to use under CC0: copy it, change it, ship it. No credit needed.
A cyanotype-blue engineering sheet that drafts itself: centerlines, compass-swept gears, hatching and dimension lines a…
A recipe app demonstrates Material-style motion: a card expands into a full screen, tabs switch on a shared axis, a FAB…
A tidy isometric line-art explainer: constant 2px strokes, white faces and one vermilion accent as a parcel travels fro…
A bright, rounded science explainer on a deep-space backdrop: springy geometric shapes, two-tone shading and soft long…
More like this on the motion design inspiration board: Explainer video inspiration