SVG Liquid Text Wave — feTurbulence Distortion Effect
SVG Liquid Text Wave · Animations · Plain HTML & CSS · Live preview
Copy & paste
Source Code
<div class="scene">
<svg viewBox="0 0 800 220" class="liquid-svg" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="liquidFilter" x="-20%" y="-20%" width="140%" height="140%">
<feTurbulence id="turb" type="fractalNoise" baseFrequency="0.012 0.04" numOctaves="2" seed="7" result="noise">
<animate attributeName="baseFrequency" dur="9s" values="0.012 0.04;0.02 0.06;0.012 0.04" repeatCount="indefinite" />
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="26" xChannelSelector="R" yChannelSelector="G" />
</filter>
<linearGradient id="liquidGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#22d3ee" />
<stop offset="50%" stop-color="#6366f1" />
<stop offset="100%" stop-color="#a855f7" />
</linearGradient>
</defs>
<text x="50%" y="55%" text-anchor="middle" dominant-baseline="middle" class="liquid-text" filter="url(#liquidFilter)">FLUID</text>
</svg>
<p class="hint">Distortion powered by an animated SVG feTurbulence + feDisplacementMap filter</p>
</div>* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; background: #050914; display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 20px; }
.scene { text-align: center; max-width: 820px; width: 100%; }
.liquid-svg { width: 100%; height: auto; }
.liquid-text {
font-size: 130px;
font-weight: 900;
letter-spacing: 6px;
fill: url(#liquidGradient);
}
.hint { font-size: 12px; color: #475569; margin-top: 6px; }Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
SVG Liquid Text Wave — feTurbulence + feDisplacementMap CSS-Free Distortion

A liquid text wave warps the outline of a headline continuously, as if it were printed on a rippling water surface, using only SVG filter primitives — no canvas, no WebGL, no JavaScript animation loop. The whole effect is declarative: an animated <feTurbulence> noise field displaces an <feDisplacementMap>-driven copy of the text, and the SVG's own native <animate> element handles the timing. It sits in different territory from a CSS-only wave text letter bounce, because the distortion here warps the actual glyph outlines rather than moving whole letters up and down — closer to a heat-haze or underwater refraction than a bounce.
feTurbulence generates the noise field
<feTurbulence type="fractalNoise" baseFrequency="0.012 0.04" numOctaves="2" seed="7" result="noise"> procedurally generates a Perlin-like noise texture directly in the browser's rendering pipeline — no image asset needed. baseFrequency controls how fine or coarse the noise pattern is (two values set separate X and Y frequencies, which is what gives the ripple its horizontally-stretched, water-like character rather than uniform blobs). numOctaves="2" layers two frequencies of noise together for more organic detail than a single octave produces, and seed="7" fixes the pattern so it is reproducible rather than different on every page load.
feDisplacementMap uses that noise to warp the text
<feDisplacementMap in="SourceGraphic" in2="noise" scale="26" xChannelSelector="R" yChannelSelector="G"> takes the actual text (SourceGraphic) and displaces each pixel horizontally and vertically based on the red and green channel values of the noise texture at that same pixel location. scale="26" sets how many pixels of maximum displacement the noise can cause — larger values produce a more extreme, melting distortion; smaller values produce a subtle shimmer.
Animating the filter natively with SVG <animate>
Rather than driving the distortion from JavaScript, a native <animate attributeName="baseFrequency" dur="9s" values="0.012 0.04;0.02 0.06;0.012 0.04" repeatCount="indefinite"> element nested inside <feTurbulence> interpolates the baseFrequency attribute through a sequence of values and back again, looping indefinitely. This makes the noise pattern itself slowly shift character over a 9-second cycle, so the ripple never looks like it is repeating a fixed loop even though the SVG markup itself has no JavaScript animation code at all — the browser's own SMIL animation engine handles everything.
Gradient fill via a linearGradient reference
The text's fill is set to url(#liquidGradient), referencing an SVG <linearGradient> defined in <defs> with three color stops — the same visual effect as CSS background-clip: text gradients (see animated gradient text), but expressed natively as an SVG paint server since the text itself is an SVG <text> element rather than HTML.
Step by step
How to Use
- 1Watch the distortion loopThe text continuously ripples as the native SVG <animate> element cycles feTurbulence's baseFrequency through a 9-second loop — no JavaScript is running.
- 2Change the headline textEdit the text content inside the SVG <text> element in the HTML panel.
- 3Adjust the distortion strengthIn the HTML panel, change scale="26" on <feDisplacementMap> — higher values produce a more melted, extreme warp.
- 4Change the ripple textureAdjust baseFrequency on <feTurbulence> (and inside its <animate> values) — smaller numbers create broader, slower-feeling waves; larger numbers create tighter, busier ripples.
- 5Change the gradient colorsEdit the <stop> color-color values inside <linearGradient id="liquidGradient"> in the HTML panel.
- 6Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
feTurbulence is a procedural SVG filter primitive that implements Perlin-style noise generation directly in the browser's rendering engine, computed on the fly from its baseFrequency, numOctaves, and seed attributes — no bitmap or texture asset is downloaded or referenced.
feTurbulence accepts separate X and Y frequency values. A lower X frequency with a higher Y frequency (as used here) stretches the noise pattern horizontally, producing ripples that look like they are flowing sideways across the text rather than uniform blobby noise in every direction.
It reads the noise texture generated by feTurbulence as a heightmap: for every pixel of the source text, it looks up the corresponding red and green channel values in the noise (set via xChannelSelector and yChannelSelector) and shifts that pixel horizontally and vertically by an amount proportional to those values and the scale attribute.
SVG supports native declarative animation (SMIL) via elements like <animate>, which can interpolate filter attributes such as baseFrequency directly without any script. This keeps the effect running smoothly even under main-thread JavaScript load and removes the need for a requestAnimationFrame loop entirely.
scale on feDisplacementMap sets the maximum pixel displacement the noise can cause. A small scale (a few pixels) produces a subtle shimmer; a large scale (30+) can distort the text enough that individual letters become hard to read, so it is worth tuning per headline length and font size.
Yes. Click "JSX" for a React component. SVG filter elements including feTurbulence, feDisplacementMap, and animate can be written directly as JSX (camelCased where React requires it, such as baseFrequency staying as-is since it is already valid) inside a returned <svg> tree with no additional JavaScript needed to drive the animation.