webdevpuneet.com

Liquid Button — Free HTML CSS JS Gooey Hover Snippet

Liquid Button · Buttons · Plain HTML, CSS & JS · Live preview

CategoryButtons

Copy & paste

Source Code

<div class="lq-stage">
  <svg width="0" height="0" class="lq-defs"><filter id="lqGoo"><feGaussianBlur in="SourceGraphic" stdDeviation="8" result="b"/><feColorMatrix in="b" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 20 -9" result="goo"/><feBlend in="SourceGraphic" in2="goo"/></filter></svg>

  <button type="button" class="lq-btn" id="lqBtn">
    <span class="lq-blobs" id="lqBlobs"></span>
    <span class="lq-label">Subscribe</span>
  </button>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

SVG goo filter
Blur plus alpha contrast fuses overlapping blobs.
Rising metaballs
Nine blobs climb and merge into liquid.
Springy entrance
An overshooting cubic-bezier sells the wobble.
Staggered drain
Blobs recede and drip on exit.
Self-cleaning blobs
Each removes itself on finish.
Crisp label
Text stays above the goo, pointer-events none.
Contained compositing
isolation keeps the filter to the button.
Click confirmation
Label swaps to a success state.

About this UI Snippet

Liquid Button — Gooey Metaball Fill With an SVG Goo Filter

Screenshot of the Liquid Button snippet rendered live

The liquid button is the gooey hover effect where the button's fill rises from the bottom as a cluster of blobs that merge into one organic liquid surface — the classic "metaball" look. This snippet builds it with plain HTML, an SVG filter for the goo, CSS, and vanilla JavaScript using the Web Animations API to launch the blobs.

The SVG goo filter

The magic is a reusable SVG filter, #lqGoo. It blurs the source graphic with feGaussianBlur, then runs the blurred result through a feColorMatrix that cranks the alpha channel's contrast (0 0 0 20 -9 on the alpha row). The high alpha multiply plus the negative bias turns the soft blurred edges into a hard threshold: anywhere the blur is dense enough becomes fully opaque, anywhere it's faint becomes transparent. The effect is that separate blurred circles which overlap fuse into a single smooth blob with organic necks between them — exactly how liquid metaballs merge. Applying filter: url(#lqGoo) to the blob container makes every circle inside it gooey.

Blobs that rise and merge

On pointerenter, the script fires nine blobs in quick succession (35ms apart), each positioned at an even horizontal slot across the button. Each blob animates with the Web Animations API from below the button (translateY(40px) scale(0)) up into place at scale(1.5), using a springy cubic-bezier(.34, 1.4, .5, 1) that overshoots. Because the blobs are large and adjacent, they overlap as they rise — and the goo filter fuses them into one wobbling liquid fill that climbs the button. A solid base fill (the ::before) sits under them so the final state is a clean filled pill.

Draining on exit

On pointerleave, each existing blob animates back down and shrinks to zero on a slight stagger, then removes itself in the onfinish callback. The staggered drain makes the liquid appear to recede and drip away rather than vanishing instantly. Pending entrance timers are cleared first so a quick hover-in-hover-out doesn't leave orphaned blobs mid-flight.

Label above the goo

The text label sits at z-index: 2 with pointer-events: none, above the gooey fill layer, so it stays crisp and readable while the liquid animates behind it. isolation: isolate on the button contains the filter's compositing to the button itself. Clicking swaps the label to a "Subscribed ✓" confirmation for a moment.

Why the Web Animations API

Each blob needs its own timed, self-cleaning animation, and element.animate() returns a handle with an onfinish hook — perfect for fire-and-forget blobs that remove themselves when done. It avoids juggling CSS animation classes and animationend listeners across nine short-lived elements.

Customizing it

Change the blob count and size for a chunkier or finer liquid, tune the stdDeviation and the alpha matrix for more or less gooeyness, recolor the blobs and base fill, or adjust the spring easing. Make it a link by swapping the element. Pair it with a shimmer button or place it in an animated gradient CTA for a lively call to action.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA rounded button renders with a label.
  2. 2
    Hover the buttonGooey blobs rise from the bottom and merge into a liquid fill.
  3. 3
    Leave the buttonThe liquid drains and drips away on a stagger.
  4. 4
    Click itThe label flips to a Subscribed confirmation briefly.
  5. 5
    Tune the gooAdjust the blur and alpha matrix for more or less merge.
  6. 6
    Recolor itChange the blob and base fill colors.

Real-world uses

Common Use Cases

Playful calls to action
Pair with an animated gradient CTA for a lively button whose fill rises as nine blobs merging into one liquid surface.
Newsletter signups
Use inside a newsletter signup form, with an overshooting cubic-bezier giving the fill a springy entrance.
Landing heroes
Place under a lamp header title, with blobs receding and dripping away in a staggered drain on exit.
Creative portfolios
Match a shimmer button elsewhere on a creative site, or use as the action on a pricing card.
SVG goo filter reference
Learn how blur followed by an alpha contrast fuses overlapping blobs into a single organic shape through SVG metaball merging.
Related: Screen Orientation Lock Toggle
See the Screen Orientation Lock Toggle for a related buttons pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

The blob container has an SVG goo filter applied: feGaussianBlur softens the circles, then feColorMatrix sharply increases the alpha channel's contrast (multiply 20, bias -9). That thresholds the blur so dense overlaps become fully opaque and faint edges vanish, fusing overlapping circles into a single smooth blob with organic necks — the metaball effect.

On hover, nine blobs are launched 35ms apart at even horizontal slots. Each animates with the Web Animations API from below the button at scale 0 up to scale 1.5 using a springy cubic-bezier(.34,1.4,.5,1) that overshoots. Being large and adjacent, they overlap as they climb, and the goo filter fuses them into one liquid fill.

Each blob is a short-lived element that needs its own timed animation and must remove itself when done. element.animate() returns a handle with an onfinish callback, perfect for fire-and-forget blobs, and avoids managing CSS animation classes and animationend listeners across nine elements that come and go.

Yes. The label sits at z-index 2 with pointer-events: none, above the gooey fill layer, so it remains crisp while the liquid animates behind it. isolation: isolate on the button keeps the filter's compositing contained so it doesn't affect surrounding content.

Include the SVG filter once in your app (it can live in a shared layout). Render the button as a component and trigger the blob animations from pointerenter/pointerleave handlers that append blobs to a ref'd container with element.animate, cleaning them up on finish and clearing timers on unmount. In Tailwind, style the pill with utilities and keep the goo filter and blob logic in the component.