AI CSS Animations

Ripple Tank

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

Type
prompt
Made with
Claude Opus 5.5 in Claude Code
Built in
Canvas
Topics
Explainer
What Claude made from the prompt below in Claude Code: one HTML file rendered frame by frame to video. Silent preview.

About this style

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.

The prompt

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.

Similar prompts

More like this on the motion design inspiration board: Explainer video inspiration