A deep zoom into the Mandelbrot set in lacquer teal, bone, vermilion and gold leaf: from the whole set through seahorse-valley spirals to a perfect miniature copy 45 billion times smaller, landing in the same framing as the first frame while the readouts lock in digit by digit.
Written and rendered by AI CSS Animations Oct 19, 2026
The Mandelbrot set is the set of complex numbers c for which repeating z → z² + c from zero never escapes to infinity, and it carries the name of Benoit Mandelbrot, who studied it at IBM around 1980. Adrien Douady and John H. Hubbard proved that it is connected, and its boundary holds endless spirals along with miniature copies of the whole set. Deep zooms go far past ordinary floating-point precision with perturbation theory, following each pixel's small difference from one precisely computed reference orbit. Zoom films are a staple of math channels, planetarium shows and generative art.
Make a 10-second deep zoom into the Mandelbrot set: from the whole set, through seahorse-valley spirals, down to a miniature copy of the set 45 billion times smaller, landing in exactly the framing of the first frame.
<inputs>
If I don't give you something, use the default and don't ask.
- Studio: Quarry Hill Mathematics (fictional).
- Title: "The same shape, again"
- Opening line: "Quarry Hill Mathematics · z → z² + c"
- Closing line: "Period 104 · 45 billion times smaller"
- Zoom target: the period-104 miniature whose nucleus is c* = −0.74359934842288111 + 0.13192894092284915i.
- Length: 10 seconds.
</inputs>
<direction>
- Palette, lacquer and porcelain: the exterior's color bands cycle through six stops, ink teal #0E2A30, jade #2F7D6D, celadon #A8CDB5, bone #F2E9D4, vermilion #D6452B and oxblood #5C1A1B, then back to ink teal. The set itself is gold leaf #D8B25C with a soft screen-space sheen from #E6C676 at upper left to #C79E4A at lower right. Filaments and the set's outline are lacquer black #0B0A0C, so the warm bands always meet the gold across a thin ink line. Caption band #0C1719 under a 1px gold rule #D8B25C; type bone #F2E9D4, secondary #9FB8B0.
- Layout: the fractal fills x 0–1920, y 0–860; the caption band fills y 860–1080.
- The view: a stage point (px, py) in the fractal area is X = (px − 960) + i·(430 − py) stage pixels from the view center, and c = C(t) + σ(t)·X, with σ(t) = (w(t) ÷ 1920)·e^{iθ(t)} and w the width of the view in the complex plane.
- Opening view: w₀ = 5.6, C₀ = −0.65, θ = 0: the whole set at about 44% of the frame width, antenna pointing left, every bulb in frame.
- Final view, so the miniature appears exactly as the whole set did: compute its size estimate at load: z = 0, l = 1, b = 1; for i = 1…103: z = z² + c*, l = 2·z·l, b = b + 1/l; then s = 1/(b·l²). It comes out at s ≈ −2.0962 × 10⁻¹¹ − 6.7015 × 10⁻¹² i (|s| ≈ 2.2007 × 10⁻¹¹, arg s ≈ −162.27°). Final width w₁ = 5.6·|s|, final center C₁ = c* + s·(−0.65), final angle θ₁ = arg s. Magnification at the end is w₀ ÷ w₁ = 1/|s| ≈ 4.54 × 10¹⁰.
- The path: progress u(t) from 0 to 1 follows a trapezoid speed profile in log scale: a half-cosine ramp up over 0.6–1.6 s, constant speed to 7.0 s (about 1.57 decades per second), a half-cosine ramp down to 8.8 s, normalized so u(8.8) = 1. Then ln w = ln w₀ + u·(ln w₁ − ln w₀); θ = θ₁·u, so the view turns 162° clockwise across the zoom; C(t) = C₁ + (C₀ − C₁)·(w ÷ w₀)^1.6, so the target drifts to the center as the zoom deepens. At 6.0 s, u ≈ 0.72 and w ≈ 1.17 × 10⁻⁷.
- Perturbation, because single-precision floats run out after about five decades: compute the nucleus's orbit Z₀…Z₁₀₃ once in double precision in JS (Z₀ = 0, Zₙ₊₁ = Zₙ² + c*; the orbit is periodic, |Z₁₀₄| < 10⁻¹⁰), upload it as a float texture and index it modulo 104. Each seek, compute Δ = C(t) − c* in doubles on the CPU and pass it as a uniform; never form c itself in the shader. Per sample: δc = Δ + σ·X; δ = 0, m = 0, z = 0, d = 0; then loop n < N: d = 2·z·d + σ; δ = (2·Zₘ + δ)·δ + δc; m = (m + 1) mod 104; z = Zₘ + δ; stop if |z|² > 10⁶ (escaped at n + 1); otherwise, if |z|² < |δ|², rebase: δ = z, m = 0 (this removes perturbation glitches). d is the derivative with respect to one stage pixel. Iteration cap N(t) = 400 + 7,000·u.
- Color: smooth count ν = n + 1 − log₂(ln|z|); palette position x = ln ν, one full cycle of the six stops per unit of x, smoothstep between neighboring stops in sRGB. Relief by the normal-map method: v = normalize(z ÷ d), light from the upper right, L = (cos 45°, sin 45°) in screen axes with y up, h = 1.5, shade = max(0, (v·L + h) ÷ (1 + h)); color × (0.35 + 0.85·shade), so every band reads like embossed lacquer. Distance estimate in stage pixels DE = 2·|z|·ln|z| ÷ |d|; mix toward #0B0A0C by 1 − √min(1, DE), so filaments stay crisp one-pixel ink lines. Samples that never escape are gold leaf. Four samples per pixel on a rotated grid, averaged.
- Caption band, all type HTML: the title in Noto Serif Display 400 italic, 56px, bone, baseline at (120, 946); under it at baseline (120, 994) the opening line in Geist Mono 400, 20px, uppercase, tracked 0.08em, secondary, which crossfades to the closing line at 8.2 s. Readouts: labels from x 1000 in Geist Mono 400, 20px, secondary; values from x 1170 in Geist Mono 500, 24px, bone, tabular figures; rows at baselines 906, 940, 974 and 1008: "Magnification" (× 1 up to × 4.54 × 10¹⁰: plain with thousands separators below 10⁶, then a two-decimal mantissa and a superscript power), "Re" and "Im" of the view center showing only the decimals one pixel resolves (ceil(−log₁₀(w ÷ 1920)) + 1 places, up to 17), so digits lock in from the left as the zoom deepens, and "Iterations" (N). At x 1640–1800, y 885–1015, an inset of the whole set (rendered once at load, gold on #0C1719) with a 1px bone frame marking the current view while it is larger than 3px, then a bone crosshair at c*.
- Easing: text rises 12px as it fades in over 0.6 s on cubic-bezier(0.22, 1, 0.36, 1). The zoom never pauses or reverses.
- Premium: continuous color with no stepping, filaments as crisp ink lines, relief like embossed lacquer, precision that holds all the way down, and a landing that rhymes with the first frame. Cheap: rainbow banding, a zoom that dissolves into blocky pixels at float precision, a camera that wanders off the target.
Banned: rainbow or hue-cycling palettes, palette animation, visible iteration bands, colors beyond the palette, blur, bloom or glow, aliasing speckle, perturbation glitch blocks, a zoom that ends on pixel mush or misses the miniature, anything flashing more than 3 times per second.
</direction>
<structure>
- 0.0–0.6s: the whole set, still: the gold silhouette inside its lacquer bands, filaments in ink; the title, then the readouts, rise in 0.15 s apart.
- 0.6–1.6s: the zoom accelerates toward the seahorse valley, the cleft between the main cardioid and the large bulb on its left; the gold walls slide out of frame.
- 1.6–7.0s: steady zoom at about 1.57 decades per second, turning slowly clockwise: seahorse tails, double spirals and spirals of spirals unfold one inside the next; the center digits lock in; the 6.0 s frame is a dense field of spirals, sharp to the pixel.
- 7.0–8.8s: the zoom eases out; a gold speck at the center grows inside its dark moat and halo of filaments into the miniature set, landing on the opening framing; the closing line crossfades in at 8.2 s.
- 8.8–10.0s: hold, completely still.
For other lengths, stretch the constant-speed stretch and keep the same target; keep 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 WebGL2 canvas (preserveDrawingBuffer: true, highp floats, drawn once per seek), its backing store sized from CSS size × devicePixelRatio, whose fragment shader runs the perturbed iteration above for every sample. The caption band is HTML and the inset a 2D canvas drawn once at load.
- The reference orbit, the size estimate and the inset are computed once at load; u, w, θ and Δ are closed-form functions of t, computed in doubles on the CPU each seek.
</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 first frame shows the whole set with its antenna to the left, no frame shows flat rectangular glitch blocks, noisy speckle or stepped bands, filaments stay one crisp pixel wide, the 6 s readout shows w ≈ 1.2 × 10⁻⁷, and the final frame shows the miniature at the same size, angle and position as the opening set with a crisp outline (raise the iteration cap if its edge looks swollen). 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 jazz-tempo film title sequence in flat vermilion, mustard, bone and black: hand-cut paper shards jab in on the beat,…
A Swiss-style event poster that builds itself on a visible 12-column grid: grotesque type rises out of baseline masks,…
A primary-colored circle, square and triangle roll, drop and turn into place on a black bar, assembling a constructed B…
A Constructivist poster in motion: red bars, a wedge piercing a black circle, halftone fields and condensed uppercase t…
More like this on the motion design inspiration board: Motion graphics inspiration