major ui overhaul! - the physics behind my site's new ui
rebuilt the entire ui. every card, the navbar, the search, all of it runs a 4-layer glass material with physics-based refraction. not just blur. the whole system is available as an npm package — npm install @sohumsuthar/liquid-glass.
drag either lens around. the frosted one has light blur. the clear one shows the raw displacement - how real curved glass bends light at the edges.
backdrop-filter: blur(20px) is frosted glass. it scatters light uniformly. real glass bends it. the edges of a curved panel displace the content behind them inward, the flat interior doesn't. i wanted that.
the math
light bends when it enters a denser medium. snell's law:
$$n_1 \sin(\theta_1) = n_2 \sin(\theta_2)$$
air is $n_1 = 1.0$, glass is $n_2 = 1.5$. for thin glass viewed head-on, this produces a lateral displacement of the background at the edges.
the displacement depends on the surface slope. i model the glass cross-section as a height function from the outer edge ($x=0$) to the flat interior ($x=1$):
$$f(x) = \sqrt[4]{1 - (1-x)^4}$$
convex squircle. same curve apple uses. smoother flat-to-curve transition than a circular arc $f(x) = \sqrt{1 - (1-x)^2}$, no visible seam when stretched into rectangles.
the slope $f'(x)$ feeds into the displacement magnitude:
$$d(x) = \frac{f'(x)}{1 + f'(x)^2}$$
peaks at the edge, zero at the interior. 127 precomputed samples along one radius, matching the 8-bit channel limit of svg displacement maps.
the displacement map
for every pixel in a 512x512 image:
compute distance to the nearest border of a rounded rectangle (signed distance field)
if inside the bezel zone, look up displacement magnitude from the precomputed table
direction = SDF gradient $\nabla\text{SDF}(p)$, always perpendicular to the nearest edge
encode as RGB: $R = 128 + d_x \cdot 127$, $G = 128 + d_y \cdot 127$, 128 = neutral
the result is a png with colored bands around the bezel and gray everywhere else. inlined as base64 in an svg filter, then chained:
backdrop-filter: blur(28px) saturate(180%) url(#lg-refract);scale 0.06 = 6% of element width. ~42px displacement on a 700px panel. chrome only - safari/firefox fall back to blur without refraction.
the layers
four layers per glass surface:
effect - backdrop-filter (blur + saturation + displacement on macros)
tint - solid $\text{rgba}(28,28,32,0.72)$. glass needs a visible base color or it vanishes on black backgrounds
shine - four inset box-shadows. top highlight 0.18, bottom shadow 0.25. that $\approx 1:1.4$ ratio reads as convex
content - z-indexed above the material
plus ::before for static noise grain and ::after for cursor-tracking glow.
keeping it fast
20+ backdrop-filter elements over an animated particle canvas at 120fps:
content-visibility: autoskips off-screen glass entirelysvg displacement only on the 3-5 macro wrappers, not every card
blur capped at 28px (safari downsamples past 25)
particle canvas throttled to 30fps so backdrop-filter can cache between updates
touch devices skip all cursor/scroll/particle effects
fps guard auto-disables glass if a device sustains below 24fps for 4 seconds
there's a toggle in the bottom-left to turn glass off entirely.
code is in the repo. reusable system at sohumsuthar/liquid-glass, published on npm as @sohumsuthar/liquid-glass.