30 CSS Keyframe Animations05 / 30

Pure CSSMIT licensed

CSS Morphing Blob Animation

Six organic morphing blob animations — duo-colour, gradient shift, blend cluster, breathing ring and labelled blob — using staggered border-radius keyframes.

Published

Live Demo
Try it

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

The code

<div class="kf-05">
  <div class="kf-05__row">
    <div class="kf-05__cell">
      <div class="kf-05__blob--1"></div>
      <span class="kf-05__label">Classic Blob</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--2"></div>
      <span class="kf-05__label">Duo-Color</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--3"></div>
      <span class="kf-05__label">Gradient Shift</span>
    </div>
  </div>
  <div class="kf-05__row">
    <div class="kf-05__cell">
      <div class="kf-05__cluster"><div class="kf-05__cluster-b"></div><div class="kf-05__cluster-b"></div><div class="kf-05__cluster-b"></div></div>
      <span class="kf-05__label">Blend Cluster</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--5"></div>
      <span class="kf-05__label">Breathing Ring</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--6">FLUID<br>DESIGN</div>
      <span class="kf-05__label">Label Blob</span>
    </div>
  </div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&display=swap');

.kf-05,
.kf-05 *,
.kf-05 *::before,
.kf-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.kf-05 ::selection {
  background: #ff6bd6;
  color: #fff;
}

.kf-05 {
  --bg: #fff8f5;
  --coral: #ff6b6b;
  --teal: #06d6a0;
  --indigo: #3d5af1;
  --pink: #ff6bd6;
  --yellow: #ffe45e;
  --dark: #1a1a2e;
  font-family: 'Syne',sans-serif;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 24px;
  gap: 64px;
  overflow: hidden;
  position: relative;
}
/* Soft BG blobs */

.kf-05::before {
  content: '';
  position: absolute;
  top: -10%;
  right: -5%;
  width: 500px;
  height: 500px;
  border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  background: rgba(255,107,214,.08);
  animation: kf-05-bg 12s ease-in-out infinite;
  z-index: 0;
}

.kf-05::after {
  content: '';
  position: absolute;
  bottom: -10%;
  left: -5%;
  width: 400px;
  height: 400px;
  border-radius: 42% 58% 37% 63%/55% 38% 62% 45%;
  background: rgba(6,214,160,.07);
  animation: kf-05-bg 10s ease-in-out infinite reverse;
  z-index: 0;
}

@keyframes kf-05-bg {
  0%,
    100% {
    border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  }

  50% {
    border-radius: 37% 63% 46% 54%/45% 55% 48% 52%;
    transform: scale(1.08);
  }
}

.kf-05__row {
  display: flex;
  gap: 48px;
  flex-wrap: wrap;
  justify-content: center;
  position: relative;
  z-index: 1;
}

.kf-05__cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.kf-05__label {
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #aaa;
}
/* 1 — Classic blob */

.kf-05__blob--1 {
  width: 140px;
  height: 140px;
  background: linear-gradient(135deg,var(--coral),var(--pink));
  animation: kf-05-blob1 6s ease-in-out infinite;
}

@keyframes kf-05-blob1 {
  0%,
    100% {
    border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  }

  25% {
    border-radius: 40% 60% 35% 65%/60% 30% 70% 40%;
  }

  50% {
    border-radius: 55% 45% 65% 35%/35% 65% 45% 55%;
  }

  75% {
    border-radius: 30% 70% 50% 50%/40% 60% 70% 30%;
  }
}
/* 2 — Duo-color blob */

.kf-05__blob--2 {
  width: 140px;
  height: 140px;
  position: relative;
  animation: kf-05-blob2 7s ease-in-out infinite;
}

.kf-05__blob--2::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg,var(--indigo),var(--teal));
  animation: kf-05-blob1 7s ease-in-out infinite;
  border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
}

.kf-05__blob--2::after {
  content: '';
  position: absolute;
  inset: 20px;
  background: rgba(255,248,245,.6);
  animation: kf-05-blob1 5s ease-in-out infinite reverse;
  border-radius: 50%;
}
/* 3 — Gradient shift blob */

