REAL HTML · EVERY BROWSER · NO DEPENDENCIES

Liquid
Glass

This text is real. Select it, Cmd+F it, click the buttons.

View on GitHub

Look
Explore

Drop an image to look through the glass

Glass that bends
real HTML.

Not a screenshot, not a canvas. The lens refracts the live page, so text stays selectable and buttons stay clickable. In Chrome, Firefox and Safari.

Lighting

Light that behaves like light.

The rim is lit as a curved surface. A specular highlight catches the light, and fresnel brightens the edge where the glass turns away from you.

Liquid Glass

Bevel

From a thin rim to a full dome.

Set how far the curve reaches in. Take it all the way to the middle and the lens becomes a magnifier. Move over it.

Button mode

Buttons that press back.

Hover and the glass brightens. Press and the bevel flattens as the shadow tightens. Springs, so it never snaps.

Tone

Brightness and saturation, in the glass.

Grade what's behind the lens without touching the page.

Presets

Five looks to start from.

Pick one, then tune it. Presets are plain settings on LiquidGlass.presets, so your site can use them too.

0

dependencies

1

file to copy

3

browser engines

UI components.

A switch, a tab bar and a button, built on the lens and tuned side by side with iOS 26, down to its springs. Hover, press and drag them.

Switch

A thumb that turns to glass.

Solid at rest. Hover, press or drag and the thumb dissolves into a lens that bends the track around it, then sets back down once it stops. Plain, or with a label each side.

Light

Wi-Fi
Bluetooth
Billing

Dark

Wi-Fi
Bluetooth
Billing

Tab bar

Press, then slide between tabs.

A lens lifts off the current tab and follows your finger, the bar swelling as you press. Let go and it lands on the nearest tab, hangs a moment, and sets down.

Light

Dark

Button

Clear at rest, lit when you reach for it.

A clear lens over whatever is behind it. Hover and it grows and fills with its tint; press and it swells a little more, with a glow under your finger. With an icon, or with words.

Light

Dark

How it works.

Four stages, all standard web platform: a copy of the page, a map of how far each pixel moves, an SVG filter that moves them, and light painted on top.

  1. 1CopyClone the page under the lens
  2. 2MapWhere each pixel should move
  3. 3FilterMove them, split the colours
  4. 4LightRim, specular and fresnel

Step 1 · Copy

A live copy, lined up underneath.

An SVG filter can't see what's behind an element. backdrop-filter: url() could, but only Chrome supports it. So the lens keeps its own copy of the page, made with cloneNode(true) and positioned so every pixel lines up with the original.

A MutationObserver, plus input and scroll listeners, marks the copy stale, and it's rebuilt on the next frame. Cloning drops live state, so input values, checkboxes, canvas pixels and scroll offsets are carried across by hand.

  • inert
  • aria-hidden
  • IDs removed
  • pointer-events: none
PageCopyLens
The lens shows the copy, never the page itself. Both move together, so the copy always matches what's underneath.

Step 2 · Map

A displacement map from a distance field.

The lens is a signed distance field for a rounded rectangle: negative inside, zero on the edge. Across the bevel the glass is a quarter circle, so the bend grows as 1 − √(1 − t²), where t runs from 0 at the bevel's inner edge to 1 at the rim. The direction is the field's gradient, which points straight out.

Each offset is written to a canvas, x in red and y in green, centred on 128. The curve stops just short of vertical, since a vertical edge would bend without limit.

bevel startsrimfull refraction
The real map behind the lens up top. Hue is the direction each pixel moves, brightness how far. Tune the lens and it redraws.

Step 3 · Filter

Three passes for dispersion.

Real glass bends each wavelength a little differently. The filter runs the displacement three times at slightly different strengths, keeps red from the strongest, green from the middle and blue from the weakest, then adds them back together.

Brightness and saturation are one colour matrix at the end. Frost is a CSS blur() around the filter rather than feGaussianBlur inside it, which keeps it on the GPU in Firefox.

  1. Copy of the page SourceGraphic
  2. feDisplacementMap × (1 + c) feDisplacementMap × 1 feDisplacementMap × (1 − c)
  3. feColorMatrix keep R feColorMatrix keep G feColorMatrix keep B
  4. feComposite arithmetic: R + G + B
  5. feColorMatrix saturation, brightness
  6. CSS blur(frost) · scale(zoom)
With dispersion at zero it's a single pass. c is the dispersion setting.

Step 4 · Light

Light, painted once.

Lighting is a canvas over the glass. Each rim pixel gets a 3D normal that tilts outward as the bevel rounds over. A Blinn-Phong half vector between a top-left light and your eye gives the specular spot, and fresnel brightens the edge where the surface turns away.

It's drawn at device resolution when the shape changes, never per frame. Moving the lens is only a transform.

Specular
(n · h)48
Fresnel
(1 − nz)3
The lens's lighting layer on its own: hairline, rim light, specular spot and fresnel edge.

Only work when something changes

The frame loop checks three flags: the copy is stale, the shape changed, or the lens moved. On an idle frame it does nothing.

Springs, not tweens

Position is a damped spring, so new input just retargets it mid-flight. Speed stretches the lens along its path, and reduced motion snaps instead.

Zoom without steps

Map channels are 8-bit, so zoom stored there moved in half-pixel steps. Instead the filtered layer is built at 1/zoom size and scaled up.

