Spoken words land one by one in a transcript card, arriving blurred and faint as a guess, then settling into solid text. One animated @property drives every word, so the sentence clears together.
Also known as: live transcription animation, speech to text animation css, live captions css, voice transcript animation.
Can you move my 3pm with Priya to Thursday and send her the new invite
<div class="aia-live-transcription" role="status" aria-label="Live transcription">
<div class="aia-live-transcription-head">
<span class="aia-live-transcription-dot"></span>
<span>Listening</span>
<span class="aia-live-transcription-lang">EN</span>
</div>
<p class="aia-live-transcription-text"><span style="--i: 0">Can</span> <span style="--i: 1">you</span> <span style="--i: 2">move</span> <span style="--i: 3">my</span> <span style="--i: 4">3pm</span> <span style="--i: 5">with</span> <span style="--i: 6">Priya</span> <span style="--i: 7">to</span> <span style="--i: 8">Thursday</span> <span style="--i: 9">and</span> <span style="--i: 10">send</span> <span style="--i: 11">her</span> <span style="--i: 12">the</span> <span style="--i: 13">new</span> <span style="--i: 14">invite</span></p>
</div>@property --aia-live-transcription-t {
syntax: "<number>";
inherits: true;
initial-value: 0;
}
.aia-live-transcription {
--c: var(--aia-color, #7c5cff);
font-size: var(--aia-size, 16px);
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
line-height: 1.4;
width: 22em;
padding: 1em 1.15em 1.1em;
box-sizing: border-box;
border-radius: 1em;
background: color-mix(in srgb, currentColor 4%, transparent);
box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 12%, transparent);
animation: aia-live-transcription-clock 6.5s linear infinite;
}
.aia-live-transcription-head {
display: flex;
align-items: center;
gap: 0.5em;
margin-bottom: 0.6em;
font-size: 0.8em;
font-weight: 500;
color: color-mix(in srgb, currentColor 60%, transparent);
}
.aia-live-transcription-dot {
width: 0.55em;
height: 0.55em;
border-radius: 50%;
background: var(--c);
box-shadow: 0 0 0 0 color-mix(in srgb, var(--c) 50%, transparent);
animation: aia-live-transcription-ping 1.6s cubic-bezier(0.2, 0.6, 0.35, 1) infinite;
}
.aia-live-transcription-lang {
margin-left: auto;
padding: 0.1em 0.45em;
border-radius: 0.35em;
font-size: 0.85em;
letter-spacing: 0.04em;
background: color-mix(in srgb, currentColor 8%, transparent);
}
.aia-live-transcription-text {
margin: 0;
font-size: 1.12em;
font-weight: 500;
line-height: 1.45;
letter-spacing: -0.01em;
opacity: clamp(0, (0.95 - var(--aia-live-transcription-t)) * 16, 1);
}
.aia-live-transcription-text span {
--s: calc(0.04 + var(--i) * 0.036);
--in: clamp(0, (var(--aia-live-transcription-t) - var(--s)) * 14, 1);
--sure: clamp(0, (var(--aia-live-transcription-t) - var(--s) - 0.07) * 12, 1);
display: inline-block;
opacity: calc(var(--in) * (0.38 + var(--sure) * 0.62));
filter: blur(calc((1 - var(--in)) * 0.25em));
translate: 0 calc((1 - var(--in)) * 0.35em);
}
@keyframes aia-live-transcription-clock {
to { --aia-live-transcription-t: 1; }
}
@keyframes aia-live-transcription-ping {
70%, 100% { box-shadow: 0 0 0 0.5em transparent; }
}
@media (prefers-reduced-motion: reduce) {
.aia-live-transcription { animation: none; --aia-live-transcription-t: 0.85; }
.aia-live-transcription-dot { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
.aia-live-transcription, .aia-live-transcription *, .aia-live-transcription::before, .aia-live-transcription::after, .aia-live-transcription *::before, .aia-live-transcription *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}--aia-color on the element or a parent.
Currently previewing #3b82f6.--aia-size. Every other dimension is in
em, so the whole thing scales together. Currently previewing 16px.transition or animation
shorthand. Staggered delays are relative, so they keep their rhythm.