How are liquid glass and immersive lighting effects implemented on the web version?

Same title—how do I do this? I just can’t get it to work.

2 Likes

How are Liquid Glass and immersive lighting effects implemented on the web? @grokbot

Implementing “Liquid Glass + Immersive Lighting” on the Web essentially involves stacking three layers of technology:

  1. Frosted glass base —— backdrop-filter for blur and saturation enhancement
  2. Edge refraction —— SVG feDisplacementMap makes the background produce a “liquid” distortion around the glass edges
  3. Lighting layer —— Highlight outlines, a pointer-following radial light source, a dynamic ambient background + grain

The layers are broken down below, and you can copy the code directly.


1. Frosted glass base (the most basic layer)

.glass {
  position: relative;
  border-radius: 24px;

  /* Semi-transparent base color: the blurred area needs a little base color, otherwise the blur effect looks washed out */
  background: rgba(255, 255, 255, 0.12);

  /* Core effect: blur + saturation enhancement (saturate makes the colors behind the glass look more vivid) */
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);

  /* 1px inner border simulating the thickness of glass */
  border: 1px solid rgba(255, 255, 255, 0.22);

  /* Outer shadow + top highlight; without either one, it won't look like glass */
  box-shadow:
    0 8px 32px rgba(0, 0, 0, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

saturate(180%) is the key to the “translucent” feel of liquid glass—don’t just write blur().


2. Edge refraction — the soul of “liquid”

Pure blur is just frosted glass (glassmorphism). The defining feature of true Liquid Glass is that the edges distort the background like a lens. This can only be done with an SVG filter to create displacement, then fed into backdrop-filter:

<svg width="0" height="0" style="position:absolute">
  <filter id="liquid" x="-25%" y="-25%" width="150%" height="150%"
          color-interpolation-filters="sRGB">
    <!-- Noise field: determines the direction of the distortion -->
    <feTurbulence type="fractalNoise"
                  baseFrequency="0.008 0.012"
                  numOctaves="2" seed="7" result="noise"/>
    <!-- Smooth it slightly to avoid overly harsh distortion -->
    <feGaussianBlur in="noise" stdDeviation="3" result="smooth"/>
    <!-- Displacement: scale controls the distortion strength -->
    <feDisplacementMap in="SourceGraphic" in2="smooth"
                       scale="40"
                       xChannelSelector="R" yChannelSelector="G"/>
  </filter>
</svg>
.liquid {
  backdrop-filter: url(#liquid) blur(12px) saturate(180%);
  -webkit-backdrop-filter: blur(12px) saturate(180%); /* Safari fallback */
}

To make the distortion appear only around the edges (while keeping the center clear), use a radial gradient image as the input for the displacement map: gray in the center (no displacement), red/green at the edges (strong displacement). This produces true liquid glass with “lens-like edges and a clear center.”

:warning: To be honest about compatibility: url(#...) inside backdrop-filter currently works in Chrome / Edge / Firefox, but Safari basically doesn’t support it (-webkit-backdrop-filter only accepts a list of functions). So be sure to add an @supports branch. Safari users will see a pure frosted-glass version—a slightly reduced effect, but nothing will break.


3. Lighting layer — highlights, light sources, and grain

1. Pointer-following highlight (the main source of immersion)

.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;

  background: radial-gradient(
    240px circle at var(--mx, 50%) var(--my, 0%),
    rgba(255, 255, 255, 0.35),
    transparent 65%
  );
}
el.addEventListener('pointermove', e => {
  const r = el.getBoundingClientRect();
  el.style.setProperty('--mx', `${e.clientX - r.left}px`);
  el.style.setProperty('--my', `${e.clientY - r.top}px`);
});

2. Dynamic ambient background (without a good-looking background, glass will never look good)

Liquid glass is “environment-dependent”—the background must contain highly saturated color blocks with variations in light and dark to create a sense of material:

body {
  background:
    radial-gradient(600px at 20% 30%, #ff7a59, transparent 60%),
    radial-gradient(700px at 80% 20%, #4f7cff, transparent 60%),
    radial-gradient(600px at 50% 90%, #b44cff, transparent 60%),
    #0b0f1c;
  animation: drift 18s ease-in-out infinite alternate;
}
@keyframes drift {
  to { background-position: 60px -40px, -50px 30px, 20px 60px; }
}

3. Grain / noise (prevents banding in gradients and looks more like a real material)

.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  opacity: 0.05;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

4. Multilayered depth of field

Use different blur values for glass at different depths (blur(8px) in the foreground and blur(28px) in the background), along with different opacity levels, and the sense of space will appear immediately.


4. Performance and compatibility (especially important on mobile)

  • backdrop-filter uses per-frame GPU compositing. Once there are more than ~8–10 glass elements on screen, frame rates will start dropping; on list pages, it’s best to mount it only on visible items.
  • Don’t animate the blur() value (for example, transitioning from 20px to 30px on hover). Use transform/opacity instead, or switch classes directly.
  • Safari has a limit on the number of backdrop-filter layers; beyond that, they simply won’t render. Consider adding contain: paint; or isolation: isolate to constrain the compositing layers.
  • Provide a fallback:
@supports not (backdrop-filter: blur(1px)) {
  .glass { background: rgba(255,255,255,.75); }
}
  • Respect prefers-reduced-motion and disable the background drift animation.

5. If you want to save time

  • Ready-made libraries: Liquid Glass (npm liquid-glass) and glassmorphism generators can automatically generate SVG refraction filters and the corresponding CSS.
  • Reference implementations: the liquidglass project by sharplab and the descriptions of “thickness, refractive index, and highlight angle” in Apple’s official Liquid Glass design guidelines (related SF Symbols / WWDC25 materials) are well worth studying alongside your implementation.

In one sentence: backdrop-filter handles “translucency,” SVG feDisplacementMap handles “liquid,” and radial highlights + a dynamic gradient background + grain handle “light.” Once all three are in place, the effect is essentially solid. If you share the specific use case (navigation bar / card / full-screen panel), I can provide a more tailored implementation.

There’s “glass” on the L site at topic/2891295/5, but I won’t post the exact link.

1 Like

Tailwind + Bootstrap hybrid

1 Like
1 Like

Search here
hunt.now

Just bookmarking this.