Whole pixels

Browsers rasterise filters on the CSS pixel grid. At a fractional position the refraction gets resampled and shimmers, so the lens snaps to whole pixels.

Safari, handled

Safari drops a filter over GPU layers and mis-scales transformed content inside one. The copy loses will-change and animations, and moves with left and top.

Invisible to assistive tech

Screen readers, keyboard focus and find-in-page only ever meet the real page. The copy is inert and hidden, and its IDs are stripped so they stay unique.

How it compares.

Liquid glass on the web, side by side. Each takes a different path to the same look, and each path has its trade-offs.

Feature comparison of liquid glass libraries for the web
Feature This project Liquid Glass samasante liquid-glass rdev liquid-glass-react ybouane liquidglass naughtyduk liquidGL shuding liquid-glass
How it works SVG displacement on a live copy of the page SVG displacement on the live page, or a copy SVG displacement on a blurred backdrop WebGL shader on a captured image of the page WebGL / WebGPU shader on a page snapshot SVG filter through backdrop-filter
Bends in Safari and Firefox
Yes.
Partly.In copy modes; the default wrapper bends in Chrome only
No.Blur only outside Chrome
Yes.
Yes.Large lenses can be unstable in Safari
No.Chrome only
Refracts live HTML
Yes.Text stays selectable, buttons clickable
Yes.
Partly.In Chrome
No.Re-captured when content changes
No.Snapshot
Partly.In Chrome
Video and canvas behind the glass
No.Not copied
Yes.Through a WebGL path
Partly.In Chrome
Yes.Re-captured every frame
Yes.Re-captured
Partly.In Chrome
Chromatic dispersion
Yes.
Yes.
Yes.
Yes.
Yes.
No.
Frost
Yes.
Yes.
Yes.
Yes.
Yes.
Partly.Fixed, very light
Lighting
Yes.Rim, specular and fresnel
Yes.Sheen, glow and specular
Partly.Edge highlights
Yes.Rim, specular and fresnel
Yes.Animated specular
No.
Presets
Yes.Five
No.
Partly.Four refraction modes
Partly.Example looks in the demo
Yes.Five
No.
Motion
Yes.Spring follow, squash and stretch, button press
Yes.Pointer follow, squash and stretch
Yes.Elastic stretch toward the cursor
Partly.Draggable panels
Partly.Draggable, tilt
Partly.Draggable
Framework
Yes.None
Partly.React
Partly.React
Yes.None
Yes.None
Yes.None

From each project's README and source in October 2026. Libraries change, so check the links for the latest. Our copy doesn't show hover or focus states, video or WebGL canvases.

Use it anywhere.

One small package for plain JavaScript, React or a single script tag. No dependencies. Tune the lens and the code below follows along.

  • Vanilla JS
  • React 18+
  • TypeScript types
  • SSR safe
  • ~10 kB gzipped

npm install @dazecoop/liquid-glass

Components

Switches, tabs and buttons.

Import the stylesheet once. In React each one is a component, controlled or not. In plain HTML, write the markup and call initComponents(). Add data-theme="light" to any ancestor for the light look.

JavaScript

The lens.

new LiquidGlass(source, options)
Glass that refracts source, a live element. Floats over the page by default.
{ container }
Lives inside an element instead, clipped to it and scrolling with it.
{ anchor, button }
Fills an element as its background. button makes it brighten on hover and flatten on press.
.moveTo(x, y)
Moves to a point in viewport coordinates, on a spring.
.press(down)
Swells a little while true.
.show(visible, { pop })
Fades in or out, or pops like a bubble.
.set(params)
Changes any parameter on the fly.
.destroy()
Removes it and everything it added.
LiquidGlass.presets
clear, frosted, dark, prism, dome.

React

The bindings.

<GlassScene glass>
A lens that follows the pointer over its children.
<GlassPanel source button>
An element with glass as its background, refracting its parent or source.
<GlassSwitch checked onChange>
The switch. Controlled, or uncontrolled with defaultChecked.
<GlassSegment options value>
Two labelled options.
<GlassTabBar items value>
Tabs as { value, label, icon, href }.
<GlassButton tint>
Clear glass that lights up in tint.
useLiquidGlass(ref, options)
The instance itself, for anything custom.

Every one cleans up on unmount and is safe in StrictMode and on the server.

Parameters

Everything the sliders set.

width, height
280, 180
Size in CSS pixels. Anchored glass takes its anchor's.
radius
64
Corner radius in CSS pixels.
bevel
35
How far the curved rim reaches in. Half the smaller side is a full dome.
refraction
40
How far the rim bends the content, in pixels.
frost
2
Blur radius in pixels.
chroma
0.4
Colour split at the rim, 0 to 1.
zoom
1.08
Magnification across the lens.
specular
0.35
The highlight, 0 to 1.
fresnel
0.3
Edge glow where the glass turns away, 0 to 1.
brightness
0
What's seen through it, −0.5 to 0.5.
saturation
0
What's seen through it, −1 (grey) to 1.
tint
""
A CSS colour laid over the glass.
rim
1
Rim lighting and hairline, 0 to 1.

GitHub npm Full docs

Find it useful?

It's free and MIT licensed. If it's saved you some time and you'd like to support the work, you can buy me a coffee. Completely optional, but always appreciated!

Buy Me a Coffee