6 SVG filter distortion effects — glitch, goo, wave, melt, shake, warp
Hover over the button to see the effect
Copy URL or Code
Paste to your AI coding assistant and customize:
Done. Your AI handles the rest.
6 effects available. Glitch (digital), Goo (liquid), Wave (watery), Melt (dripping), Shake (vibration), Warp (extreme). GPU-accelerated SVG filters. Works in Chrome, Firefox, Safari.
/*
================================================================================
AI COMPONENT: GlitchButton
================================================================================
SETUP:
1. Install dependency: npm install gsap
2. Create file: components/GlitchButton.tsx
3. Copy this entire code block into that file
4. Import: import GlitchButton from "@/components/GlitchButton"
================================================================================
QUICK CUSTOMIZATION (via props)
================================================================================
| Prop | Type | Default |
|------------------|-----------------------------------------|-------------|
| children | ReactNode | required |
| variant | "glitch" | "ripple" | "turbulence" | "glitch" |
| intensity | number (0-100) | 30 |
| duration | number (ms) | 200 |
| trigger | "hover" | "click" | "both" | "hover" |
| className | string | "" |
| buttonClassName | string | "" |
================================================================================
EFFECT VARIANTS
================================================================================
| Variant | Description |
|-------------|--------------------------------------------------------|
| glitch | Quick turbulence burst - digital glitch aesthetic |
| ripple | Outward displacement ripple from center |
| turbulence | Continuous wavy distortion while active |
================================================================================
SOURCE CODE
================================================================================
*/
"use client";
import React, { useRef, useEffect, useId, ReactNode, useCallback } from "react";
import gsap from "gsap";
export type EffectVariant = "glitch" | "ripple" | "turbulence";
export type TriggerType = "hover" | "click" | "both";
export interface GlitchButtonProps {
children: ReactNode;
variant?: EffectVariant;
intensity?: number;
duration?: number;
trigger?: TriggerType;
className?: string;
buttonClassName?: string;
onClick?: () => void;
}
export default function GlitchButton({
children,
variant = "glitch",
intensity = 30,
duration = 200,
trigger = "hover",
className = "",
buttonClassName = "",
onClick,
}: GlitchButtonProps) {
const filterId = useId().replace(/:/g, "");
const buttonRef = useRef<HTMLButtonElement>(null);
const turbulenceRef = useRef<SVGFETurbulenceElement>(null);
const displacementRef = useRef<SVGFEDisplacementMapElement>(null);
const animationRef = useRef<gsap.core.Tween | gsap.core.Timeline | null>(null);
const triggerEffect = useCallback(() => {
if (!turbulenceRef.current || !displacementRef.current) return;
if (animationRef.current) {
animationRef.current.kill();
}
const turbulence = turbulenceRef.current;
const displacement = displacementRef.current;
const scaledIntensity = intensity / 100;
switch (variant) {
case "glitch":
animationRef.current = gsap.timeline()
.to(turbulence, {
attr: { baseFrequency: 0.04 * scaledIntensity },
duration: duration / 1000 / 2,
ease: "power2.in",
})
.to(displacement, {
attr: { scale: 20 * scaledIntensity },
duration: duration / 1000 / 2,
ease: "power2.in",
}, 0)
.to(turbulence, {
attr: { baseFrequency: 0.000001 },
duration: duration / 1000 / 2,
ease: "power2.out",
})
.to(displacement, {
attr: { scale: 0 },
duration: duration / 1000 / 2,
ease: "power2.out",
}, duration / 1000 / 2);
break;
case "ripple":
animationRef.current = gsap.timeline()
.fromTo(displacement,
{ attr: { scale: 30 * scaledIntensity } },
{ attr: { scale: 0 }, duration: duration / 1000, ease: "power2.out" }
)
.fromTo(turbulence,
{ attr: { baseFrequency: 0.02 * scaledIntensity } },
{ attr: { baseFrequency: 0.000001 }, duration: duration / 1000, ease: "power2.out" },
0
);
break;
case "turbulence":
gsap.to(turbulence, {
attr: { baseFrequency: 0.03 * scaledIntensity },
duration: 0.1,
});
gsap.to(displacement, {
attr: { scale: 15 * scaledIntensity },
duration: 0.1,
});
break;
}
}, [variant, intensity, duration]);
const resetEffect = useCallback(() => {
if (!turbulenceRef.current || !displacementRef.current) return;
if (animationRef.current) {
animationRef.current.kill();
}
gsap.to(turbulenceRef.current, {
attr: { baseFrequency: 0.000001 },
duration: 0.15,
ease: "power2.out",
});
gsap.to(displacementRef.current, {
attr: { scale: 0 },
duration: 0.15,
ease: "power2.out",
});
}, []);
const handleMouseEnter = useCallback(() => {
if (trigger === "hover" || trigger === "both") {
triggerEffect();
}
}, [trigger, triggerEffect]);
const handleMouseLeave = useCallback(() => {
if (trigger === "hover" || trigger === "both") {
if (variant === "turbulence") {
resetEffect();
}
}
}, [trigger, variant, resetEffect]);
const handleClick = useCallback(() => {
if (trigger === "click" || trigger === "both") {
triggerEffect();
}
onClick?.();
}, [trigger, triggerEffect, onClick]);
useEffect(() => {
return () => {
if (animationRef.current) {
animationRef.current.kill();
}
};
}, []);
return (
<div className={"inline-block relative " + className}>
<svg className="absolute w-0 h-0" aria-hidden="true">
<defs>
<filter id={"filter-" + filterId}>
<feTurbulence
ref={turbulenceRef}
type="fractalNoise"
baseFrequency="0.000001"
numOctaves="1"
result="turbulence"
/>
<feDisplacementMap
ref={displacementRef}
in="SourceGraphic"
in2="turbulence"
scale="0"
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</defs>
</svg>
<button
ref={buttonRef}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onClick={handleClick}
className={"relative select-none transition-transform " + buttonClassName}
style={{
filter: "url(#filter-" + filterId + ")",
}}
>
{children}
</button>
</div>
);
}
export { GlitchButton };import GlitchButton from "@/components/GlitchButton";
// Glitch effect - digital distortion on hover
<GlitchButton
variant="glitch"
intensity={40}
duration={200}
buttonClassName="bg-gray-900 text-white px-6 py-3 rounded-lg font-medium"
>
Glitch Effect
</GlitchButton>
// Ripple effect - triggered on click
<GlitchButton
variant="ripple"
intensity={50}
duration={400}
trigger="click"
buttonClassName="bg-indigo-600 text-white px-6 py-3 rounded-lg font-medium"
>
Ripple Effect
</GlitchButton>
// Turbulence effect - continuous distortion while hovering
<GlitchButton
variant="turbulence"
intensity={35}
trigger="hover"
buttonClassName="bg-purple-600 text-white px-6 py-3 rounded-lg font-medium"
>
Turbulence Effect
</GlitchButton>
// Both hover and click triggers
<GlitchButton
variant="glitch"
intensity={50}
trigger="both"
buttonClassName="bg-rose-500 text-white px-6 py-3 rounded-lg font-medium"
onClick={() => console.log("Clicked!")}
>
Click or Hover
</GlitchButton>