An organic blob whose outline keeps morphing while a mesh of accent, pink, cyan and white light swirls inside it, with a soft glow underneath. Border radius keyframes plus two counter rotating layers.
Also known as: ai blob animation, mesh gradient blob css, morphing blob css, voice assistant blob.
<div class="aia-mesh-voice-blob" role="status" aria-label="Assistant is speaking"> <span class="aia-mesh-voice-blob-a"></span> <span class="aia-mesh-voice-blob-b"></span> </div>
.aia-mesh-voice-blob {
--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;
position: relative;
width: 8.5em;
height: 8.5em;
overflow: hidden;
isolation: isolate;
background: var(--c);
border-radius: 46% 54% 58% 42% / 44% 46% 54% 56%;
box-shadow: 0 1.2em 2.6em -1em color-mix(in srgb, var(--c) 75%, transparent), inset 0 0.3em 0.9em rgba(255, 255, 255, 0.35);
animation:
aia-mesh-voice-blob-morph 7s cubic-bezier(0.45, 0, 0.55, 1) infinite,
aia-mesh-voice-blob-beat 2.4s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}
.aia-mesh-voice-blob-a,
.aia-mesh-voice-blob-b {
position: absolute;
inset: -30%;
z-index: -1;
filter: blur(0.9em);
}
.aia-mesh-voice-blob-a {
background:
radial-gradient(circle at 30% 32%, color-mix(in oklab, var(--c) 30%, #ff6fb5) 0 20%, transparent 42%),
radial-gradient(circle at 72% 68%, color-mix(in oklab, var(--c) 25%, #3ddcff) 0 18%, transparent 40%);
animation: aia-mesh-voice-blob-turn 9s linear infinite;
}
.aia-mesh-voice-blob-b {
background:
radial-gradient(circle at 66% 28%, rgba(255, 255, 255, 0.85) 0 8%, transparent 30%),
radial-gradient(circle at 30% 74%, color-mix(in oklab, var(--c) 30%, #ffb35c) 0 12%, transparent 34%);
mix-blend-mode: screen;
animation: aia-mesh-voice-blob-turn 13s linear infinite reverse;
}
@keyframes aia-mesh-voice-blob-turn {
to { rotate: 360deg; }
}
@keyframes aia-mesh-voice-blob-morph {
0%, 100% { border-radius: 46% 54% 58% 42% / 44% 46% 54% 56%; }
25% { border-radius: 58% 42% 44% 56% / 52% 60% 40% 48%; }
50% { border-radius: 42% 58% 52% 48% / 60% 42% 58% 40%; }
75% { border-radius: 54% 46% 40% 60% / 42% 54% 46% 58%; }
}
@keyframes aia-mesh-voice-blob-beat {
0%, 100% { scale: 1; }
18% { scale: 1.07; }
32% { scale: 0.99; }
52% { scale: 1.1; }
70% { scale: 1.01; }
84% { scale: 1.04; }
}
@media (prefers-reduced-motion: reduce) {
.aia-mesh-voice-blob,
.aia-mesh-voice-blob-a,
.aia-mesh-voice-blob-b { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
.aia-mesh-voice-blob, .aia-mesh-voice-blob *, .aia-mesh-voice-blob::before, .aia-mesh-voice-blob::after, .aia-mesh-voice-blob *::before, .aia-mesh-voice-blob *::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.