
Hyalite is a Vanilla JavaScript library that creates real refractive Liquid Glass effects on rounded HTML elements.
It bends the backdrop near the edges while the center stays clear, with controls for bevel width, glass thickness, blur, chromatic dispersion, and rim lighting.
The effect uses SVG filters through backdrop-filter and does not require WebGL.
Full refraction currently runs in Chromium browsers, while Safari and Firefox keep the CSS fallback.
Features
- Per-corner circular radius handling with CSS overlap rules.
- Automatic map rebuilding after element size changes.
- Dynamic attachment and cleanup through container watchers.
- Multiple watchers on one page.
- Optional refraction ramps when elements appear.
- Settled or throttled live rebuilding during resize.
- Shared lens maps for near-identical element sizes.
- Motion ramps that respect
prefers-reduced-motion.
How To Use It
Basic Usage
Load hyalite.js in the page:
<script src="hyalite.js"></script>
Hyalite writes --hyalite on each attached element. Use the property only on that element because CSS custom properties inherit, and a descendant that reads it would receive a filter built for its parent.
.glass-card {
backdrop-filter: var(--hyalite, blur(6px));
-webkit-backdrop-filter: var(--hyalite, blur(6px));
background: rgba(20, 24, 32, 0.14);
border-radius: 24px;
padding: 24px;
}
<div class="glass-card" id="profile-card"> <h3>Profile</h3> <p>Content inside the refractive glass panel.</p> </div>
Attach Hyalite after the element enters the document:
const card = document.getElementById('profile-card');
Hyalite.attach(card, {
bevel: 20,
thickness: 10,
blur: 3
});
Watch Dynamic Glass Elements
Hyalite.watch() manages matching elements inside a container. It attaches existing matches, new matches, and elements that later gain the matching class.
const glassWatcher = Hyalite.watch(
document.getElementById('app'),
'.glass-card',
{
bevel: 16,
thickness: 10,
blur: 3
}
);
Stop that watcher when automatic management is no longer needed:
glassWatcher.stop();
Update All Attached Elements
Use Hyalite.setOpts() to change options across attached elements and update watcher settings for future matches.
await Hyalite.setOpts({
blur: 1,
dispersion: 0.08,
rim: 0.6
});
Refresh After a Border Radius Change
ResizeObserver reacts to element dimension changes. A border-radius change that leaves the element dimensions unchanged requires Hyalite.refresh().
card.style.borderRadius = '40px'; Hyalite.refresh(card);
Materialize the Refraction
Set materialize to a duration in milliseconds to ramp displacement and rim lighting from zero when an element is attached.
Hyalite.attach(card, {
bevel: 24,
thickness: 12,
blur: 2,
materialize: 220
});
Self Filtering
Set self: true when the SVG filter should process the element itself rather than its backdrop.
.filtered-element {
filter: var(--hyalite, none);
}
Hyalite.attach(
document.querySelector('.filtered-element'),
{
self: true,
bevel: 16,
thickness: 10
}
);
Configuration Options
bevel(number, default16, range1to400): Width of the refracted edge zone in pixels. Element geometry and the largest corner radius can reduce the effective value.thickness(number, default10, range0to400): Simulated glass thickness in pixels. Larger values increase backdrop displacement near the edge.blur(number, default3, range0to64): Center frost blur in pixels.dispersion(number, default0.05, range0to0.5): Chromatic aberration intensity.0uses one outer displacement pass.rim(number, default0.45, range0to4): Edge-light intensity.0disables the rim light.light(number, default-145, range-180to180): Rim-light direction in degrees.0points from above, and positive values rotate clockwise.smooth(number, default1, range0to4): Blur between the two displacement passes in pixels. It applies only inside the bevel ring.0uses one displacement pass.materialize(number, default0, range0to10000): Attachment ramp duration in milliseconds for displacement and rim lighting.settle(number, default120, range0to10000): Required size stability in milliseconds before rebuilding a lens map.0switches to throttled live rebuilding.self(boolean, defaultfalse): Filters the element itself throughfilter: var(--hyalite)and uses displacement only.onBuild(info)(function): Runs after a new lens map is built. Recreating a filter from a cached map does not call it.
API Methods
| API | Description |
|---|---|
Hyalite.watch(container, selector, opts) | Attaches matching elements now and as the container changes. Returns an object with stop(), which stops the watcher and detaches its managed elements. |
Hyalite.unwatch() | Stops every watcher and detaches watcher-managed elements. Manual attachments survive. |
Hyalite.attach(el, opts) | Manually attaches Hyalite to one element. |
Hyalite.detach(el) | Detaches an element and removes its --hyalite property. |
Hyalite.refresh(el) | Rebuilds the filter against the element’s current geometry. |
Hyalite.setOpts(opts) | Changes options across attached elements and returns a Promise. A newer call supersedes an unfinished earlier call. |
Hyalite.info() | Returns information from the most recent lens-map build, or null before any map has been built. |
Hyalite.supported() | Returns the current SVG backdrop-filter compatibility verdict. |
Hyalite.force(true | false | null) | Overrides browser detection. null returns to automatic detection and the method returns the resulting verdict. |
Hyalite.DEFAULTS | Contains the default option values. |
Hyalite.version | Contains the current library version string. |
`Hyalite.info()` Return Value
| Field | Type | Description |
|---|---|---|
maxDisplacement | number | Maximum displacement represented by the outer lens map. |
bevel | number | Effective bevel width used for the map. |
mapSize | [number, number] | Pixel dimensions of the generated map after size bucketing and downsampling. |
radii | [number, number, number, number] | Element corner radii in pixels after CSS overlap handling. |
map | string | Outer displacement map as a PNG data URL. |
mapInner | string | Inner displacement map used by the two-pass smoothing process. |
split | number | Fraction of the displacement field assigned to the outer pass. |
Corner Radius Behavior
Hyalite calculates circular corner radii individually and follows the CSS overlap rule when adjacent radii exceed the available edge length.
Elliptical radii such as:
border-radius: 40px / 16px;
Browser Compatibility
| Browser Engine | Result |
|---|---|
| Chromium browsers such as Chrome, Edge, Brave, Arc, and Electron | Full refraction. |
| Safari / WebKit | CSS fallback. |
| Firefox / Gecko | CSS fallback. |
Define the fallback directly in the element styles:
.glass {
backdrop-filter: var(--hyalite, blur(6px));
-webkit-backdrop-filter: var(--hyalite, blur(6px));
}
Override Hyalite’s automatic browser verdict at the document level when needed:
<html data-hyalite="force">
Disable it with:
<html data-hyalite="off">
JavaScript can perform the equivalent override through Hyalite.force().
Alternatives & Related Resources
- liquid-glass.js: iOS Liquid Glass Web Component with SVG & WebGL
- Create Realistic iOS Liquid Glass Effects with SVG Filters and JS (Web Component)
- WebGL Liquid Glass Effect for HTML Elements – LiquidGlass.js
- Create Advanced Liquid Glass & Noise Effects with FxFilterJS