.kf-05__blob--3 {
  width: 140px;
  height: 140px;
  animation: kf-05-blob3 8s ease-in-out infinite;
  background: linear-gradient(0deg,var(--yellow),var(--coral));
  background-size: 100% 200%;
}

@keyframes kf-05-blob3 {
  0%,
    100% {
    border-radius: 60% 40% 55% 45%/50% 55% 45% 50%;
    background-position: 0% 0%;
  }

  33% {
    border-radius: 40% 60% 30% 70%/60% 40% 60% 40%;
    background-position: 0% 50%;
  }

  66% {
    border-radius: 55% 45% 68% 32%/38% 68% 32% 62%;
    background-position: 0% 100%;
  }
}
/* 4 — Multi-blob cluster */

.kf-05__cluster {
  width: 180px;
  height: 180px;
  position: relative;
}

.kf-05__cluster-b {
  position: absolute;
  border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  mix-blend-mode: multiply;
}

.kf-05__cluster-b:nth-child(1) {
  width: 100px;
  height: 100px;
  background: rgba(255,107,107,.7);
  top: 0;
  left: 0;
  animation: kf-05-blob1 5s ease-in-out infinite;
}

.kf-05__cluster-b:nth-child(2) {
  width: 100px;
  height: 100px;
  background: rgba(61,90,241,.7);
  top: 30px;
  left: 50px;
  animation: kf-05-blob1 6s ease-in-out infinite .5s;
}

.kf-05__cluster-b:nth-child(3) {
  width: 100px;
  height: 100px;
  background: rgba(255,228,94,.7);
  top: 60px;
  left: 20px;
  animation: kf-05-blob1 7s ease-in-out infinite 1s;
}
/* 5 — Breathing ring blob */

.kf-05__blob--5 {
  width: 140px;
  height: 140px;
  border: 12px solid var(--pink);
  background: transparent;
  animation: kf-05-blob1 6s ease-in-out infinite 0.5s,kf-05-ring 3s ease-in-out infinite;
}

@keyframes kf-05-ring {
  0%,
    100% {
    border-width: 12px;
    box-shadow: 0 0 0 0 rgba(255,107,214,.3);
  }

  50% {
    border-width: 4px;
    box-shadow: 0 0 0 20px rgba(255,107,214,0);
  }
}
/* 6 — Text inside blob */

