A glossy sky orb with clouds drifting inside wobbles gently while listening, then beats in bigger pulses as it speaks, and the label swaps to match. Gradients only, one shared clock.
Also known as: voice agent animation, chatgpt voice orb css, animated orb css, voice mode orb.
<div class="aia-voice-orb-states" role="status" aria-label="Voice mode: listening, then speaking">
<div class="aia-voice-orb-states-orb"><span class="aia-voice-orb-states-clouds"></span></div>
<div class="aia-voice-orb-states-label">
<span class="aia-voice-orb-states-listen">Listening</span>
<span class="aia-voice-orb-states-speak">Speaking</span>
</div>
</div>.aia-voice-orb-states {
--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;
--t: 8s;
display: grid;
justify-items: center;
gap: 1.1em;
}
.aia-voice-orb-states-orb {
position: relative;
width: 7.5em;
aspect-ratio: 1;
border-radius: 50%;
overflow: hidden;
background:
radial-gradient(circle at 50% 120%, color-mix(in srgb, var(--c) 80%, #000), transparent 60%),
linear-gradient(180deg, color-mix(in srgb, var(--c) 30%, #fff), color-mix(in srgb, var(--c) 75%, #fff) 40%, var(--c) 80%);
box-shadow:
inset 0 0.35em 0.9em rgba(255, 255, 255, 0.55),
inset 0 -0.5em 1.2em color-mix(in srgb, var(--c) 70%, #000),
0 0.9em 2.2em -0.7em color-mix(in srgb, var(--c) 70%, transparent);
animation: aia-voice-orb-states-pulse var(--t) cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
/* Two copies of the cloud field side by side; sliding one width loops cleanly. */
.aia-voice-orb-states-clouds {
position: absolute;
inset: 0 auto 0 0;
width: 200%;
background:
radial-gradient(ellipse 26% 16% at 12% 56%, rgba(255, 255, 255, 0.85), transparent 70%),
radial-gradient(ellipse 18% 13% at 22% 48%, rgba(255, 255, 255, 0.75), transparent 70%),
radial-gradient(ellipse 30% 14% at 36% 80%, rgba(255, 255, 255, 0.6), transparent 70%),
radial-gradient(ellipse 16% 10% at 40% 36%, rgba(255, 255, 255, 0.55), transparent 70%);
background-size: 50% 100%;
filter: blur(0.22em);
opacity: 0.8;
animation: aia-voice-orb-states-drift calc(var(--t) * 1.5) linear infinite;
}
.aia-voice-orb-states-label {
display: grid;
font-size: 0.95em;
font-weight: 500;
letter-spacing: 0.01em;
}
.aia-voice-orb-states-label > span {
grid-area: 1 / 1;
text-align: center;
opacity: 0;
color: color-mix(in srgb, currentColor 70%, transparent);
animation: aia-voice-orb-states-listen var(--t) ease infinite;
}
.aia-voice-orb-states-label > .aia-voice-orb-states-speak { animation-name: aia-voice-orb-states-speak; }
@keyframes aia-voice-orb-states-drift {
to { transform: translateX(-50%); }
}
@keyframes aia-voice-orb-states-pulse {
0%, 100% { scale: 1; }
8% { scale: 1.03; }
14% { scale: 0.99; }
22% { scale: 1.045; }
30% { scale: 1; }
38% { scale: 1.03; }
47% { scale: 1; }
54% { scale: 1.11; }
58% { scale: 1.02; }
63% { scale: 1.14; }
67% { scale: 1.03; }
72% { scale: 1.08; }
76% { scale: 1.01; }
82% { scale: 1.12; }
87% { scale: 1.02; }
92% { scale: 1.06; }
}
@keyframes aia-voice-orb-states-listen {
0%, 44%, 100% { opacity: 1; translate: 0 0; }
49%, 96% { opacity: 0; translate: 0 -0.3em; }
}
@keyframes aia-voice-orb-states-speak {
0%, 46%, 100% { opacity: 0; translate: 0 0.3em; }
51%, 94% { opacity: 1; translate: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
.aia-voice-orb-states-orb,
.aia-voice-orb-states-clouds,
.aia-voice-orb-states-label > span { animation: none !important; }
.aia-voice-orb-states-listen { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.aia-voice-orb-states, .aia-voice-orb-states *, .aia-voice-orb-states::before, .aia-voice-orb-states::after, .aia-voice-orb-states *::before, .aia-voice-orb-states *::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.