AI CSS Animations

Rain on Window

Rain on a night window: drops cling, then run in stop-start lurches, swallowing smaller drops and wiping trails through the mist. Each holds a tiny upside-down copy of the blurred street lights, and every one turns green when the traffic light changes.

Written and rendered by AI CSS Animations Oct 18, 2026

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

About this style

Rain on glass has long been a film shorthand for night, solitude and the city, and it works because each drop on the pane acts as a small lens, holding a focused, upside-down image of the lights behind it while the street itself falls out of focus into bokeh. Drops cling until enough water gathers, then run in stop-start paths, absorbing smaller drops and leaving a line of beads behind. Cinematographers get the look by focusing on the glass with a wide aperture, and it is now a fixture of ambient videos, music visuals and night-time city films.

The prompt

Make a 10-second shot of rain on a window at night: drops cling, run and merge down the glass, each one a tiny lens holding an upside-down copy of the blurred city lights behind it.

<inputs>
If I don't give you something, use the default and don't ask.
- Title: "After the Last Tram"
- Line: "Corvane Street · 23:14 · light rain" (fictional)
- Story in the lights: the last tram pulls away, a car comes toward the window and passes, and the traffic light turns from red to green at 5.2 s.
- Length: 10 seconds.
</inputs>

<direction>
- The camera is focused on the glass, so the drops are sharp and the street is soft bokeh. Each drop works as a lens: it holds a small, sharper, upside-down copy of the lights behind it. That inversion is the style; without it this is just dots on a blur.
- Palette: night #070A12 at the top to #121A2A at the wet street; sodium streetlamps #FFB45C; tram windows #FFD9A0; tail lights #FF4A3D; a teal shop sign #3FD1C4; a far magenta sign #E85BD9; headlights #F4F1E8; traffic light red #FF3B30, then green #3DDC84; mist on the glass #9AA7B8; title #EEF1F5, line #9AA6B6.
- The street, as lights only (no drawn buildings): a vanishing point at (1180, 470); two rows of eight sodium lamps converging on it, their bokeh discs 70px in radius near and 16px far; a few dim rows of lit windows above; the teal sign at (420, 520); the magenta sign far down the street; the traffic light at (1460, 380), disc radius 34px; every light doubled below the street line as a soft vertical streak at 40% (wet asphalt). Bokeh discs are lens-real: a flat disc with a slightly brighter rim, additive where they overlap, and clipped into cat's-eye shapes pointing toward the center near the frame edges. Keep the title zone (x 100–900, y 80–280) to dark sky and at most two dim window discs.
- Moving lights, on smooth closed-form paths: from 0 to 4.0 s the tram recedes toward the vanishing point (a row of six warm window discs and two red tail lights, shrinking); from 3.0 to 6.8 s a pair of headlights grows out of the vanishing point and passes out of frame right; the traffic light crossfades from red to green over 0.4 s at 5.2 s.
- Glass, three layers: (1) mist: a static field of tiny droplets (1–3px, seeded) that scatter light, lowering the background's contrast by 25% and adding a faint glow around bright lights; (2) resting drops, 2–14px, about 1,400 at the start, with new ones landing at 40 a second anywhere on the glass (each appears at full size in 0.05 s, no splash); (3) runners, 18–40px, that slide.
- Drop shape: render every drop into a full-resolution height canvas as a soft dome sprite composited with 'lighten', so touching drops fuse into one wet shape. A runner is a larger dome at the bottom with a smaller one trailing above, stretched up to 1.6× tall with speed.
- Optics, in one WebGL fragment shader, with normals from the height field. Inside a drop, sample the focused light layer (the same lights drawn as small sharp points with a short glow) at uv − n × 0.18 in screen units, which flips the scene inside each drop as a real lens does; add a dark rim (35%) where the dome meets the glass, a small white glint toward the upper left, and a bright crescent at the lower edge where the drop focuses light. Outside drops, show the bokeh layer seen through the mist.
- Runner physics, precomputed once at load at a fixed 1/60 s step from t = −3 s, so the glass is already wet and busy: a drop starts running once its radius passes 14px; it moves in stick-slip at speeds between 0 and 520px/s set by a seeded stickiness field over the glass, so it pauses, lurches and wanders up to 1px sideways per step; it swallows any resting drop it touches (masses add, radius ∝ ∛mass); every 14–28px it leaves a 1.5–3px bead behind; it wipes a clear trail through the mist 0.8× its width, which mists over again across 6 s; it stops when its radius falls under 9px. Two runners that touch merge. About 18 runners over the piece.
- Camera: locked off on the glass, with a 2% push-in across the piece on easeInOutSine. Nothing shakes.
- Type (HTML over the canvas): title in Instrument Serif 400 italic, 104px, #EEF1F5, baseline at (120, 200); line in Azeret Mono 400, 20px, uppercase, tracked 0.16em, #9AA6B6, baseline at (124, 252). Each rises 10px as it fades in over 1.0 s on cubic-bezier(0.22, 1, 0.36, 1).
- Premium: drops that hold tiny flipped lights, runners that stutter and swallow, trails wiped through mist, bokeh you could have shot with a fast lens. Cheap: white streaks and grey circles over a blurred photo, drops sliding at constant speed.
Banned: lightning, rain streaks in the air, wipers, a reflected person, drops sliding upward or at constant speed, bokeh that flickers, hexagonal bokeh, lens flares, anything flashing more than 3 times per second.
</direction>

<structure>
- 0.0–1.2s: misted glass over the city lights; resting drops already hold tiny copies of the lamps; new drops land; the tram recedes.
- 1.2–3.0s: the first large drop lets go at upper left and runs down in stops and lurches, swallowing smaller drops and leaving a clear trail; a second starts at the right.
- 3.0–4.8s: headlights rise out of the vanishing point and swell; two runners meet and merge at about 4.2 s; the title fades up at 3.6 s and the line at 4.0 s.
- 4.8–6.4s: at 5.2 s the light turns green, and the tiny copy of it in every drop turns green too; the headlights cross toward the right edge as more runners cut fresh trails.
- 6.4–8.6s: the headlights leave the frame; the rain eases to 15 drops a second and the last runner slows to a stop at 8.4 s.
- 8.6–10.0s: hold: nothing runs; the green light, the lamps and the trails stay put while the mist slowly closes over the trails.
For other lengths, stretch 1.2–6.4s with more runners; 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: two 2D canvases redrawn each seek (the street lights twice, as bokeh and as sharp points, at half resolution; and the drop height field at full resolution), uploaded as textures to one WebGL canvas (preserveDrawingBuffer: true, drawn once per seek) that renders the mist, refraction and highlights; backing stores sized from CSS size × devicePixelRatio. Text is HTML over the canvas.
- The runner simulation and every landing time are precomputed into Float32Arrays at load; seek(t) only reads them, and trails are drawn from stored points with their ages.
</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 that lamps above the street appear in the lower half of each drop (the inversion), runners stop and start instead of gliding, trails are clear and then mist over, the green shows in the drops from 5.2 s, and the title zone stays dark. 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: Particle animation inspiration