.kf-05__blob--6 {
  width: 160px;
  height: 160px;
  background: linear-gradient(135deg,var(--indigo),#7c3aed);
  display: grid;
  place-items: center;
  color: #fff;
  font-weight: 800;
  font-size: 1.2rem;
  text-align: center;
  line-height: 1.2;
  animation: kf-05-blob1 7s ease-in-out infinite;
  box-shadow: 0 20px 60px rgba(61,90,241,.35);
}

@media (max-width: 600px) {
  .kf-05__row {
    gap: 24px;
  }

  .kf-05__blob--1,
    .kf-05__blob--2,
    .kf-05__blob--3,
    .kf-05__blob--5,
    .kf-05__blob--6 {
    width: 110px;
    height: 110px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kf-05 * {
    animation: none!important;
    border-radius: 50%!important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Keyframe Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Morphing Blob Animation
Source: https://codefronts.com/motion/css-keyframe-animations/css-morphing-blob-animation/

Six organic morphing blob animations — duo-colour, gradient shift, blend cluster, breathing ring and labelled blob — using staggered border-radius keyframes.
## HTML
```html
<div class="kf-05">
  <div class="kf-05__row">
    <div class="kf-05__cell">
      <div class="kf-05__blob--1"></div>
      <span class="kf-05__label">Classic Blob</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--2"></div>
      <span class="kf-05__label">Duo-Color</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--3"></div>
      <span class="kf-05__label">Gradient Shift</span>
    </div>
  </div>
  <div class="kf-05__row">
    <div class="kf-05__cell">
      <div class="kf-05__cluster"><div class="kf-05__cluster-b"></div><div class="kf-05__cluster-b"></div><div class="kf-05__cluster-b"></div></div>
      <span class="kf-05__label">Blend Cluster</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--5"></div>
      <span class="kf-05__label">Breathing Ring</span>
    </div>
    <div class="kf-05__cell">
      <div class="kf-05__blob--6">FLUID<br>DESIGN</div>
      <span class="kf-05__label">Label Blob</span>
    </div>
  </div>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;700;800&display=swap');

.kf-05,
.kf-05 *,
.kf-05 *::before,
.kf-05 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.kf-05 ::selection {
  background: #ff6bd6;
  color: #fff;
}

.kf-05 {
  --bg: #fff8f5;
  --coral: #ff6b6b;
  --teal: #06d6a0;
  --indigo: #3d5af1;
  --pink: #ff6bd6;
  --yellow: #ffe45e;
  --dark: #1a1a2e;
  font-family: 'Syne',sans-serif;
  background: var(--bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 60px 24px;
  gap: 64px;
  overflow: hidden;
  position: relative;
}
/* Soft BG blobs */

.kf-05::before {
  content: '';
  position: absolute;
  top: -10%;
  right: -5%;
  width: 500px;
  height: 500px;
  border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  background: rgba(255,107,214,.08);
  animation: kf-05-bg 12s ease-in-out infinite;
  z-index: 0;
}

.kf-05::after {
  content: '';
  position: absolute;
  bottom: -10%;
  left: -5%;
  width: 400px;
  height: 400px;
  border-radius: 42% 58% 37% 63%/55% 38% 62% 45%;
  background: rgba(6,214,160,.07);
  animation: kf-05-bg 10s ease-in-out infinite reverse;
  z-index: 0;
}

@keyframes kf-05-bg {
  0%,
    100% {
    border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  }

  50% {
    border-radius: 37% 63% 46% 54%/45% 55% 48% 52%;
    transform: scale(1.08);
  }
}

.kf-05__row {
  display: flex;
  gap: 48px;
  flex-wrap: wrap;
  justify-content: center;
  position: relative;
  z-index: 1;
}

.kf-05__cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
}

.kf-05__label {
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: #aaa;
}
/* 1 — Classic blob */

.kf-05__blob--1 {
  width: 140px;
  height: 140px;
  background: linear-gradient(135deg,var(--coral),var(--pink));
  animation: kf-05-blob1 6s ease-in-out infinite;
}

@keyframes kf-05-blob1 {
  0%,
    100% {
    border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  }

  25% {
    border-radius: 40% 60% 35% 65%/60% 30% 70% 40%;
  }

  50% {
    border-radius: 55% 45% 65% 35%/35% 65% 45% 55%;
  }

  75% {
    border-radius: 30% 70% 50% 50%/40% 60% 70% 30%;
  }
}
/* 2 — Duo-color blob */

.kf-05__blob--2 {
  width: 140px;
  height: 140px;
  position: relative;
  animation: kf-05-blob2 7s ease-in-out infinite;
}

.kf-05__blob--2::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg,var(--indigo),var(--teal));
  animation: kf-05-blob1 7s ease-in-out infinite;
  border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
}

.kf-05__blob--2::after {
  content: '';
  position: absolute;
  inset: 20px;
  background: rgba(255,248,245,.6);
  animation: kf-05-blob1 5s ease-in-out infinite reverse;
  border-radius: 50%;
}
/* 3 — Gradient shift blob */

.kf-05__blob--3 {
  width: 140px;
  height: 140px;
  animation: kf-05-blob3 8s ease-in-out infinite;
  background: linear-gradient(0deg,var(--yellow),var(--coral));
  background-size: 100% 200%;
}

@keyframes kf-05-blob3 {
  0%,
    100% {
    border-radius: 60% 40% 55% 45%/50% 55% 45% 50%;
    background-position: 0% 0%;
  }

  33% {
    border-radius: 40% 60% 30% 70%/60% 40% 60% 40%;
    background-position: 0% 50%;
  }

  66% {
    border-radius: 55% 45% 68% 32%/38% 68% 32% 62%;
    background-position: 0% 100%;
  }
}
/* 4 — Multi-blob cluster */

.kf-05__cluster {
  width: 180px;
  height: 180px;
  position: relative;
}

.kf-05__cluster-b {
  position: absolute;
  border-radius: 63% 37% 54% 46%/55% 48% 52% 45%;
  mix-blend-mode: multiply;
}

.kf-05__cluster-b:nth-child(1) {
  width: 100px;
  height: 100px;
  background: rgba(255,107,107,.7);
  top: 0;
  left: 0;
  animation: kf-05-blob1 5s ease-in-out infinite;
}

.kf-05__cluster-b:nth-child(2) {
  width: 100px;
  height: 100px;
  background: rgba(61,90,241,.7);
  top: 30px;
  left: 50px;
  animation: kf-05-blob1 6s ease-in-out infinite .5s;
}

.kf-05__cluster-b:nth-child(3) {
  width: 100px;
  height: 100px;
  background: rgba(255,228,94,.7);
  top: 60px;
  left: 20px;
  animation: kf-05-blob1 7s ease-in-out infinite 1s;
}
/* 5 — Breathing ring blob */

.kf-05__blob--5 {
  width: 140px;
  height: 140px;
  border: 12px solid var(--pink);
  background: transparent;
  animation: kf-05-blob1 6s ease-in-out infinite 0.5s,kf-05-ring 3s ease-in-out infinite;
}

@keyframes kf-05-ring {
  0%,
    100% {
    border-width: 12px;
    box-shadow: 0 0 0 0 rgba(255,107,214,.3);
  }

  50% {
    border-width: 4px;
    box-shadow: 0 0 0 20px rgba(255,107,214,0);
  }
}
/* 6 — Text inside blob */

.kf-05__blob--6 {
  width: 160px;
  height: 160px;
  background: linear-gradient(135deg,var(--indigo),#7c3aed);
  display: grid;
  place-items: center;
  color: #fff;
  font-weight: 800;
  font-size: 1.2rem;
  text-align: center;
  line-height: 1.2;
  animation: kf-05-blob1 7s ease-in-out infinite;
  box-shadow: 0 20px 60px rgba(61,90,241,.35);
}

@media (max-width: 600px) {
  .kf-05__row {
    gap: 24px;
  }

  .kf-05__blob--1,
    .kf-05__blob--2,
    .kf-05__blob--3,
    .kf-05__blob--5,
    .kf-05__blob--6 {
    width: 110px;
    height: 110px;
  }
}

@media (prefers-reduced-motion: reduce) {
  .kf-05 * {
    animation: none!important;
    border-radius: 50%!important;
  }
}
```

How this works

Each blob uses asymmetric border-radius values like 63% 37% 54% 46% / 55% 48% 52% 45% — the slash separates horizontal and vertical radii, producing organic, non-elliptical curves. The keyframe walks through four different radius profiles at 0/25/50/75/100% with ease-in-out timing, so the silhouette appears to morph fluidly rather than stretch.

The duo-color variant stacks two ::before/::after blobs at different inset levels, each running the same keyframe but at 5s vs 7s reverse so they constantly desync. The blend cluster relies on mix-blend-mode: multiply across three semi-transparent overlapping blobs — the colour mix shifts as each runs at a different duration. The breathing ring layers two animations: the morph plus a border-width: 12px → 4px with expanding box-shadow.

Make it yours

  • Generate fresh blob profiles by adjusting the four radius percentages — keep all four 25-75% for natural shapes.
  • Recolour via the --coral, --pink, --indigo, --teal custom properties on the .kf-05 root.
  • Slow the morph by changing 6s on .kf-05__blob--1 to 10s for a more meditative pace.
  • Change the blend-cluster mix by switching mix-blend-mode: multiply to screen for lighter overlap colours.

Gotchas — read before shipping

  • mix-blend-mode forces the element into its own stacking context — child z-index values inside the cluster stop working as expected.
  • Animating border-radius every frame is cheap, but combining it with a large filter: blur() tanks performance on integrated GPUs.
  • The background blobs use overflow: hidden on the parent — removing that lets the soft shadow extend visibly past the section edge.

Browser support

ChromeSafariFirefoxEdge
55+10.1+52+55+

mix-blend-mode on the cluster needs Safari 10.1+; older versions render solid overlaps.

Techniques used in this demo

Search CodeFronts

Loading…