Glassy Button with SVG Filters & GSAP in Webflow

Intermediate 27:13 webflowsvg-filtersgsaphoverglassmorphism

Build a glassy crystal button in Webflow that refracts the page behind it with an SVG filter, then sharpens from soft glass to crisp ice on hover with GSAP.

Key takeaways

  • The glass look isn't a CSS blur — it's an SVG filter applied through backdrop-filter, so the button actually refracts whatever scrolls behind it instead of just frosting it.
  • A displacement map is the secret ingredient: it's a texture the filter reads like a topographic map to decide how far to push and pull each pixel, which is why the effect looks like real glass and not a flat blur.
  • The whole thing is driven by three filter primitives — feImage loads the map, feGaussianBlur softens the button, and feDisplacementMap bends it — and its scale parameter alone takes you from gentle ripples to funhouse-mirror chaos.
  • The script finds your button by the fc-glass-button attribute, not a class name, so you can name your classes anything you like and the effect still hooks up.
  • GSAP animates the same filter values on hover, and because it also fires on keyboard focus, the crystal transition stays fully accessible instead of being a mouse-only flourish.

Video chapters

  1. 00:00 Intro
  2. 01:47 Analyzing the structure of the page
  3. 04:16 Creating the button
  4. 07:37 Adding the SVG filter
  5. 08:53 Linking the SVG filter to the button
  6. 13:10 How the SVG filter works and how to customize it
  7. 19:00 Animating the button with GSAP
  8. 23:38 Bonus examples
  9. 24:18 Multiple button instances or different pages
  10. 26:00 Outro

Build a glassy crystal button in Webflow that actually refracts the page behind it, then comes to life on hover — softening from frosted glass to crisp ice with a small nudge of GSAP. The trick isn’t a CSS blur; it’s a real SVG filter wired to the button through backdrop-filter, so whatever scrolls underneath bends and ripples through the glass.

What makes it worth the setup is how physical it feels. Because the filter reads the actual content behind the button, the effect changes as you scroll, and the hover animation gives it a tactile little lift. It looks like a lot of moving parts, but underneath it’s three SVG filter primitives, a few lines of JavaScript to feed it a texture, and a tiny GSAP timeline.

And none of it is locked to my class names. The script hooks onto an attribute, so you can name things however you like and still get the effect for free.

How it works

The glass look comes from an SVG filter, not a CSS blur. You paste the filter markup (from the cloneable) into an Embed element, give it display: none, and move it to the very top of the <body>. That placement matters: put the filter at the top so it’s global and always available — drop it lower and some browsers scope it locally or skip it, the same way a CSS variable belongs in :root. The filter carries id="glass", and the button references it with a custom property, backdrop-filter: url(#glass). That single line is the connection: the button grabs its filter source from the element with that ID.

Inside the filter, three primitives do the work. feImage loads the displacement map — a texture that acts like a topographic map, where lighter and darker areas tell the filter how far to push or pull each pixel — and outputs a result the rest of the chain can read. feGaussianBlur softens the button first; its stdDeviation controls how sharp or cloudy the glass looks (0.01–0.02 is a subtle haze, 1–2 is properly fuzzy). Then the star, feDisplacementMap, takes the blurred button and the map together and shifts the pixels — its scale is the intensity dial: 0 is no distortion, 15 gives gentle glass ripples, 20+ turns into funhouse-mirror chaos. The filter region is deliberately oversized (x="-1" y="-1" width="3" height="3") so nothing clips at the edges.

There’s one thing the markup alone can’t do: the displacement map is an external image, so a short JavaScript snippet fetches it and feeds it into feImage. Without that, the filter knows it should distort but has no texture to distort with. This script lives in a second Embed at the bottom of the body — not strictly required, since it runs after the DOM is ready, but it’s the cleaner, more predictable place for it. Crucially, the script targets the button by the fc-glass-button attribute, not by a class, so your naming convention is completely irrelevant.

The hover comes last, and it’s GSAP. A tiny timeline animates the displacement and blur from the resting “soft glass” state to a “crisp ice” state, nudging the button up and scaling it slightly for tactile feedback. You tune it with a handful of constants at the top of the code: hoverScale (the distortion strength — ~1.05–1.25 for a classy ripple, 1.40+ for jagged broken-crystal edges), hoverTransScale (the button’s scale transform — keep it modest), hoverBlur (0 for crisp, ~0.03 for a softer landing), duration (0.2s snappy, 0.4s elegant), and ease (e.g. power1.out, power2.out, expo.out). The same animation also runs on keyboard focus and reverses on blur, so keyboard users get the exact same experience — the effect stays accessible, not mouse-only.

How to use it

  1. Clone the project. Grab the Webflow cloneable — it ships with the button, the SVG filter markup, the displacement-map loader, and the GSAP hover snippet, all commented.

  2. Give the button contrast to refract. The effect only shines when there’s content behind it. In the demo the button sits fixed and centered while a full-width background image scrolls underneath, so you can watch the filter change with the page. Any rich, scrollable background works.

  3. Style the glass base. Make the button background transparent with a subtle white linear gradient (roughly 135deg, white at ~20% → ~8% → ~3% opacity), a pill border-radius, and a 1px white border at ~30% opacity. This frosted base is what the filter refracts — tweak or drop it freely; it changes the look, not the filter.

  4. Add the filter. Paste the filter markup into an Embed, set it to display: none, and move that Embed to the top of the body. It has id="glass".

  5. Link the filter to the button. On the button, add the custom property backdrop-filter with the value url(#glass). You’ll see a rough glass effect appear immediately.

  6. Load the displacement map. Paste the loader script into a second Embed (display: none), placed at the bottom of the body. This feeds the map image into the filter so it has texture to bend.

  7. Tag the button. Add the attribute fc-glass-button to your button so the script can find it regardless of its class name.

  8. Add the hover and turn on GSAP. Paste the GSAP snippet, then flip GSAP on in your project settings — the code does nothing until the library loads. Tweak hoverScale, hoverBlur, duration, and ease to taste.

  9. Reuse it. For several buttons on one page, duplicate the button — the loader already handles multiple instances. For other pages, turn the filter and script into components (edits then propagate everywhere), or place the script alone in the site-wide footer code and drop a button wherever you need it.

Resources

Frequently asked questions

How do I make a glass button that distorts the background in Webflow?
Real background distortion comes from an SVG filter, not a CSS blur. You embed a filter that uses feDisplacementMap and reference it from the button with backdrop-filter: url(#glass) — the filter then bends whatever sits behind the button, so scrolling content ripples through it like glass.
What is a displacement map and why does the effect need one?
A displacement map is a grayscale or colored texture the filter reads like a topographic map: lighter and darker areas tell it how much to push or pull each pixel. Without one the filter has no idea how to bend the light, so the glass looks flat — the map is what gives it depth and movement.
Why is my SVG filter not working when I add it in Webflow?
Two things usually cause it. Place the filter embed at the very top of the body so it's globally available — dropped further down, some browsers scope it locally and ignore it. And make sure the button carries the fc-glass-button attribute, because the loader script targets that attribute rather than a class.
Do I need GSAP for the hover animation on the button?
Yes — the hover transition from soft glass to crisp ice is a GSAP timeline that animates the filter's blur and displacement. Remember to switch GSAP on in Webflow's project settings; the code runs but nothing moves until the library itself is loaded.
Can I use the same glassy button on multiple pages in Webflow?
You can. The loader script already handles several buttons on one page, so you just duplicate the button. To reuse it across pages, turn the filter and the script into components (so edits propagate everywhere), or drop the script alone into the site-wide footer code and place a button on any page.

← Back to the course

Also part of these courses