AI CSS Animations

One Million Users Milestone

A counter climbs to 1,000,000 as thousands of user dots light up across a butter-yellow grid in five colors, then the colors drain away and the ink dots that remain spell out the number itself.

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
SaaS promo · Particles · Kinetic type
What Claude made from the prompt below in Claude Code: one HTML file rendered frame by frame to video. Silent preview.

About this style

User milestones such as the first thousand customers or the first million users are among the most shared posts a software company makes. The usual video pairs a counter racing to the number with a visual that makes the scale feel real, often by turning people into dots, so that a million becomes something you can see. The format borrows from unit charts in data visualization, where each mark stands for a fixed number of people, and from stadium card stunts, where a crowd forms a picture. It works when it stays a thank-you rather than a boast, lands the number exactly, and reads as a still image when it's shared.

The prompt

Make a 10-second milestone celebration in Canvas: a counter climbs from 0 to 1,000,000 while thousands of user dots light up across a dot grid in a seeded scatter, and as it lands the colors drain away so the dots that remain spell out the number itself, 1,000,000.

<inputs>
If I don't give you something, use the default and don't ask.
- Product: Larkspur (fictional), a habit-tracking app, launched in April 2023.
- Milestone line, with the counter in it: "1,000,000 people build their habits with Larkspur" · after it lands: "Thank you."
- Date ticker, top right: "Since Apr 2023" and the current month, from "Apr 2023" to "Oct 2026".
- Scale note: "Each dot is about 240 people" (4,165 dots × 240 ≈ 1,000,000).
- Length: 10 seconds.
</inputs>

<direction>
- Look: a warm, bright, printed-poster celebration. A butter-yellow field covered in a fine grid of dots, like a halftone or a stadium card stunt. As people join, dots light up in five friendly colors, scattered across the grid, until it is full; then the colors drain from everything except the dots that make up the number, which are ink black, so the grid reads as 1,000,000. One idea, done exactly.
- Palette: field #FFE27A; unlit dots #F2CB4E; lit user dots tangerine #FF8A3D, rose #EF6A8A, sky #5DAFE6, leaf #5DB874 and cocoa #8C5A3C; number dots ink #1E1A12; text ink #1E1A12; secondary #6B5A2A; drained dots #F5D25C.
- Type: Unbounded 800 for the counter (72px, tabular figures) and 600 for the brand name (30px); Outfit 500/600 for everything else: the milestone line 34px, the date ticker 22px with tabular figures, the scale note 18px. Oswald 700 is used only to build the number mask, never shown as text.
- The grid: 119 columns × 35 rows (4,165 dots) at a 14px pitch, from (128, 300) to (1780, 776); dot radius 4.5px.
- The number mask: at load, render "1,000,000" in Oswald 700 into an offscreen canvas, fitted to 1560px wide and centered on (954, 538), commas kept. A dot is a number dot if the glyphs cover at least half of the 14px cell around it. Expect about 1,000 number dots, each stroke three to four dots wide; the rest are field dots.
- The fill order: at load, give every dot a seeded threshold between 0 and 1: 0.8 × random + 0.2 × its distance from the grid's center (normalized to 0–1), so the scatter is mostly random with a gentle drift outward. A dot lights when count / 1,000,000 passes its threshold: field dots in one of the five user colors (seeded), number dots in ink. The light-up is a pop: radius 0 → 5.6 → 4.5px over 0.22 s on easeOutBack.
- The counter, inside the milestone line on a baseline at y 880: the count in Unbounded 800 72px ink with thousands separators, right-aligned in a 500px box starting at x 128 so the line never shifts, then "people build their habits with Larkspur" in Outfit 500 34px, 18px after the box. count(t) = floor(1,000,000 × easeInOutCubic(u)) with u running from 0 at 0.5 s to 1 at 5.3 s, so it accelerates, races and slows to land; the final 10,000 tick by visibly over the last half second.
- The date ticker, right-aligned to x 1792 on a baseline at y 112: "Since Apr 2023 ·" in secondary, then the current month in ink, mapped from the count on a log scale through 1,000 → Jun 2023, 10,000 → Dec 2023, 100,000 → Sep 2024, 500,000 → Nov 2025 and 1,000,000 → Oct 2026 (before 1,000 it reads "Apr 2023").
- The brand at (128, 112): an original mark (three rounded teardrop petals in tangerine, rose and sky fanned above a short ink stem, 36px tall) and "Larkspur" in Unbounded 600 30px ink.
- The drain: when the count lands, the field dots fade from their colors to #F5D25C over 0.5 s, staggered from the center outward across 0.3 s, while the number dots stay ink; then the number dots pop once in a diagonal wave from bottom left to top right (scale 1 → 1.35 → 1 over 0.3 s each, the wave crossing in 0.45 s).
- A period and "Thank you." follow the line, in Outfit 600 34px ink, rising 16px as they fade in. The scale note sits at (128, 940) in secondary.
- Premium: thousands of crisp dots on a precise grid, a scatter that fills evenly, a count that eases and lands exactly, a number that emerges from the dots instead of being stamped on top, a calm, warm palette. Cheap: confetti cannons, fireworks, a gold trophy, glowing numbers, a shake on landing, emoji, the number simply faded in over the grid.
- Banned: confetti, fireworks, trophies, emoji, glow, camera shake, purple or blue gradients, a dark background, real company names, logos and UI copied from existing products, anything flashing more than 3 times a second.
</direction>

