AI CSS Animations

Lava Lamp Background

Warm wax blobs rise from a pool at the bottom, stretch, merge into the pool at the top and sink back. The goo is the blur and contrast trick in em, so it scales with --bgx-size.

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

HTML

<div class="bgx-lava-lamp" aria-hidden="true">
  <div class="bgx-lava-lamp-wax"><span></span><span></span><span></span><span></span><span></span><span></span></div>
</div>

CSS

.bgx-lava-lamp {
  --c: var(--bgx-color, #3b82f6);
  font-size: var(--bgx-size, 16px);
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: #000;
  isolation: isolate;
}
.bgx-lava-lamp-wax {
  position: absolute;
  inset: 0;
  background: #000;
  filter: contrast(24);
}
.bgx-lava-lamp-wax::before,
.bgx-lava-lamp-wax::after {
  content: "";
  position: absolute;
  left: -10%;
  right: -10%;
  border-radius: 50%;
  background: #fff;
  filter: blur(1em);
}
.bgx-lava-lamp-wax::before { bottom: -3.4em; height: 6em; }
.bgx-lava-lamp-wax::after { top: -3.8em; height: 5.4em; }
.bgx-lava-lamp-wax span {
  position: absolute;
  top: 0;
  left: var(--x);
  width: var(--size);
  height: 100%;
  animation: bgx-lava-lamp-rise var(--t) cubic-bezier(0.45, 0, 0.55, 1) var(--d) infinite;
}
.bgx-lava-lamp-wax span::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  aspect-ratio: 1;
  translate: 0 -50%;
  border-radius: 50%;
  background: #fff;
  filter: blur(1em);
  animation: bgx-lava-lamp-squish calc(var(--t) / 2) ease-in-out var(--d) infinite alternate;
}
.bgx-lava-lamp-wax span:nth-child(1) { --x: 8%; --size: 5em; --t: 13s; --d: -2s; }
.bgx-lava-lamp-wax span:nth-child(2) { --x: 26%; --size: 3.6em; --t: 10s; --d: -7s; }
.bgx-lava-lamp-wax span:nth-child(3) { --x: 42%; --size: 6.2em; --t: 16s; --d: -12s; }
.bgx-lava-lamp-wax span:nth-child(4) { --x: 60%; --size: 4.2em; --t: 11.5s; --d: -4s; }
.bgx-lava-lamp-wax span:nth-child(5) { --x: 74%; --size: 5.6em; --t: 14s; --d: -9s; }
.bgx-lava-lamp-wax span:nth-child(6) { --x: 88%; --size: 3.8em; --t: 9s; --d: -1s; }
.bgx-lava-lamp::before,
.bgx-lava-lamp::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.bgx-lava-lamp::before {
  z-index: 1;
  background: linear-gradient(to top, oklch(from var(--c) 0.72 0.19 calc(h + 120)), oklch(from var(--c) 0.64 0.24 calc(h + 80)) 55%, oklch(from var(--c) 0.58 0.22 calc(h + 40)));
  mix-blend-mode: multiply;
}
.bgx-lava-lamp::after {
  z-index: 2;
  background: radial-gradient(ellipse 80% 70% at 50% 50%, oklch(from var(--c) 0.26 0.08 calc(h + 60)), oklch(from var(--c) 0.16 0.05 calc(h + 40)));
  mix-blend-mode: lighten;
}
@keyframes bgx-lava-lamp-rise {
  0%, 100% { transform: translate(0, 54%); }
  50%      { transform: translate(18%, -54%); }
}
@keyframes bgx-lava-lamp-squish {
  from { transform: scale(1, 1); }
  to   { transform: scale(0.84, 1.2); }
}

@media (prefers-reduced-motion: reduce) {
  .bgx-lava-lamp, .bgx-lava-lamp *, .bgx-lava-lamp::before, .bgx-lava-lamp::after, .bgx-lava-lamp *::before, .bgx-lava-lamp *::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