On deep cobalt, an apricot speech waveform scrolls past a playhead and words condense out of it as they are spoken, fixing their own mishearings; on release, fillers dissolve, a self-correction resolves, capitals and punctuation drop in and the words glide into a polished serif paragraph.
Written and rendered by AI CSS Animations Oct 19, 2026
Dictation software turns speech into text as you talk. Streaming recognizers show words within a fraction of a second and revise their guesses as more of the sentence arrives, and a cleanup pass then removes filler words, resolves self-corrections, adds punctuation and capitals and formats numbers and times. Dragon NaturallySpeaking, released in 1997, was among the first continuous dictation programs for personal computers, and dictation is now built into phones, laptops and writing tools. Product films for it usually show the same story: a waveform, words appearing as you speak, and a tidy result.
Make a 10-second voice-to-text product film: a speech waveform flows across the frame and turns into words as they're spoken, and when the speaker lets go, the rough draft tidies itself into a polished paragraph.
<inputs>
If I don't give you something, use the default and don't ask.
- App: Wrenlet (fictional), a dictation app.
- Spoken, word for word: "um so the demo is thursday no wait tuesday at uh three pm can you send noor the slides" (19 words).
- Word timings in seconds (start–end): um 0.60–0.82 · so 0.91–1.06 · the 1.13–1.22 · demo 1.24–1.54 · is 1.57–1.68 · thursday 1.71–2.12 · no 2.29–2.41 · wait 2.43–2.63 · tuesday 2.68–3.09 · at 3.14–3.23 · uh 3.29–3.48 · three 3.51–3.72 · pm 3.76–4.06 · can 4.25–4.36 · you 4.38–4.46 · send 4.48–4.66 · noor 4.68–4.89 · the 4.91–4.98 · slides 5.00–5.32
- Mishearings, fixed as more speech arrives: "thursday" first shows as "thirsty" · "noor" first shows as "nor".
- Polished: "The demo is Tuesday at 3 PM. Can you send Noor the slides?" (13 words).
- Summary chip: "19 words spoken · 13 kept · 1 correction"
- Mic states: "Hold to talk" · "Listening" · "Polishing" · "Done"
- Length: 10 seconds.
</inputs>
<direction>
- Look: speech made visible. A living waveform scrolls in from the right like a ribbon of sound, and words condense out of it as they're recognized, in a raw draft face; when the speaker lets go, the draft tidies itself step by step (fillers struck out, a self-correction resolved, numbers, capitals and punctuation dropped in) and the words glide into a calm, typeset paragraph, like a typesetter quietly cleaning up after you.
- Palette: background cobalt #1E3FAE deepening to #152E86 at the edges, with a soft lift #2A4FC4 behind the text and a static paper grain at 3%; waveform apricot #FFB27A with a lilac echo #C9B6FF at 55%, drawn with 'screen' so where they overlap they glow cream #FFE9D6; raw words cream #F7EFE3 at 85%; filler marks lilac #C9B6FF; edits arrive apricot #FFB27A and cool to cream over 0.6 s; the correction highlight apricot at 30%; the polished paragraph cream #FFF7EC; UI pill #0F2266 with a 1px #4D6BD8 border; secondary #B9C6F2. Warm apricot sound on cool cobalt: the voice is the only warm thing in the frame until the finished words take its color for a moment.
- Layout: app mark top-left at (120, 96): an original glyph of three rounded vertical bars of rising height and "Wrenlet" in Lexend 600, 26px, cream; at the top right, right-aligned at x 1800, the state and an elapsed time ("Listening · 0:04") in Lexend 500, 22px, secondary. The waveform band is centered on y 360 from x 120 to 1800 with a playhead at x 1500. The raw draft sits at x 240–1680 from baseline 560 in Fira Code 400, 40px, 60px leading. The polished paragraph sits at x 300–1620 on baselines 580 and 670 in Crimson Pro 500, 72px, ragged right, below the waveform band. The mic pill is centered at (960, 960), 360 × 72.
- The waveform, synthesized from the word timings: audio time runs right to left past the playhead at 600px per second, so the band shows the last 2.3 s of sound to the left of x 1500 and a flat line to its right. Loudness A(τ) is built per word: a 40 ms attack, one bump per syllable (demo, thursday, tuesday and pm have two), a 60 ms decay, and true silence in the gaps between words, deepest at the pauses after "thursday" and "pm". The line, sampled every 2px, is y = 360 + 110·A(τ)·(0.6·sin(2π·31τ + φ₁) + 0.3·sin(2π·57τ + φ₂) + 0.1·sin(2π·89τ + φ₃)), with slowly drifting seeded phases; the main stroke apricot 3px, the lilac echo 2px offset 6 ms and smoothed; under them a mirrored soft fill of the envelope in apricot at 18%. The playhead is a 2px cream line with a soft 10px dot riding the wave. When speech stops, the wave relaxes to a flat line over 0.4 s and slides to a halt.
- Words appearing: 0.12 s after each word ends, it condenses into the next place in the draft: 14 short apricot dashes fly from the playhead's dot to the word's box on curved paths (0.25 s) and the word fades and rises 10px into place as they arrive. Words wrap like real text. Fillers (um, so, uh) get a dotted lilac underline as they land.
- Mishearings: "thirsty" lands first; when "no" arrives (2.53 s) its letters re-form into "thursday" (the changed letters drop out and new ones drop in, 0.2 s, with a quick lilac underline flick). "nor" becomes "noor" when "the" arrives (5.10 s) the same way. Neighbors slide to make room; nothing jumps.
- The tidy-up, after release, each step legible on its own:
- fillers: a lilac strike line draws through um, so and uh (6.10, 6.18 and 6.26 s, 0.12 s each); each word lifts 30px and dissolves into fine cream dots that drift down into the flat waveform line;
- the correction: "thursday no wait" takes an apricot highlight (6.40 s); "thursday" slides down 40px and fades, "no wait" fades, and "tuesday" slides left into the gap (6.55–6.75 s);
- numbers: "three" folds up letter by letter and a "3" drops in; "pm" becomes "PM" (6.80 s);
- capitals: the first letters of "the", "tuesday", "can" and "noor" flip vertically into capitals, apricot, 40 ms apart (6.90 s);
- punctuation: "." after PM and "?" after slides drop 40px with a small 6% bounce (7.05 s);
- the reflow (7.10–7.90 s): every surviving word crossfades from Fira Code to Crimson Pro while gliding from its draft box to its place in the paragraph (a FLIP move on cubic-bezier(0.3, 0, 0.1, 1) over 0.7 s, 25 ms apart in reading order), the paragraph wrapping as "The demo is Tuesday at 3 PM." over "Can you send Noor the slides?"
- Mic pill: an original line-drawn mic glyph, the state in Lexend 600, 24px, cream, and a five-bar level meter that follows A(t). "Hold to talk" at the start; pressed (scale 0.97) at 0.45 s and "Listening", with a thin ring breathing at 1 Hz; released at 5.5 s and "Polishing", the meter falling to zero; "Done" with a drawn check at 7.9 s.
- Summary chip: centered at y 770 under the paragraph, Lexend 500, 22px, secondary, in a pill with a 1px #4D6BD8 border, rising in at 8.0 s.
- Motion: arrivals rise 10px and fade over 0.25 s on cubic-bezier(0.22, 1, 0.36, 1); edits are short and crisp; the reflow is the one long, smooth move. Nothing bounces except the punctuation.
- Premium: a waveform that truly follows the speech (one bump per syllable, quiet gaps, a pause where the speaker hesitates), recognition that streams and fixes itself, a tidy-up where every change can be read, words that physically travel into a typeset paragraph. Cheap: a generic looping sine wave, all the words fading in at once, a crossfade from messy to clean text, a microphone emoji.
Banned: real apps, assistants, brands or logos; emoji; chat bubbles; neon glow; lorem ipsum; text under 20px; words that jump or teleport; any element blinking faster than 1 Hz; anything flashing more than 3 times per second.
</direction>
<structure>
- 0.0–0.6s: the cobalt field; the app mark and the mic pill rise in; the pill reads "Hold to talk", then presses and reads "Listening" (0.45 s); a flat line draws across the band.
- 0.6–3.1s: speech: the wave swells word by word and words condense into the draft: "um so the demo is thirsty", which becomes "thursday" as "no wait tuesday" arrives.
- 3.1–5.5s: "at uh three pm", a pause, then "can you send nor the slides", "nor" becoming "noor"; the last word lands at 5.44 s; the pill releases at 5.5 s and reads "Polishing".
- 5.5–6.0s: the wave relaxes to a flat line. At 6.0 s: the full raw draft on two lines with its three fillers underlined, the flat line above it, the pill reading "Polishing": a complete composition.
- 6.0–7.1s: the tidy-up: strikes and dissolves, the correction, "3 PM", capitals, punctuation.
- 7.1–7.9s: the reflow into the polished paragraph; the waveform line fades to 20%; the pill reads "Done".
- 7.9–8.6s: the edited letters cool from apricot to cream; the summary chip rises in.
- 8.6–10.0s: hold: only the background lift drifts slowly.
For other lengths, scale the speech timings together; keep the tidy-up steps in order 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 2D, repainting every frame from scratch, the backing store sized from CSS size × devicePixelRatio. Canvas won't fetch fonts itself, so the awaited loads matter.
- Measure every word in both faces with measureText after the fonts load, and lay out the draft (with its interim versions) and the final paragraph once at load. The waveform, word arrivals, letter swaps, strikes, dissolves, the FLIP reflow and the pill are closed-form functions of t and the word timings; dashes and dots follow seeded closed-form 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 the waveform's bumps line up with the word timings (two bumps on demo, thursday, tuesday and pm; silence in the gaps); each word appears only after it's spoken; both mishearings show and then fix themselves; every tidy-up step reads on its own; the polished paragraph reads exactly "The demo is Tuesday at 3 PM. Can you send Noor the slides?" with no overlaps; the chip says 19, 13 and 1; all text is at least 20px; the 6.0 s and final frames are complete compositions. 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, ribbon-like gradient mesh flows in a skewed band behind sharp headline type, while white payment, API and chart…
Glass panels with real refraction float over vivid, striped color: their rims bend what's behind them, two buttons fuse…
A dark-mode work tool drawn in hairlines and quiet radial glows: issue rows populate, ⌘K opens a command menu, a comman…
Rounded tiles spring into a bento grid on warm paper, each acting out one feature in a small animation, then the tiles…
More like this on the motion design inspiration board: Kinetic typography inspirationUI animation inspiration