<structure>
- 0.0–0.5s: the unlit grid ripples in from the center (each dot fades in over 0.15 s, delayed by its distance, 0.4 s spread); the brand, the ticker and the line fade in with the counter at "0".
- 0.5–5.3s: the count climbs and the dots light as it passes their thresholds; the ticker steps month by month; the field fills with color and the ink number shows through more and more from about 3.5 s.
- 5.3s: the count lands on 1,000,000 and the ticker on "Oct 2026".
- 5.3–5.8s: the drain; the ink number stands alone on the faded field.
- 5.45–5.9s: the number's pop wave; "Thank you." rises (5.5–5.85 s); the scale note fades in (5.6–5.9 s).
- 6.0s: the hero frame: the grid reading 1,000,000 in ink dots, the full line "1,000,000 people build their habits with Larkspur. Thank you.", the brand, "Oct 2026" and the scale note.
- 6.4–7.2s: one slow ripple from the center: each dot brightens 10% and returns as the ring passes it.
- 7.2–10.0s: hold.
For other lengths, stretch the climb (keep it at least 3.5 s) and the hold; keep the drain and the pop wave as they are 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: at load, solve each dot's light-up time from its threshold by inverting count(t) (bisection), so a dot's pop at any t is a pure function of t. 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 and weight (Oswald included, before building the mask) before defining window.seek: Canvas won't fetch fonts itself, so the awaited loads matter.
- Engine: Canvas 2D, repainting every frame from scratch, the backing store sized from CSS size × devicePixelRatio. Draw the dots with arc() batched by color (one path per color per frame).
</build>

<check>
Before showing me, step through one frame per second (t = 0…10) plus 4.5, 5.3 and 5.6: screenshot each and look at it. Fix overlaps, clipped or cramped text, and anything off-style or Banned. Check that the counter never shifts the line's text; the count is exactly 1,000,000 from 5.3 s on and the ticker reads "Oct 2026"; the fill looks evenly scattered, not in rows; at 4.5 s the number is partly visible through the colors; the number's commas and zeros read clearly in dots; the drain leaves only ink number dots on an even pale field. At 6.0 s the grid reads 1,000,000 and the whole line, "Thank you.", the brand, the ticker and the scale note are readable. The final frame is that composition at rest. 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: Kinetic typography inspirationSaaS video inspirationParticle animation inspiration