cd ../writing
// svg · 8 effects

SVG filters cookbook — what CSS can't do.

CSS filter gives you eight things: blur, brightness, contrast, hue-rotate, drop-shadow, grayscale, invert, sepia. Useful, but tiny. SVG filters give you everything else — liquid distortion, gooey blob effects, paper grain, ink bleed, chromatic aberration. Same filter: url(...) syntax. Way more power.

8 recipes SVG + CSS © copy freely

Every modern browser ships with the same SVG filter primitives: feTurbulence (noise), feDisplacementMap (warp by another image), feGaussianBlur (blur), feColorMatrix (channel math), feConvolveMatrix (sharpen / emboss / edge detect), and a dozen more. You define a filter once in an SVG, give it an ID, and apply with filter: url(#myFilter) from CSS.

Below are 8 production-ready recipes. Each is a <filter> definition + a CSS class that applies it. The SVG block at the top of the page (invisible, 0×0) holds all the filter defs.

01 / distortion

Liquid distortion

Warps text or shapes with smooth fractal noise. Used for organic, watery, "morphing" effects. The scale controls how much warp.

<!-- in your SVG defs -->
<filter id="liquid">
  <feTurbulence type="fractalNoise"
                baseFrequency="0.015"
                numOctaves="2"/>
  <feDisplacementMap in="SourceGraphic"
                     scale="14"/>
</filter>

/* apply in CSS */
.liquid { filter: url(#liquid); }
flowing
02 / blob

Gooey blob menu

The "metaball" effect — circular elements merge into single blobs when close. The classic floating-action-button radial menu. feGaussianBlur blurs, feColorMatrix sharpens the alpha threshold.

<filter id="gooey">
  <feGaussianBlur stdDeviation="6"/>
  <feColorMatrix values="
    1 0 0 0 0
    0 1 0 0 0
    0 0 1 0 0
    0 0 0 18 -7"/>
</filter>

.gooey-container {
  filter: url(#gooey);
}
.gooey-container .blob {
  width: 40px; height: 40px;
  border-radius: 50%;
  background: var(--brand);
}
The matrix values matter. The 18 increases alpha contrast (sharper edges) and -7 shifts the alpha threshold (controls where merge happens). Adjust both for tighter or looser blob fusion.
03 / texture

Paper grain overlay

The slight noisy texture that makes a flat background feel like printed paper or matte cardstock. feTurbulence at high frequency = fine grain.

<filter id="paper">
  <feTurbulence type="fractalNoise"
                baseFrequency="0.9"
                numOctaves="3"/>
  <feColorMatrix values="
    0 0 0 0 0
    0 0 0 0 0
    0 0 0 0 0
    0 0 0 0.6 0"/>
</filter>

.card::after {
  content: '';
  position: absolute;
  inset: 0;
  filter: url(#paper);
  background: rgba(255,255,255,0.6);
  mix-blend-mode: overlay;
}
04 / type

Ink bleed

For typography that should feel hand-printed. Slight displacement + mild blur = letterforms with imperfect edges, like newspaper ink.

<filter id="ink">
  <feTurbulence baseFrequency="0.04"
                numOctaves="2"/>
  <feDisplacementMap in="SourceGraphic"
                     scale="3"/>
  <feGaussianBlur stdDeviation="0.4"/>
</filter>

h1 { filter: url(#ink); }
EDITION
05 / line

Squiggle line

Convert any straight line into a hand-drawn wavy one. Useful for sketchy underlines, dividers, mock wireframes.

<filter id="squiggle">
  <feTurbulence baseFrequency="0.06"
                numOctaves="3"/>
  <feDisplacementMap in="SourceGraphic"
                     scale="6"/>
</filter>

hr { filter: url(#squiggle); }
06 / glow

Soft neon glow

Better than text-shadow for headlines and logos. Combines blur with the source image — gives that "lit from within" feel.

<filter id="glow">
  <feGaussianBlur stdDeviation="3" result="b"/>
  <feMerge>
    <feMergeNode in="b"/>
    <feMergeNode in="b"/>
    <feMergeNode in="SourceGraphic"/>
  </feMerge>
</filter>

.neon { filter: url(#glow); }
SIGNAL
07 / glitch

Chromatic aberration

Splits the red and blue channels by 2px in opposite directions — the "lens distortion" look. Hover state on retro-styled buttons or hero headlines.

<filter id="chroma">
  <feOffset dx="-2" result="r"/>
  <feColorMatrix in="r" values="
    1 0 0 0 0
    0 0 0 0 0
    0 0 0 0 0
    0 0 0 1 0" result="rO"/>
  <feOffset dx="2" result="b"/>
  <feColorMatrix in="b" values="
    0 0 0 0 0
    0 0 0 0 0
    0 0 1 0 0
    0 0 0 1 0" result="bO"/>
  <feMerge>
    <feMergeNode in="rO"/>
    <feMergeNode in="bO"/>
    <feMergeNode in="SourceGraphic"/>
  </feMerge>
</filter>
GLITCH
08 / depth

Embossed surface

The feConvolveMatrix kernel is a 3×3 grid that performs edge detection / sharpening / embossing depending on the values. This kernel creates a soft top-left light, bottom-right shadow.

<filter id="emboss">
  <feConvolveMatrix order="3"
    kernelMatrix="
      -2 -1  0
      -1  1  1
       0  1  2"/>
</filter>

.card { filter: url(#emboss); }
EMBOSSED CARD

Performance note: SVG filters are GPU-accelerated on modern browsers, but expensive on large surfaces. Apply them to small elements (icons, headings, buttons) — not full-page backgrounds. feTurbulence in particular is heavy; cache the result with filter-region if you reuse the same noise multiple times.

Browser support is universal. Every modern browser implements the SVG Filter spec. If a filter works on desktop Chrome, it works on mobile Safari, Firefox, Edge — no fallback engineering needed.