AI CSS Animations

Gooey Gradient Lava Background

Pastel wax blobs pinch off a glowing pool, drift up, stretch and sink back like a real lava lamp, lit from within. CSS blur on each shaded blob, then an inline SVG alpha threshold makes the goo.

Also known as: lava lamp css, lava lamp animation, gooey blob background, metaball css.

HTML

<div class="bgx-gooey-lava" aria-hidden="true">
  <svg class="bgx-gooey-lava-defs" width="0" height="0"><filter id="bgx-gooey-lava-goo"><feColorMatrix values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 14 -5.5" /></filter></svg>
  <span class="bgx-gooey-lava-tank"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span>
</div>

CSS

.bgx-gooey-lava {
  --c: var(--bgx-color, #3b82f6);
  font-size: var(--bgx-size, 16px);
  position: absolute;
  inset: 0;
  overflow: hidden;
  background:
    radial-gradient(ellipse 70% 60% at 50% 105%, oklch(from var(--c) 0.9 0.07 calc(h + 20) / 0.9), transparent),
    linear-gradient(oklch(from var(--c) 0.975 0.012 calc(h - 20)), oklch(from var(--c) 0.95 0.03 calc(h + 30)));}
.bgx-gooey-lava-defs {
  position: absolute;
}
.bgx-gooey-lava-tank {
  position: absolute;
  inset: 0;
  filter: url(#bgx-gooey-lava-goo) drop-shadow(0 0 0.8em oklch(from var(--c) 0.75 0.14 calc(h + 10) / 0.45));
}
.bgx-gooey-lava-tank::after {
  content: "";
  position: absolute;
  left: 50%;
  border-radius: 50%;
  filter: blur(0.7em);
}
.bgx-gooey-lava-tank::after {
  bottom: -3.2em;
  width: 120%;
  height: 6em;
  translate: -50% 0;
  background: radial-gradient(ellipse at 50% 20%, oklch(from var(--c) 0.86 0.1 calc(h + 30)), oklch(from var(--c) 0.7 0.16 calc(h + 0)) 60%, oklch(from var(--c) 0.62 0.17 calc(h - 30)));
}
.bgx-gooey-lava-tank span {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--x);
  width: var(--s);
  animation: bgx-gooey-lava-rise var(--t) cubic-bezier(0.45, 0, 0.55, 1) var(--d) infinite alternate;
}
.bgx-gooey-lava-tank span::before {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: var(--s);
  height: var(--s);
  border-radius: 50%;
  background: radial-gradient(circle at 38% 30%, var(--l), var(--a) 45%, var(--b));
  filter: blur(0.6em);
  animation: bgx-gooey-lava-stretch calc(var(--t) / 2) cubic-bezier(0.45, 0, 0.55, 1) var(--d) infinite alternate;
}
.bgx-gooey-lava-tank span:nth-child(1) { --x: 2%; --s: 5.02em; --t: 11.3s; --d: -7.8s; --l: oklch(from var(--c) 0.95 0.05 calc(h + 0)); --a: oklch(from var(--c) 0.8 0.13 calc(h + 0)); --b: oklch(from var(--c) 0.64 0.17 calc(h + 30)); --k: 0.76; }
.bgx-gooey-lava-tank span:nth-child(2) { --x: 14.7%; --s: 4.55em; --t: 15.8s; --d: -14.3s; --l: oklch(from var(--c) 0.95 0.05 calc(h + 40)); --a: oklch(from var(--c) 0.8 0.13 calc(h + 40)); --b: oklch(from var(--c) 0.64 0.17 calc(h + 70)); --k: 0.75; }
.bgx-gooey-lava-tank span:nth-child(3) { --x: 26.6%; --s: 4.12em; --t: 13.8s; --d: -5.7s; --l: oklch(from var(--c) 0.95 0.05 calc(h - 40)); --a: oklch(from var(--c) 0.8 0.13 calc(h - 40)); --b: oklch(from var(--c) 0.64 0.17 calc(h - 10)); --k: 0.53; }
.bgx-gooey-lava-tank span:nth-child(4) { --x: 39.1%; --s: 3.44em; --t: 15s; --d: -3.1s; --l: oklch(from var(--c) 0.95 0.05 calc(h + 20)); --a: oklch(from var(--c) 0.8 0.13 calc(h + 20)); --b: oklch(from var(--c) 0.64 0.17 calc(h + 50)); --k: 0.67; }
.bgx-gooey-lava-tank span:nth-child(5) { --x: 51.5%; --s: 4.68em; --t: 10s; --d: -4s; --l: oklch(from var(--c) 0.95 0.05 calc(h - 60)); --a: oklch(from var(--c) 0.8 0.13 calc(h - 60)); --b: oklch(from var(--c) 0.64 0.17 calc(h - 30)); --k: 0.49; }
.bgx-gooey-lava-tank span:nth-child(6) { --x: 62.3%; --s: 5.41em; --t: 12.4s; --d: -5.6s; --l: oklch(from var(--c) 0.95 0.05 calc(h + 60)); --a: oklch(from var(--c) 0.8 0.13 calc(h + 60)); --b: oklch(from var(--c) 0.64 0.17 calc(h + 90)); --k: 0.46; }
.bgx-gooey-lava-tank span:nth-child(7) { --x: 74.9%; --s: 5.44em; --t: 12.7s; --d: -9.7s; --l: oklch(from var(--c) 0.95 0.05 calc(h - 20)); --a: oklch(from var(--c) 0.8 0.13 calc(h - 20)); --b: oklch(from var(--c) 0.64 0.17 calc(h + 10)); --k: 0.63; }
.bgx-gooey-lava-tank span:nth-child(8) { --x: 88.5%; --s: 4.08em; --t: 12s; --d: -2.1s; --l: oklch(from var(--c) 0.95 0.05 calc(h + 30)); --a: oklch(from var(--c) 0.8 0.13 calc(h + 30)); --b: oklch(from var(--c) 0.64 0.17 calc(h + 60)); --k: 0.65; }
@keyframes bgx-gooey-lava-rise {
  from { transform: translateY(1.6em); }
  to   { transform: translateY(calc((-100% + var(--s) + 0.6em) * var(--k))); }
}
@keyframes bgx-gooey-lava-stretch {
  from { transform: scale(1, 1); }
  to   { transform: scale(0.82, 1.22); }
}
@media (prefers-reduced-motion: reduce) {
  .bgx-gooey-lava, .bgx-gooey-lava::before, .bgx-gooey-lava::after, .bgx-gooey-lava *, .bgx-gooey-lava *::before, .bgx-gooey-lava *::after { animation-play-state: paused; }
}

@media (prefers-reduced-motion: reduce) {
  .bgx-gooey-lava, .bgx-gooey-lava *, .bgx-gooey-lava::before, .bgx-gooey-lava::after, .bgx-gooey-lava *::before, .bgx-gooey-lava *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --bgx-color on the element or a parent. Currently previewing #3b82f6.
  • Pattern scale: set --bgx-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 16px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar animated backgrounds