skip to note
back to writing

wfd / Feb 28, 2026 / 9 min

exploring liquid glass for the web

apple showed off liquid glass at WWDC 2025 and it immediately split the internet. half the people loved it, the other half couldn't read their phone anymore. both sides had a point.

i'm less interested in whether it's a good design choice and more interested in how it works. the effect is non-trivial. it goes well beyond glassmorphism with backdrop-filter: blur() into a multi-layered rendering pipeline that bends light through a simulated glass surface in real time.

i want to recreate it on the web.

Stub i haven't finished writing this yet. i publish drafts early as part of WFD 17.

what liquid glass actually is

apple calls it a "digital meta-material." it dynamically bends and shapes light like physical glass while moving fluidly like liquid. the WWDC session breaks it into several layers:

layerwhat it does
highlightsrespond to environmental lighting and device motion. lights move through 3D space.
shadowsadjust opacity based on what's behind them. darker over text, lighter over solid backgrounds.
tintingcontinuously adapts. flips between light and dark based on the content underneath.
lensingthe core innovation. background elements are warped through refraction instead of blurred.
illuminationinteractive feedback that spreads from your fingertip through the element.

the key difference from glassmorphism is lensing. previous apple materials scattered light (blur). liquid glass bends it. when you look at a button, the content behind it is displaced and warped as if you're looking through a curved piece of real glass. the displacement follows the shape's geometry, the surface curvature, and snell's law of refraction.

in plain english imagine holding a magnifying glass over your phone screen. the content behind it warps and shifts as you move it around. liquid glass does that in software for every button, tab bar, and sidebar in the OS. the glass shape determines how the content warps, and the system adjusts the colors and lighting to make it look physically convincing.

how apple implements it natively

AlexStrNik's ShatteredGlass reverse-engineered the macOS implementation. the layer hierarchy is an exploded stack of compositing passes, each handling a different aspect of the effect:

about this visualization the visualization above is a conceptual decomposition of the rendering pipeline. in the actual native implementation, shadow, blur, refraction, and tint are all handled by a single glassBackground CAFilter on one CABackdropLayer in a single GPU pass. the visualization separates them into individual planes so you can see what each stage contributes to the final composite.

Three.js has no access to the browser's paint layer, so the shader layers above sample a static background image instead of the actual page content behind the element. the composited view approximates liquid glass visually, but the per-layer behavior when exploded is illustrative and simplified.

drag the slider to separate the layers. the native hierarchy from ShatteredGlass:

SwiftUI.SDFLayer (container)
├── CABackdropLayer ("@0")
│   ├── glassBackground CAFilter
│   │   (shadow, blur, refraction, face tint, bleed — all in one filter)
│   └── CASDFLayer ("@0") + CASDFElementLayer (defines the shape)
├── CASDFLayer ("@1") + CASDFGlassHighlightEffect (edge lighting, 45°)
│   └── vibrantColorMatrix filter
└── CASDFLayer ("@2") + CASDFGlassHighlightEffect (edge lighting, -135°)
    └── vibrantColorMatrix filter

the important bits:

  • SDF (Signed Distance Fields) define the shape geometry. SDFs replace meshes and paths with smooth distance-based calculations for normals, curvature, and surface detection.
  • a single glassBackground CAFilter on the CABackdropLayer handles refraction, blur, shadow, face tint, and bleed in one GPU pass. the visualization above splits these into separate layers for clarity.
  • two CASDFGlassHighlightEffect layers add directional edge highlights at opposite angles (45° and -135°), each with its own vibrantColorMatrix filter for tonal adjustment.

apple does this without ray tracing. it's closer to a screen-space refraction shader that uses the SDF normal to compute displacement. chromatic aberration (dispersion) emerges from sampling each color channel at slightly different refracted positions within the same shader. the GPU does all the heavy lifting.

the dday.it analysis confirms this: "it is the first time that a design language relies on some instructions of the graphic processor with shaders specifically developed to process a type of effect that is not at all trivial." they also note it responds to the accelerometer and gyroscope for light direction.

the math: snell's law and displacement

the core visual effect is refraction. when light passes from air into glass, it bends. the relationship is described by snell's law:

n1 * sin(theta1) = n2 * sin(theta2)

for a glass surface with refractive index ~1.5, light bends toward the normal when entering the glass. the amount of bending depends on the angle of incidence, which depends on the surface curvature at that point.

mws did an optically correct GLSL implementation from first principles. the approach:

  1. model the glass shape as a rounded rectangle with a curved bevel (half-sphere top + cylinder bottom)
  2. for each pixel, compute the distance to the glass edge using an SDF
  3. from the distance, compute the surface normal (the SDF gradient via dFdx/dFdy in GLSL)
  4. apply snell's law to compute the refraction vector
  5. the refracted ray hits the "floor" (the background content) at a displaced position
  6. sample the background at the displaced position instead of the original

the displacement is always inward for convex surfaces (which is what apple uses). this means the background content inside the glass shape appears slightly magnified at the center and displaced at the edges.

