SVG Liquid Distortion
Organic distortion using SVG feTurbulence and feDisplacementMap filters. No WebGL — works everywhere, animates with SMIL.
Preview
Loading preview…
Customize
Scale30
Frequency0.015
Animate
Radius16
"use client";
import { useId } from "react";
import styles from "./SvgLiquidDistortion.module.css";
type SvgLiquidDistortionProps = {
scale?: number;
frequency?: number;
animate?: boolean;
radius?: number;
className?: string;
children: React.ReactNode;
};
export default function SvgLiquidDistortion({
scale = 30,
frequency = 0.015,
animate = true,
radius = 16,
className,
children,
}: SvgLiquidDistortionProps) {
const id = useId().replace(/:/g, "");
const filterId = `liquid-${id}`;
const freqStr = `${frequency} ${frequency}`;
const freqAnim = `${frequency} ${frequency};${frequency * 1.4} ${frequency * 1.2};${frequency} ${frequency}`;
return (
<div className={[styles.wrapper, className].filter(Boolean).join(" ")} style={{ borderRadius: radius, overflow: "hidden" }}>
<svg className={styles.svg} aria-hidden="true">
<defs>
<filter id={filterId} x="-10%" y="-10%" width="120%" height="120%">
<feTurbulence
type="fractalNoise"
baseFrequency={freqStr}
numOctaves={3}
result="noise"
>
{animate && (
<animate
attributeName="baseFrequency"
values={freqAnim}
dur="6s"
repeatCount="indefinite"
/>
)}
</feTurbulence>
<feDisplacementMap
in="SourceGraphic"
in2="noise"
scale={scale}
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</defs>
</svg>
<div
className={styles.content}
style={{ filter: `url(#${filterId})` }}
>
{children}
</div>
</div>
);
}