{"_id":"@daviduo/liquid-glass-web","name":"@daviduo/liquid-glass-web","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@daviduo/liquid-glass-web","version":"0.1.0","publishConfig":{"access":"public"},"description":"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.","type":"module","license":"MIT","author":{"name":"0Daviduo0"},"repository":{"type":"git","url":"git+https://github.com/0Daviduo0/liquidGlass-web.git"},"homepage":"https://github.com/0Daviduo0/liquidGlass-web#readme","bugs":{"url":"https://github.com/0Daviduo0/liquidGlass-web/issues"},"keywords":["liquid-glass","glassmorphism","refraction","backdrop-filter","feDisplacementMap","vue","vue3","composable","directive","shader","telegram","frosted-glass","ui"],"main":"./dist/liquid-glass-web.umd.cjs","module":"./dist/liquid-glass-web.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/liquid-glass-web.js","require":"./dist/liquid-glass-web.umd.cjs"}},"sideEffects":false,"scripts":{"dev":"vite","build":"vue-tsc --noEmit && vite build","preview":"vite preview","prepublishOnly":"npm run build"},"peerDependencies":{"vue":"^3.3.0"},"devDependencies":{"@vitejs/plugin-vue":"^5.0.0","typescript":"^5.4.0","vite":"^5.2.0","vite-plugin-dts":"^3.9.0","vue":"^3.4.0","vue-tsc":"^2.0.0"},"gitHead":"0ddc9067085f2e994a82d177cf952eb1c1a16acd","_id":"@daviduo/liquid-glass-web@0.1.0","_nodeVersion":"26.0.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-b/PwpUb5MLtxLnhpLEPuY2OQhJJ3jVu7NRvEeq2zGOBsl4w0koFoI6vvchDwXEcnnxlicIMO4i8ocMLRlMAeAQ==","shasum":"772c902aae42d459ad56716c540482fb4bccd4cc","tarball":"https://registry.npmjs.org/@daviduo/liquid-glass-web/-/liquid-glass-web-0.1.0.tgz","fileCount":9,"unpackedSize":34672,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCQcN5oaay7V3B7/uV4lXCxLd01u+oxOOPQwNXiCK5//QIhAPSeAJG+IZoFwVwU4aN7bqTnQGsvsO1tcVV/jFtpS58S"}]},"_npmUser":{"name":"daviduo","email":"davide0400@gmail.com"},"directories":{},"maintainers":[{"name":"daviduo","email":"davide0400@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/liquid-glass-web_0.1.0_1784887486113_0.5178357344849565"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-24T10:04:45.889Z","0.1.0":"2026-07-24T10:04:46.263Z","modified":"2026-07-24T10:04:46.496Z"},"maintainers":[{"name":"daviduo","email":"davide0400@gmail.com"}],"description":"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.","homepage":"https://github.com/0Daviduo0/liquidGlass-web#readme","keywords":["liquid-glass","glassmorphism","refraction","backdrop-filter","feDisplacementMap","vue","vue3","composable","directive","shader","telegram","frosted-glass","ui"],"repository":{"type":"git","url":"git+https://github.com/0Daviduo0/liquidGlass-web.git"},"author":{"name":"0Daviduo0"},"bugs":{"url":"https://github.com/0Daviduo0/liquidGlass-web/issues"},"license":"MIT","readme":"# liquid-glass-web\n\n> Real **liquid glass** (optical refraction, not just blur) for the web — a **Vue 3**\n> composable + directive. Port of the technique used by the **Telegram Android** client\n> (`org.telegram.ui.Components.blur3`).\n\nApply a physically-motivated glass refraction to buttons, cards, panels — anything —\nand it bends whatever is *behind* the element like a real lens with thickness.\n\n![status](https://img.shields.io/badge/vue-3-42b883) ![license](https://img.shields.io/badge/license-MIT-blue)\n\n---\n\n## TL;DR\n\n```bash\nnpm i @daviduo/liquid-glass-web\n```\n\n```vue\n<script setup lang=\"ts\">\nimport { ref } from \"vue\";\nimport { useLiquidGlass } from \"@daviduo/liquid-glass-web\";\n\nconst el = ref<HTMLElement>();\nuseLiquidGlass(el, { radius: 20, thickness: 14, refractIntensity: 1.2, blur: 2 });\n</script>\n\n<template>\n  <!-- needs something BEHIND it; the tint is plain CSS on top -->\n  <button ref=\"el\" style=\"background: rgba(255,255,255,.12); border-radius: 20px\">\n    Glass\n  </button>\n</template>\n```\n\n---\n\n## What it actually is (and isn't)\n\nCommon misconception: \"glass = blurred background\". That's **frosted glass** and it's only\nhalf of it. Telegram's real effect is **refraction**: the panel behaves like a lens —\nflat in the center (no distortion), curved at the edges (the background gets pushed /\nbent), which is what gives the *liquid*, three-dimensional look.\n\n`backdrop-filter: blur()` alone can't do that. This library does.\n\n---\n\n## How it works\n\nOn Android, Telegram runs an AGSL fragment shader over the captured background: for every\npixel inside a rounded-rect it builds a bulged glass surface from a **Signed Distance\nFunction (SDF)**, computes the surface normal, applies **Snell's law** (`refract()`), and\nsamples the background at the shifted coordinate.\n\nThe web can't sample arbitrary backdrop pixels inside a generic fragment shader. So this\nlibrary splits the work:\n\n```\n  generateGlassMap()                  buildSvgFilter()                     CSS\n ┌────────────────────┐   href    ┌───────────────────────────┐   url()  ┌───────────────┐\n │ same math as the    │ ───────► │ <feImage> displacement map │ ───────► │ backdrop-     │\n │ AGSL shader:        │          │ <feDisplacementMap         │          │ filter:url(#) │\n │ SDF + Snell refract │          │    in=backdrop in2=map>    │          │ → refraction  │\n │ → per-pixel offset  │          │ [+ feGaussianBlur]         │          │   of backdrop │\n │   packed in R/G     │          │ [+ feColorMatrix saturate] │          │               │\n └────────────────────┘          └───────────────────────────┘          └───────────────┘\n```\n\n1. **`generateGlassMap`** re-implements the shader's `main()` in TypeScript. For each pixel\n   it computes the exact refraction offset `(dx, dy)` and encodes it into a PNG\n   **displacement map** — offset X → red channel, offset Y → green (neutral 128 = no shift).\n2. **`buildSvgFilter`** wraps that map in an SVG `<filter>`: `feImage` loads the map,\n   `feDisplacementMap` uses it to shift the backdrop (`in=\"SourceGraphic\"` = the backdrop\n   when the filter is used as `backdrop-filter`). Optional `feGaussianBlur` (frosted) and\n   `feColorMatrix` (saturation, like Telegram's `saturationX3`).\n3. The composable sets `backdrop-filter: url(#thatFilter)` on your element. The browser does\n   the sampling + displacement natively on the GPU.\n\nOptical result = identical to the Telegram shader; the map is precomputed once per\nsize/parameter set instead of per frame.\n\n### Faithful bits ported 1:1\n\n- `sdfRect()` rounded-rect SDF, including the per-quadrant corner-radius selection and the\n  Android uniform order `radius = (br, tr, bl, tl)`.\n- Normal build (`n_cos`/`n_sin` thickness profile), `refract(I, N, eta)`, refracted path\n  length `(h + 8*thickness) / -refract_vec.z`, and `refractIntensity` multiplier.\n- **Radius clamp** to `min(w,h)/2` (so pills/circles keep refracting — an unclamped radius\n  degenerates the SDF and kills the effect).\n\n---\n\n## Install\n\n```bash\nnpm i @daviduo/liquid-glass-web\n# or straight from GitHub:\nnpm i github:0Daviduo0/liquidGlass-web\n```\n\nRequires Vue 3 (`^3.3.0`) as a peer dependency.\n\n---\n\n## Usage\n\n### Composable — `useLiquidGlass`\n\n```ts\nimport { useLiquidGlass } from \"@daviduo/liquid-glass-web\";\n\nconst handle = useLiquidGlass(elRef, {\n  radius: 20,          // px, or {tl,tr,br,bl}; default: reads computed border-radius\n  thickness: 12,       // virtual glass thickness (px); higher = wider edge refraction\n  refractIndex: 1.5,   // ~1.5 = glass\n  refractIntensity: 1, // 0 = flat, 1 = full\n  blur: 0,             // gaussian blur on the backdrop (px), 0 = crisp\n  saturate: 1,         // backdrop saturation (Telegram uses ~3)\n  pixelRatio: 1,       // map density; devicePixelRatio for sharper edges\n  autoResize: true,    // regenerate map on resize (ResizeObserver)\n  enabled: true,       // toggle at runtime\n});\n\nhandle.update();   // force rebuild\nhandle.destroy();  // remove filter + styles\n```\n\nOptions can be a reactive object / ref / getter — change them and the effect updates\n(deep-watched). The target can be a ref, getter or element.\n\n### Directive — `v-liquid-glass`\n\n```ts\nimport { createApp } from \"vue\";\nimport { vLiquidGlass } from \"@daviduo/liquid-glass-web\";\napp.directive(\"liquid-glass\", vLiquidGlass);\n```\n\n```vue\n<div v-liquid-glass=\"{ radius: 22, thickness: 14, blur: 3 }\">card</div>\n```\n\n### Framework-agnostic core\n\nNo Vue required — use the primitives directly:\n\n```ts\nimport { generateGlassMap, buildSvgFilter } from \"@daviduo/liquid-glass-web\";\n\nconst map = generateGlassMap({ width: 320, height: 120, radius: 24, thickness: 14, refractIndex: 1.5, refractIntensity: 1 });\nconst svg = buildSvgFilter(\"my-glass\", map, { blur: 2, saturate: 2 });\ndocument.body.appendChild(svg);\nel.style.backdropFilter = \"url(#my-glass)\";\n```\n\n---\n\n## Options reference\n\n| Option             | Type                              | Default | Meaning                                            |\n| ------------------ | --------------------------------- | ------- | -------------------------------------------------- |\n| `radius`           | `number \\| {tl,tr,br,bl}`         | auto    | Corner radii (px). Auto reads `border-radius`.     |\n| `thickness`        | `number`                          | 12      | Virtual thickness → edge refraction width.         |\n| `refractIndex`     | `number`                          | 1.5     | Refractive index (>1). 1.5 = glass.                |\n| `refractIntensity` | `number`                          | 1       | Distortion multiplier. 0 disables.                 |\n| `blur`             | `number`                          | 0       | Gaussian blur on the backdrop (frosted).           |\n| `saturate`         | `number`                          | 1       | Backdrop saturation (vivid Telegram look).         |\n| `pixelRatio`       | `number`                          | 1       | Map density. Use `devicePixelRatio` for sharpness. |\n| `autoResize`       | `boolean`                         | true    | Rebuild map on element resize.                     |\n| `enabled`          | `boolean`                         | true    | Enable/disable at runtime.                         |\n\n---\n\n## Important rules\n\n1. **There must be something behind the element** (image, text, gradient). Refraction acts\n   on the backdrop; on a flat solid background you'll see nothing.\n2. **The tint is CSS**, not part of the filter — set `background: rgba(...)` on the element.\n   It sits over the refracted backdrop (src-over compositing, same as the shader).\n3. The map depends on size; `autoResize` regenerates it. Moving the element (drag/scroll)\n   costs nothing — the browser recomputes the backdrop automatically.\n4. Keep `radius` sensible; it's internally clamped to `min(w,h)/2`.\n\n---\n\n## Browser support\n\n`backdrop-filter: url(#svgFilter)` is the gating feature:\n\n- ✅ **Chromium** (Chrome / Edge / Brave / Arc): full support.\n- ⚠️ **Firefox**: SVG filters in `backdrop-filter` work, rendering less consistent.\n- ❌ **Safari**: does **not** support `url()` in `backdrop-filter`. The composable\n  feature-detects and skips applying the filter, so the element keeps its own CSS\n  background/blur (graceful frosted fallback — no refraction).\n\nDifference vs Android: there the shader runs per-frame on a RenderNode; here the map is\nprecomputed (static for a given size + params) and the browser's compositor does the\nrefraction — same optics, one-time generation cost.\n\n---\n\n## Playground\n\n```bash\ngit clone https://github.com/0Daviduo0/liquidGlass-web.git\ncd liquidGlass-web\nnpm install\nnpm run dev   # http://localhost:5174\n```\n\nThe playground (`playground/`) has a draggable glass shape over photos, shape presets\n(Card / Pill / Circle / Square / Bar / Button) and live sliders for every parameter.\n\n---\n\n## Build\n\n```bash\nnpm run build   # → dist/ (ESM + UMD + bundled .d.ts)\n```\n\n---\n\n## Project layout\n\n```\nsrc/\n  liquidGlass.ts    core, framework-agnostic (generateGlassMap, buildSvgFilter)\n  useLiquidGlass.ts Vue 3 composable\n  vLiquidGlass.ts   Vue directive\n  index.ts          public exports\nplayground/         demo app (Vite)\n```\n\n---\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n\nTechnique derived (independently re-implemented, not copied) from DrKLO/Telegram's\n`blur3` liquid glass (GPLv2). Attribution kept in the LICENSE file.\n","readmeFilename":"README.md","_rev":"1-c58c1c7fe03593a57cc571432dca2774"}