npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@daviduo/liquid-glass-web

v0.1.0

Published

Liquid glass (real refraction, not blur) for the web — a Vue 3 composable/directive port of Telegram Android's blur3 shader, driven by an SVG feDisplacementMap on backdrop-filter.

Readme

liquid-glass-web

Real liquid glass (optical refraction, not just blur) for the web — a Vue 3 composable + directive. Port of the technique used by the Telegram Android client (org.telegram.ui.Components.blur3).

Apply a physically-motivated glass refraction to buttons, cards, panels — anything — and it bends whatever is behind the element like a real lens with thickness.

status license


TL;DR

npm i @daviduo/liquid-glass-web
<script setup lang="ts">
import { ref } from "vue";
import { useLiquidGlass } from "@daviduo/liquid-glass-web";

const el = ref<HTMLElement>();
useLiquidGlass(el, { radius: 20, thickness: 14, refractIntensity: 1.2, blur: 2 });
</script>

<template>
  <!-- needs something BEHIND it; the tint is plain CSS on top -->
  <button ref="el" style="background: rgba(255,255,255,.12); border-radius: 20px">
    Glass
  </button>
</template>

What it actually is (and isn't)

Common misconception: "glass = blurred background". That's frosted glass and it's only half of it. Telegram's real effect is refraction: the panel behaves like a lens — flat in the center (no distortion), curved at the edges (the background gets pushed / bent), which is what gives the liquid, three-dimensional look.

backdrop-filter: blur() alone can't do that. This library does.


How it works

On Android, Telegram runs an AGSL fragment shader over the captured background: for every pixel inside a rounded-rect it builds a bulged glass surface from a Signed Distance Function (SDF), computes the surface normal, applies Snell's law (refract()), and samples the background at the shifted coordinate.

The web can't sample arbitrary backdrop pixels inside a generic fragment shader. So this library splits the work:

  generateGlassMap()                  buildSvgFilter()                     CSS
 ┌────────────────────┐   href    ┌───────────────────────────┐   url()  ┌───────────────┐
 │ same math as the    │ ───────► │ <feImage> displacement map │ ───────► │ backdrop-     │
 │ AGSL shader:        │          │ <feDisplacementMap         │          │ filter:url(#) │
 │ SDF + Snell refract │          │    in=backdrop in2=map>    │          │ → refraction  │
 │ → per-pixel offset  │          │ [+ feGaussianBlur]         │          │   of backdrop │
 │   packed in R/G     │          │ [+ feColorMatrix saturate] │          │               │
 └────────────────────┘          └───────────────────────────┘          └───────────────┘
  1. generateGlassMap re-implements the shader's main() in TypeScript. For each pixel it computes the exact refraction offset (dx, dy) and encodes it into a PNG displacement map — offset X → red channel, offset Y → green (neutral 128 = no shift).
  2. buildSvgFilter wraps that map in an SVG <filter>: feImage loads the map, feDisplacementMap uses it to shift the backdrop (in="SourceGraphic" = the backdrop when the filter is used as backdrop-filter). Optional feGaussianBlur (frosted) and feColorMatrix (saturation, like Telegram's saturationX3).
  3. The composable sets backdrop-filter: url(#thatFilter) on your element. The browser does the sampling + displacement natively on the GPU.

Optical result = identical to the Telegram shader; the map is precomputed once per size/parameter set instead of per frame.

Faithful bits ported 1:1

  • sdfRect() rounded-rect SDF, including the per-quadrant corner-radius selection and the Android uniform order radius = (br, tr, bl, tl).
  • Normal build (n_cos/n_sin thickness profile), refract(I, N, eta), refracted path length (h + 8*thickness) / -refract_vec.z, and refractIntensity multiplier.
  • Radius clamp to min(w,h)/2 (so pills/circles keep refracting — an unclamped radius degenerates the SDF and kills the effect).

Install

npm i @daviduo/liquid-glass-web
# or straight from GitHub:
npm i github:0Daviduo0/liquidGlass-web

Requires Vue 3 (^3.3.0) as a peer dependency.


Usage

Composable — useLiquidGlass

import { useLiquidGlass } from "@daviduo/liquid-glass-web";

const handle = useLiquidGlass(elRef, {
  radius: 20,          // px, or {tl,tr,br,bl}; default: reads computed border-radius
  thickness: 12,       // virtual glass thickness (px); higher = wider edge refraction
  refractIndex: 1.5,   // ~1.5 = glass
  refractIntensity: 1, // 0 = flat, 1 = full
  blur: 0,             // gaussian blur on the backdrop (px), 0 = crisp
  saturate: 1,         // backdrop saturation (Telegram uses ~3)
  pixelRatio: 1,       // map density; devicePixelRatio for sharper edges
  autoResize: true,    // regenerate map on resize (ResizeObserver)
  enabled: true,       // toggle at runtime
});

handle.update();   // force rebuild
handle.destroy();  // remove filter + styles

Options can be a reactive object / ref / getter — change them and the effect updates (deep-watched). The target can be a ref, getter or element.

Directive — v-liquid-glass

import { createApp } from "vue";
import { vLiquidGlass } from "@daviduo/liquid-glass-web";
app.directive("liquid-glass", vLiquidGlass);
<div v-liquid-glass="{ radius: 22, thickness: 14, blur: 3 }">card</div>

Framework-agnostic core

No Vue required — use the primitives directly:

import { generateGlassMap, buildSvgFilter } from "@daviduo/liquid-glass-web";

const map = generateGlassMap({ width: 320, height: 120, radius: 24, thickness: 14, refractIndex: 1.5, refractIntensity: 1 });
const svg = buildSvgFilter("my-glass", map, { blur: 2, saturate: 2 });
document.body.appendChild(svg);
el.style.backdropFilter = "url(#my-glass)";

Options reference

| Option | Type | Default | Meaning | | ------------------ | --------------------------------- | ------- | -------------------------------------------------- | | radius | number \| {tl,tr,br,bl} | auto | Corner radii (px). Auto reads border-radius. | | thickness | number | 12 | Virtual thickness → edge refraction width. | | refractIndex | number | 1.5 | Refractive index (>1). 1.5 = glass. | | refractIntensity | number | 1 | Distortion multiplier. 0 disables. | | blur | number | 0 | Gaussian blur on the backdrop (frosted). | | saturate | number | 1 | Backdrop saturation (vivid Telegram look). | | pixelRatio | number | 1 | Map density. Use devicePixelRatio for sharpness. | | autoResize | boolean | true | Rebuild map on element resize. | | enabled | boolean | true | Enable/disable at runtime. |


Important rules

  1. There must be something behind the element (image, text, gradient). Refraction acts on the backdrop; on a flat solid background you'll see nothing.
  2. The tint is CSS, not part of the filter — set background: rgba(...) on the element. It sits over the refracted backdrop (src-over compositing, same as the shader).
  3. The map depends on size; autoResize regenerates it. Moving the element (drag/scroll) costs nothing — the browser recomputes the backdrop automatically.
  4. Keep radius sensible; it's internally clamped to min(w,h)/2.

Browser support

backdrop-filter: url(#svgFilter) is the gating feature:

  • ✅ Chromium (Chrome / Edge / Brave / Arc): full support.
  • ⚠️ Firefox: SVG filters in backdrop-filter work, rendering less consistent.
  • ❌ Safari: does not support url() in backdrop-filter. The composable feature-detects and skips applying the filter, so the element keeps its own CSS background/blur (graceful frosted fallback — no refraction).

Difference vs Android: there the shader runs per-frame on a RenderNode; here the map is precomputed (static for a given size + params) and the browser's compositor does the refraction — same optics, one-time generation cost.


Playground

git clone https://github.com/0Daviduo0/liquidGlass-web.git
cd liquidGlass-web
npm install
npm run dev   # http://localhost:5174

The playground (playground/) has a draggable glass shape over photos, shape presets (Card / Pill / Circle / Square / Bar / Button) and live sliders for every parameter.


Build

npm run build   # → dist/ (ESM + UMD + bundled .d.ts)

Project layout

src/
  liquidGlass.ts    core, framework-agnostic (generateGlassMap, buildSvgFilter)
  useLiquidGlass.ts Vue 3 composable
  vLiquidGlass.ts   Vue directive
  index.ts          public exports
playground/         demo app (Vite)

License

MIT — see LICENSE.

Technique derived (independently re-implemented, not copied) from DrKLO/Telegram's blur3 liquid glass (GPLv2). Attribution kept in the LICENSE file.