Gooey Blob Construction
Two overlapping divs blurred + contrast-thresholded into a single gooey shape via an SVG filter chain (gaussianBlur → colorMatrix). Three motion archetypes: a calm pulse, a synchronized orbit, and a chaotic jitter.
Two overlapping divs blurred + contrast-thresholded into a single gooey shape via an SVG filter chain (gaussianBlur → colorMatrix). Three motion archetypes: a calm pulse, a synchronized orbit, and a chaotic jitter.
Blob Construction / Blur Threshold Filter
Overlapping authored primitives merge through an SVG blur-and-threshold filter, showing how blob construction differs from a single morphing path.
Circles · merged bridge
Three circles converge through a shared filter region until their alpha fields fuse, then breathe back apart. Keep the filter region larger than the moving primitives so blur does not clip at the viewport edge.
Capsules · side pressure
Capsule-like blobs press together horizontally while a small satellite circle drifts in to demonstrate that primitives do not have to be circular. The same filter merges circles, rounded rects, or authored silhouettes as long as alpha overlaps.
Offset blobs · calm loop
Three satellite blobs orbit a central mass at different speeds + directions while the core silhouette breathes subtly. Useful when the blob needs continuous life without changing the central silhouette.
A CSS gooey blob is built from overlapping primitives passed through an SVG blur-and-threshold filter: blur fuses alpha, then feColorMatrix snaps the edge back.
1<div class="gooey-demo" aria-hidden="true"><svg width="0" height="0" focusable="false">3<filter id="gooey-construction-filter"x="-35%" y="-35%" width="170%" height="170%">5<feGaussianBlur in="SourceGraphic" stdDeviation="9" result="blur" />6<feColorMatrix in="blur" mode="matrix"values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7"result="goo" /><feBlend in="SourceGraphic" in2="goo" /></filter></svg><div class="gooey-demo__layer"><span class="gooey-demo__blob gooey-demo__blob--a"></span><span class="gooey-demo__blob gooey-demo__blob--b"></span><span class="gooey-demo__blob gooey-demo__blob--c"></span></div></div><style>.gooey-demo {position: relative;display: grid;place-items: center;width: 220px;height: 160px;overflow: visible;background: radial-gradient(circle, #12213b, #020617);border-radius: 22px;}.gooey-demo__layer {position: relative;width: 130px;height: 94px;34filter: url(#gooey-construction-filter);}.gooey-demo__blob {position: absolute;display: block;border-radius: 999px;background: linear-gradient(135deg, #67e8f9, #34d399);transform-box: fill-box;transform-origin: center;}.gooey-demo__blob--a {width: 68px;height: 68px;left: 10px;top: 18px;animation: gooey-construction-a 3.8s ease-in-out infinite alternate;}.gooey-demo__blob--b {width: 58px;height: 58px;right: 10px;top: 24px;animation: gooey-construction-b 3.8s ease-in-out infinite alternate;}.gooey-demo__blob--c {width: 38px;height: 38px;left: 50px;top: 0;animation: gooey-construction-c 3.8s ease-in-out infinite alternate;}@keyframes gooey-construction-a { to { transform: translateX(12px); } }66@keyframes gooey-construction-b { to { transform: translateX(-14px); } }@keyframes gooey-construction-c { to { transform: translateY(12px); } }@media (prefers-reduced-motion: reduce) {.gooey-demo__blob { animation: none; }}</style>
The filter region is too small, or a parent has overflow: hidden. Increase the filter x/y/width/height around the moving primitives and leave visual padding around the group. The blur needs space before thresholding can produce a clean edge.
The threshold step. Blur spreads alpha so shapes can overlap, but feColorMatrix raises the alpha contrast afterward, turning the soft overlap into one crisp silhouette. Without the matrix, the result is just fuzzy circles.
Blur alone makes soft circles; the threshold matrix turns the overlap into a crisp fused blob.
The threshold step. Blur spreads alpha so shapes can overlap, but feColorMatrix raises the alpha contrast afterward, turning the soft overlap into one crisp silhouette. Without the matrix, the result is just fuzzy circles.
A filter applied to each blob individually keeps them as separate silhouettes — the blur and threshold run per element, never sharing alpha. Applied to the shared parent, the two alpha fields blur and threshold together, fusing where they meet.
The threshold step. Blur spreads alpha so shapes can overlap, but feColorMatrix raises the alpha contrast afterward, turning the soft overlap into one crisp silhouette. Without the matrix, the result is just fuzzy circles.
It can be. Blur and color matrix filters touch many pixels, especially with large regions or high stdDeviation values. Keep the filtered area small, animate transform only, and avoid many independent gooey groups on the same viewport.
Same three blobs, same SVG goo filter, same 3.8s drift animation that pulls the blobs apart and back together. BEFORE fills all three with one cyan-green gradient — the merged shape reads as a single flat hue. AFTER swaps to three solid hues (cyan, pink, yellow) and applies mix-blend-mode: lighten, so each pixel keeps the brighter channel from the overlapping blobs. Cyan + pink → lilac, pink + yellow → peach, all three → bright pink core. The geometry is identical; only the color story changes.
The base recipe runs three blobs through the same monochromatic gradient, fused by the SVG goo filter — the merged shape reads as one flat hue. Real production examples (Stripe pricing graphics, Apple keynote intros) give each blob a distinct chromatic identity and use mix-blend-mode: lighten so overlap zones produce NEW colors per channel: cyan+pink→lilac, pink+yellow→peach, all three→bright pink core. The filter still fuses the alphas, so the geometry stays gooey; only the color story changes. isolation: isolate on the layer keeps the blend scoped.
Append these rules inside the <style> block from the base snippet above.
/* Advanced: color-shift + lighten blend at overlap — extends the base recipe. */.gooey-demo__layer {isolation: isolate;}.gooey-demo__blob {mix-blend-mode: lighten;}.gooey-demo__blob--a { background: #19c6e6; } /* cyan */.gooey-demo__blob--b { background: #ff3d8b; } /* pink */.gooey-demo__blob--c { background: #ffc21f; } /* yellow */
SVG gooey blob effects merge two or more overlapping shapes into a single fluid silhouette via an SVG filter that blurs the alpha channel and then thresholds it back to a hard edge. The primitives stay div-or-circle simple; the filter does the work. Pair the technique with mix-blend-mode or a colored gradient and the result passes for true metaball math without any WebGL or per-frame JS.
Reach for gooey for loading indicators, ambient hero accents, or any place where two shapes need to feel related (a navigation dot that absorbs into the next dot, drops of liquid coalescing, a marker that splits when reaching a divider). Skip it for content layout — the filter region creates a stacking context that traps clicks and can break form layouts beneath. Skip it under transparent backgrounds; the threshold matrix depends on opaque alpha to find an edge.
The filter is a two-step pipeline: feGaussianBlur stdDeviation="9" smudges the alpha channel of overlapping shapes into one blurry mass, then feColorMatrix multiplies the alpha by ~18 and subtracts ~7 so the smooth blur snaps back to a hard edge wherever the alpha crossed a threshold. Shapes that were too far apart to overlap during the blur step stay separate; shapes that overlapped fuse. Apply the filter to a parent<g> or div containing the primitives. Animate the underlying shapes’ transform or cx/cy — the goo follows.
In practical search terms, feColorMatrix values are the whole trick. A matrix tail like 18 -7 means “multiply the blurred alpha by 18, then subtract 7,” which crushes the soft blur into a crisp edge. A stronger tail like 21 -7 tightens the threshold and makes the gooey bridge appear later; a lower multiplier makes blobs merge sooner but look mushier. Tune the multiplier and the feGaussianBlur stdDeviation together instead of copying one magic matrix into every SVG gooey effect.
Filter region defaults to a small bounding box; set x="-35%" y="-35%" width="170%" height="170%" on the filter so blur near the edges does not clip into a hard line. The created stacking context blocks pointer-events on anything underneath the filtered region; if a form sits beneath, use pointer-events: none on the gooey wrapper or move the form out of the filter region. On Safari iOS the colorMatrix multiplier > 20 can banding-artifact — stay in the 17-19 range. Animating the shapes with filter: blur already applied is double work; use a single SVG filter, not two.
The gooey region is decorative; wrap it in aria-hidden="true" and ensure focusable content sits outside the filtered region (the stacking context can make focus rings render wrong against the filter). Under prefers-reduced-motion: reduce freeze the underlying shape positions; the goo itself is fine because the filter is decorative geometry, not motion.
The blur and threshold are a coupled pair. feGaussianBlur decides how far neighboring shapes bleed into each other, while feColorMatrix decides where that soft overlap snaps back into a hard gooey edge.
Tune matrix values against the actual blob size. A value such as 18 -7 can feel soft at one stdDeviation and too sticky at another; a value such as 21 -7 delays the bridge and needs enough overlap to avoid flicker.