Soap bubbles drift through a soft dusk garden, their thin-film colors swirling as the soap drains; two meet as a double bubble with a 120° shared wall and merge into one, and an old bubble turns black at its crown before popping in slow motion.
Written and rendered by AI CSS Animations Oct 18, 2026
A soap bubble is a film of water held between two layers of soap molecules, often less than a micrometer thick. Light reflected from the film's front and back surfaces interferes, so each thickness shows its own color, and the colors drift as gravity drains the film thinner at the top; just before a bubble bursts, its thinnest patches turn black. When bubbles touch, their walls meet at 120 degrees, one of the rules Joseph Plateau described in the 19th century. Photographers, scientists and motion designers use slow-motion bubbles to show light, color and fragility.
Make a 10-second 3D film of soap bubbles: iridescent bubbles drift through soft evening air, two merge into one and one pops, their thin-film colors swirling as the soap drains. <inputs> If I don't give you something, use the default and don't ask. - Studio: Slowlight (fictional). - Title: "Thin film" - Line: "Every color on a bubble is its thickness, measured in light." - Length: 10 seconds. </inputs> <direction> - Look: macro footage of real bubbles against a soft, out-of-focus evening garden: dark enough for the films to glow, with big blurred lights behind. Every bubble is crisp. - Palette: background gradient #1C2140 at the top to #3B2F55 at the bottom, with seven large soft light discs (180–420px, feathered, 8–18% opacity) in warm #FFB46B and cool #7FA8FF drifting 4px/s; film colors come only from the physics below; title #F3EEF8, secondary #B9B2CF. - Bubbles: 11 spheres of radius 0.35–1.4 units, each a ShaderMaterial (transparent, premultiplied, depthWrite false), drawn back to front. Per fragment: - thickness d in nm: thicker at the bottom as gravity drains the film (about 950 nm at the base to 220 nm at the crown), plus a marbled swirl of ±220 nm from seeded 3D noise on the sphere that flows around the vertical axis at 0.15 rad/s and slowly upward; - thin-film interference per channel at λ = 650, 532 and 450 nm with film index n = 1.33 and the refracted angle θₜ from the view angle: R(λ) = sin²(2π·n·d·cosθₜ ÷ λ) × F, where F is water's Fresnel reflectance (Schlick, F₀ = 0.02) rising strongly toward the rim, so centers stay nearly clear and rims are bright and colored; - a reflected environment: one big soft window highlight (two rounded rectangles in #FFF4E6, upper left on each bubble) and a dim horizon of the background colors, both tinted by the same interference; - black film: where d drops below 30 nm the film reflects almost nothing and reads as a clear black patch, which grows at the crown of an old bubble just before it pops. - Drift: each bubble follows a closed-form path: a breeze of 0.25 units/s to the right plus two seeded sines per axis (periods 4–9 s, amplitudes 0.2–0.6), a slight sink of 0.03 units/s, and a gentle wobble (±1.5% along a seeded axis at 1.2 Hz). Bubbles never touch except the pair that merges, and they stay out of the title area (x < 780, y < 340) and off the legend. - Merge, by the real rules: two bubbles (radii 0.9 and 0.7) drift into contact at 3.2 s and form a double bubble. Each sphere is clipped where it lies inside the other (discard in the shader using the other's center and radius), and a shared wall closes the joint: a spherical cap of radius r₁r₂ ÷ (r₁ − r₂) = 3.15, bulging into the larger bubble and meeting both at 120° (Plateau's law), drawn with the same film shader. At 5.0 s the shared wall ruptures, retracting from one edge over 0.2 s, and the two become one bubble of radius (r₁³ + r₂³)^(1/3) ≈ 1.02, which wobbles from a peanut toward a sphere (a damped n = 2 shape oscillation at 2.2 Hz, decaying over 0.6 s). - Pop: from 6.0 s the oldest bubble's crown thins to black film; at 7.0 s a hole opens there and its rim races across the sphere over 0.18 s of screen time (a slow-motion pop), the remaining film shrinking behind a slightly thicker bright rim that sheds 60–90 droplets (2–5px, catching the light) outward along its path; they fall in short arcs and fade over 0.6 s. No flash: the pop never adds more than 5% to frame brightness. - Motes: 120 faint dust motes drift in the light (1–2px, 10–25%) for depth. - Camera: fov 35, a slow dolly forward of 1.2 units and a 3° drift right across the piece, centered on the cluster. No depth-of-field pass: the background is already soft and every bubble stays sharp. The films need no scene lights; the window highlight and bright rims keep every bubble readable from the first frame. - Type (HTML over the canvas): title Spectral 300 italic, 96px, #F3EEF8, top-left at (120, 120); line Jost 300, 26px, secondary, beneath, at most 620px wide; a thin legend at bottom-left (x 120–620, baseline y 960): a 10px bar of the interference colors for 0–1,000 nm computed from the same formula at normal incidence, ticks at 0, 500 and 1,000 nm in Jost 400, 20px, and a marker labeled "black film" at the thin end. Text rises 10px as it fades in over 0.8 s on cubic-bezier(0.22, 1, 0.36, 1). - Premium: real thin-film color that follows thickness and swirls as the soap drains, clear centers and bright rims, a merge that obeys Plateau's laws and a slow-motion pop that sheds droplets. Cheap: rainbow-gradient spheres, one iridescent texture on everything, a pop that's a white flash. Banned: rainbow textures or hue cycling not derived from thickness, opaque bubbles, sparkles, lens flares, bloom, a bubble wand or hands, a white flash on the pop, anything flashing more than 3 times per second. </direction> <structure> - 0.0–1.2s: the bubbles drift in from the left on the breeze, already lit, their colors swirling. - 1.2–3.2s: they float across the frame; the title and line rise in at 1.4 s. - 3.2–5.0s: two bubbles meet and form a double bubble; colors stream across the shared wall. - 5.0–6.0s: the wall ruptures and one larger bubble wobbles into a sphere; at 6.0 s the cluster is calm, the merged bubble at its center. - 6.0–7.0s: a black spot grows at the crown of the oldest bubble. - 7.0–7.6s: it pops in slow motion, the rim racing round and droplets flying. - 7.6–8.8s: the breeze eases and the bubbles slow; the legend draws in from the left at 7.8 s. - 8.8–10.0s: hold: the bubbles barely bob (±0.03 units) and only their colors keep flowing. For other lengths, stretch the drifting beats; keep the merge, the pop 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: three.js as an ES module pinned to three@0.160.0 through an import map on jsDelivr (`three` and `three/addons/`). WebGLRenderer with antialias, preserveDrawingBuffer: true and setPixelRatio(window.devicePixelRatio); render once at the end of every seek. Text is HTML over the canvas. - Bubble paths, drainage, the merge geometry and the pop are closed-form functions of t; droplets are seeded once at load with ballistic paths. </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 centers stay clear and rims colored, the color bands follow thickness (thicker toward the bottom, swirling) rather than a hue gradient, the double bubble's wall meets both bubbles at 120°, the merged bubble's radius is about 1.02, the pop adds no flash (log mean luminance from 6.8 to 7.8 s), no bubble covers the title or legend, and the title reads clearly over the background. 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 slow golden-hour glide down a faceted valley: flat-shaded ridges, a low sun, fog melting the far range into the horiz…
A device built in three.js rises out of total darkness while a softbox reflection glides along its titanium edge, then…
A toy-like isometric city block that builds itself on a floating slab: bevelled buildings rise floor by floor, cars loo…
Chunky two-tone 3D letters swing into place one by one on a seamless studio sweep, lit like a product shot with soft sh…
More like this on the motion design inspiration board: 3D motion design inspirationParticle animation inspiration