A frame of boiling grain slowly denoises into a dusk landscape while a step counter climbs to 30 of 30. The scene is pure gradients, the grain an SVG turbulence filter.
Also known as: image generation progress animation css, ai image generation animation, diffusion loading animation, noise to image reveal.
<div class="aia-diffusion-reveal" role="status" aria-label="Generating image">
<div class="aia-diffusion-reveal-frame">
<div class="aia-diffusion-reveal-scene">
<span class="aia-diffusion-reveal-far"></span>
<span class="aia-diffusion-reveal-near"></span>
</div>
<svg class="aia-diffusion-reveal-noise" aria-hidden="true">
<filter id="aia-diffusion-reveal-grain"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2" stitchTiles="stitch"/><feColorMatrix type="saturate" values="0.2"/></filter>
<rect width="100%" height="100%" filter="url(#aia-diffusion-reveal-grain)"/>
</svg>
</div>
<div class="aia-diffusion-reveal-caption">
<span>Generating</span>
<span class="aia-diffusion-reveal-step"></span>
</div>
</div>@property --aia-diffusion-reveal-n {
syntax: "<integer>";
inherits: false;
initial-value: 0;
}
.aia-diffusion-reveal {
--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: 6s;
display: grid;
gap: 0.6em;
width: 15em;
}
.aia-diffusion-reveal-frame {
position: relative;
height: 9.6em;
border-radius: 0.8em;
overflow: hidden;
background: #3a3a40;
box-shadow: 0 0 0 1px color-mix(in srgb, currentColor 12%, transparent), 0 0.8em 1.6em -0.9em rgba(0, 0, 0, 0.45);
}
.aia-diffusion-reveal-scene {
position: absolute;
inset: 0;
background:
radial-gradient(circle at 68% 56%, #fff4dc 0 6%, transparent 6.5%),
radial-gradient(circle at 68% 56%, color-mix(in oklab, var(--c) 25%, #ffb35c), transparent 38%),
linear-gradient(180deg, color-mix(in oklab, var(--c) 70%, #0b0b2a), color-mix(in oklab, var(--c) 45%, #ff7a59) 58%, #ffc98f 74%);
animation: aia-diffusion-reveal-denoise var(--t) cubic-bezier(0.25, 0.6, 0.35, 1) infinite;
}
.aia-diffusion-reveal-far,
.aia-diffusion-reveal-near {
position: absolute;
inset: 0;
}
.aia-diffusion-reveal-far {
background: linear-gradient(180deg, color-mix(in oklab, var(--c) 45%, #5b3a66), color-mix(in oklab, var(--c) 30%, #2a2140));
clip-path: polygon(0 66%, 14% 52%, 26% 60%, 40% 44%, 55% 58%, 63% 54%, 78% 62%, 90% 50%, 100% 58%, 100% 100%, 0 100%);
}
.aia-diffusion-reveal-near {
background: linear-gradient(180deg, #24182f, #120d1a);
clip-path: polygon(0 78%, 18% 68%, 34% 76%, 50% 66%, 66% 74%, 82% 70%, 100% 80%, 100% 100%, 0 100%);
}
.aia-diffusion-reveal-noise {
position: absolute;
inset: -20%;
width: 140%;
height: 140%;
mix-blend-mode: hard-light;
animation:
aia-diffusion-reveal-grain var(--t) cubic-bezier(0.25, 0.6, 0.35, 1) infinite,
aia-diffusion-reveal-boil 0.4s steps(4) infinite;
}
.aia-diffusion-reveal-caption {
display: flex;
justify-content: space-between;
font-size: 0.82em;
font-weight: 500;
color: color-mix(in srgb, currentColor 65%, transparent);
}
.aia-diffusion-reveal-step {
font-variant-numeric: tabular-nums;
counter-reset: aia-diffusion-reveal-n var(--aia-diffusion-reveal-n);
animation: aia-diffusion-reveal-count var(--t) cubic-bezier(0.25, 0.6, 0.35, 1) infinite;
}
.aia-diffusion-reveal-step::before { content: "Step " counter(aia-diffusion-reveal-n) " of 30"; }
@keyframes aia-diffusion-reveal-denoise {
0% { filter: blur(1.2em) saturate(0.3); scale: 1.12; }
72%, 90% { filter: blur(0) saturate(1); scale: 1; }
100% { filter: blur(1.2em) saturate(0.3); scale: 1.12; }
}
@keyframes aia-diffusion-reveal-grain {
0% { opacity: 1; }
72%, 90% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes aia-diffusion-reveal-boil {
0% { translate: 0 0; }
25% { translate: -4% 3%; }
50% { translate: 3% -2%; }
75% { translate: -2% -4%; }
100% { translate: 0 0; }
}
@keyframes aia-diffusion-reveal-count {
0% { --aia-diffusion-reveal-n: 0; }
72%, 100% { --aia-diffusion-reveal-n: 30; }
}
@media (prefers-reduced-motion: reduce) {
.aia-diffusion-reveal-scene,
.aia-diffusion-reveal-noise,
.aia-diffusion-reveal-step { animation: none; }
.aia-diffusion-reveal-noise { opacity: 0; }
.aia-diffusion-reveal-step { --aia-diffusion-reveal-n: 30; }
}
@media (prefers-reduced-motion: reduce) {
.aia-diffusion-reveal, .aia-diffusion-reveal *, .aia-diffusion-reveal::before, .aia-diffusion-reveal::after, .aia-diffusion-reveal *::before, .aia-diffusion-reveal *::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.