The refresh of Apple's design language in iOS 26 (Liquid Glass) leans on one effect that CSS has been quietly capable of for a few years now: the content behind a translucent surface should appear lensed, not just blurred. Real glass refracts; CSS's backdrop-filter: blur(…) doesn't.
You can get refraction in pure CSS — through an SVG filter that the backdrop-filter rule then references.
The recipe
Three SVG filter primitives, chained:
<filter id="liquid-glass">
<feTurbulence type="fractalNoise"
baseFrequency="0.008 0.008"
numOctaves="2"
seed="7"
result="noise" />
<feGaussianBlur in="noise"
stdDeviation="2"
result="smoothNoise" />
<feDisplacementMap in="SourceGraphic"
in2="smoothNoise"
scale="9"
xChannelSelector="R"
yChannelSelector="G" />
</filter>
In CSS:
.liquid-glass {
backdrop-filter: url(#liquid-glass) blur(14px) saturate(180%) brightness(1.06);
}
What each line does:
feTurbulencegenerates noise.fractalNoisegives a smooth multi-scale field — think clouds, not static. A lowbaseFrequencymakes the field large-scale and slow; a fixedseedmakes it deterministic (no shimmer between page loads). Two octaves layer one detail scale on top of one bigger scale.feGaussianBlursmooths the noise. Without this you get visible noise grain. With σ ≈ 2 the field reads as a glass surface, not a noisy texture.feDisplacementMapis the refraction. For each pixel it reads(noise.R, noise.G), treats those channel values as(x, y)offsets, multiplies byscale, and looks up the source pixel at the offset position. The chained CSSbackdrop-filtermeans the SourceGraphic is the page content behind the glass — that's what gets warped.
Why scale = 9
A back-of-envelope optics derivation gets you to roughly the right value:
For a thin slab of glass with refractive index n and thickness t, a light ray hitting at angle θ exits offset laterally by approximately
deflection ≈ t · (1 − 1/n) · sin(θ)
Plug in numbers for what we're modelling visually:
- t ≈ 14 px — our backdrop blur σ. The blur defines the perceptual "thickness" of the glass layer.
- n = 1.52 — sodium-lime / BK7 crown glass. Window glass, in other words.
- A typical off-axis viewing angle θ ≈ 30° gives sin(θ) = 0.5.
That works out to:
14 · (1 − 1/1.52) · 0.5
= 14 · 0.342 · 0.5
≈ 2.4 px
So "physically honest" displacement for our glass thickness lands around 2–3 px. Apple's renders amplify this — the effect is supposed to be perceptible, not subliminal. I doubled-plus the honest value and landed at scale = 9, which reads as glass without looking like a fairground hall of mirrors. Strong variant at 16 for emphasis cards; soft variant at 5 for small UI.
You can drive scale from a CSS custom property and label it as a refractive index — but the SVG filter attribute can't reference CSS variables directly, so the indirection is documentation rather than computation. Still worth doing; it makes the magic number trace back to the physics.
What backdrop-filter actually buys you
The trick that makes this practical is that backdrop-filter runs SVG filters against what's behind the element, not against the element itself. So the glass surface stays crisp; the content underneath bends. That's the right semantic — a window distorts the world you see through it, not the window frame.
Browser support landed in Safari 17 (Sep 2023), Chromium 125 (mid-2024), Firefox is still partial as of late 2025. I gate the SVG-filter path behind @supports (backdrop-filter: url(#x)) so older browsers fall back to plain blur + saturate + brightness, which still reads as glass.
What this isn't
It isn't physically accurate. Real glass refracts based on the local surface normal — the curvature of the lens, the angle of incidence. We're displacing by a noise field that has no relationship to either. The result looks like glass because the human visual system is loose about what "glass" means: anything that lensed slightly + blurred + slightly brighter on top + with a soft edge highlight will read as a translucent surface.
It also isn't free. backdrop-filter with a chained SVG filter is GPU-expensive. On a high-frequency element — a tooltip, a sticky toolbar that re-paints often — you'll see frame drops on lower-end hardware. The fallback path (no SVG filter, just blur + saturate + brightness) is materially cheaper and looks 90% as good. Profile before you deploy on a tooltip.
Where this site uses it
Three intensities, applied where they make sense:
- Default on every
.glass-surface(cards, timeline panels, nav-stripe, statement cards). - Strong on the hero parallax card stack — biggest, most decorative, can afford the GPU cost.
- Soft on the floating navigation — present on every page, so the refraction stays subtle and text stays legible.
The whole filter setup is one component mounted once in the root layout: a hidden SVG with the <defs> block. Every page references the filter IDs through CSS — backdrop-filter: url(#liquid-glass-refract). The static export carries the SVG into every prerendered HTML page, so the filter resolves whether you arrive on / or deep-link to /computer-science/projects/sim-game-theory.
If you're curious about the rest of the pattern — the multi-shadow inset rim, the specular ::before pseudo, the mask-composite: exclude directional edge-light — the implementation notes file PHASE-4.1-LIQUID-GLASS-NOTES.md in the repo has the full breakdown.