in plain english for each pixel on screen, the shader asks: "how far am i from the edge of this rounded rectangle?" if you're near the edge, the glass surface is curved there, so the shader looks up the background at a shifted position instead of straight through. the result is that content near the edges gets pushed inward while the center stays mostly in place. that's the "lensing" look.

kube.io explored four surface functions:

surfaceshapeeffect
convex circlespherical domesharp refraction edges, magnification in center
convex squircleapple's preferred shapesmoother transitions, optically thinner bezel
concavebowl-like depressionrays diverge outward (not used by apple)
lipconvex rim + concave centerused for switches and toggles

apple uses the squircle profile. it's smoother than a circular arc and keeps refraction gradients consistent even when stretched into rectangles.

web implementation approaches

this is where it gets interesting. there are several ways to approach liquid glass on the web, each with real tradeoffs.

approach 1: SVG displacement maps

kube.io built the most technically rigorous CSS/SVG implementation. the idea:

  1. pre-calculate the refraction displacement for a range of distances from the border
  2. encode the displacement vectors into an SVG image (red channel = X displacement, green = Y)
  3. use <feDisplacementMap> in an SVG filter to apply the refraction
  4. apply the filter via CSS backdrop-filter: url(#filter-id)
  5. add a specular highlight overlay for edge lighting

the catch is that backdrop-filter with SVG filters only works in Chrome. safari and firefox don't support this combination. the displacement map is also limited to 8-bit channels (256 values per axis), capping the precision, and any shape/size change requires rebuilding the entire displacement map.

approach 2: Three.js / WebGL

specy took a different route:

  1. use html2canvas to screenshot the page (the "paint layer")
  2. create a Three.js scene overlaid on the page with position: fixed
  3. place the screenshot as a texture behind a 3D glass object
  4. move the texture to match scroll position so it stays aligned with the real content
  5. the glass object refracts the texture using Three.js materials

they published a React library for this.

OverShifted built a standalone OpenGL shader that uses SDF-based refraction with blur, noise, and glow. the approach works but the resource usage is heavy.

approach 3: pure shader (shadertoy / raw WebGL)

mws's shadertoy implementation is the most physically accurate. it computes refraction from first principles using the SDF normal and GLSL's built-in refract() function. it adds reflection and shadow (neither physically correct, but visually convincing).

the problem mws notes: curvature discontinuity at the bevel-to-flat transition creates sharp visual artifacts. apple avoids this through careful surface function design. "apple has an extreme hate for curvature discontinuity."

the web's fundamental limitation

specy put it plainly: we don't have access to the paint layer. in native iOS, the GPU has direct access to the rendered content behind any element. on the web, CSS and JavaScript cannot read the pixels behind an element unless you hack around it with screenshots.

SVG filters are the exception. <feDisplacementMap> with SourceGraphic gets you access to the paint layer, but only within the constraints of the SVG filter pipeline. custom shaders and arbitrary per-pixel operations are off the table. you get displacement and blending and that's about it.

the html2canvas approach works as a workaround for demos, but falls short for production UI. it's expensive, async, and drifts out of sync with the real page.

the dday.it article goes further: "there is no Liquid Glass for the web, and it is not possible in any way to replicate the same effect using CSS."

in plain english on iOS, the system can peek at the pixels behind any button and warp them through a glass shader in real time. browsers don't let JavaScript do that. CSS can blur what's behind an element (backdrop-filter), but it can't bend or distort it. every web recreation has to work around this by taking a screenshot of the page and feeding it to a shader manually, which is slow and fragile.

arguments for doing this anyway

  • the effect is achievable in constrained contexts (fixed overlays, hero sections, interactive demos)
  • Three.js + React Three Fiber gives us full shader control for specific elements
  • a partial recreation that nails the core refraction is more interesting than a perfect backdrop-filter: blur()
  • WFD 17 now supports inline Three.js scenes, so interactive demonstrations are possible
  • it's a good exercise in understanding optics, SDFs, and GPU rendering

arguments against

  • the full liquid glass experience (adaptive tint, shadow response, motion feedback) requires paint layer access we don't have
  • WebGL overlays are expensive and don't compose well with regular DOM elements
  • the accessibility concerns are real. liquid glass has contrast issues even on native iOS where apple controls the rendering pipeline
  • browser support for the SVG filter approach is Chrome-only
  • the performance cost of a WebGL overlay for UI elements is significant compared to native GPU compositing

where i'm leaning

i want to build a focused recreation scoped to specific use cases. the plan:

  1. build interactive Three.js visualizations that demonstrate each concept (refraction, SDF normals, displacement maps, surface functions)
  2. create a standalone glass effect component using a custom shader instead of MeshTransmissionMaterial
  3. keep it scoped to specific elements (hero sections, interactive demos) and leave general UI alone

the interactive visualizations are the more interesting output. a ray diagram you can drag. a surface function you can tweak. a displacement map you can watch being generated. the kind of thing that makes the math tangible.

haven't built any of this yet. this WFD will be updated as the implementation progresses.

references