{"_id":"@codedgar/glassworks","name":"@codedgar/glassworks","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@codedgar/glassworks","version":"1.0.0","description":"Liquid glass for the web — turns any positioned element into a refracted, glossy WebGL glass pane. Rebuilt on snapdom for ~4× faster captures.","main":"scripts/liquidGL.js","unpkg":"scripts/liquidGL.js","jsdelivr":"scripts/liquidGL.js","keywords":["glassmorphism","liquid-glass","webgl","refraction","glass","snapdom","ui"],"homepage":"https://github.com/codedgar/glassworks#readme","repository":{"type":"git","url":"git+https://github.com/codedgar/glassworks.git"},"bugs":{"url":"https://github.com/codedgar/glassworks/issues"},"author":{"name":"codedgar"},"license":"MIT","publishConfig":{"access":"public"},"_id":"@codedgar/glassworks@1.0.0","gitHead":"9e0521d0a0cdefda2c73098171c6a7e9900e31ee","_nodeVersion":"20.9.0","_npmVersion":"10.1.0","dist":{"integrity":"sha512-ZVETvJqSmqVfmjoLrVo6VQGL+WIqzg2l8sB3xiIctBIMl3VXvbiH20zAub1JVVAaEUu8fWSSCisiaEod6HAMGQ==","shasum":"4a1bc11490472944c93a829f5969a4687be06adf","tarball":"https://registry.npmjs.org/@codedgar/glassworks/-/glassworks-1.0.0.tgz","fileCount":5,"unpackedSize":391207,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDSnu/EQoB7/65fTf5a4d8gOgViixyF5nGZK2OTWNIPSAIhAMIiUdkXkm5KtPwbWF58XZ6VS2yVZUP/w6w+N/XVpp0v"}]},"_npmUser":{"name":"codedgar","email":"codedgar.dev@gmail.com"},"directories":{},"maintainers":[{"name":"codedgar","email":"codedgar.dev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/glassworks_1.0.0_1779224607014_0.2920092910258576"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-19T21:03:26.918Z","1.0.0":"2026-05-19T21:03:27.195Z","modified":"2026-05-19T21:03:27.414Z"},"maintainers":[{"name":"codedgar","email":"codedgar.dev@gmail.com"}],"description":"Liquid glass for the web — turns any positioned element into a refracted, glossy WebGL glass pane. Rebuilt on snapdom for ~4× faster captures.","homepage":"https://github.com/codedgar/glassworks#readme","keywords":["glassmorphism","liquid-glass","webgl","refraction","glass","snapdom","ui"],"repository":{"type":"git","url":"git+https://github.com/codedgar/glassworks.git"},"author":{"name":"codedgar"},"bugs":{"url":"https://github.com/codedgar/glassworks/issues"},"license":"MIT","readme":"# Glassworks\n\n<a href=\"https://github.com/codedgar/glassworks\"><img src=\"/assets/glassworks-promo.gif\" alt=\"Glassworks\" style=\"width: 100%\"/></a>\n\n**Liquid glass for the web.** Glassworks turns any positioned element into a refracted, glossy glass pane rendered in WebGL — fast, lightweight, and rebuilt on [snapdom](https://github.com/zumerlab/snapdom) for ~4× faster captures than the original pipeline.\n\n<a href=\"/demos/demo-1.html\"><strong>DEMO 1</strong></a> | <a href=\"/demos/demo-2.html\"><strong>DEMO 2</strong></a> | <a href=\"/demos/demo-3.html\"><strong>DEMO 3</strong></a> | <a href=\"/demos/demo-4.html\"><strong>DEMO 4</strong></a> | <a href=\"/demos/demo-5.html\"><strong>DEMO 5</strong></a>\n\n> The library exposes one global, `liquidGL`, so existing code keeps working. Same call, same options. The project, brand, and ongoing maintenance are Glassworks.\n\n---\n\n## Why Glassworks\n\n|                          |                                                                                                                                                                                                                                                       |\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **~4× faster captures**  | snapdom's `<foreignObject>` pipeline does in ~80ms what html2canvas does in ~300ms on a typical page. Captures stop being the bottleneck.                                                                                                             |\n| **~50KB by default**     | html2canvas only loads if your page actually needs it (when an absolute overlay is detected on the snapshot target). Most pages stay at ~50KB; the legacy stack would have shipped ~250KB up front.                                                   |\n| **No drift on long pages** | Patched the upstream bug where in-flow lenses far down the page rendered against a collapsed snapshot. Lenses now stay perfectly aligned, however many you stack.                                                                                  |\n| **Broader browser story**| Tested and tuned on Chrome, Safari, Firefox, and Edge across desktop, tablet, and mobile. Includes a frosted CSS fallback for environments where WebGL isn't available.                                                                               |\n| **Drop-in API**          | Same `liquidGL({ ... })` call, same options. If you want the html2canvas-only pipeline, pass `engine: \"html2canvas\"` or load `liquidGL-legacy.js`. The drift fix is applied either way.                                                              |\n\n---\n\n## Features\n\n| Feature                                | Supported | Feature                  | Supported |\n| :------------------------------------- | :-------: | :----------------------- | :-------: |\n| Real-time refraction (static content)  |    ✅     | Magnification control    |    ✅     |\n| Real-time refraction (video)           |    ✅     | Dynamic element support  |    ✅     |\n| Real-time refraction (text animations) |    ✅     | GSAP-ready animations    |    ✅     |\n| Real-time refraction (CSS animations)  |    ❌     | Lightweight & performant |    ✅     |\n| Adjustable bevel                       |    ✅     | Seamless scroll sync     |    ✅     |\n| Frosted glass effect                   |    ✅     | Auto-resize handling     |    ✅     |\n| Dynamic shadows                        |    ✅     | Auto video refraction    |    ✅     |\n| Specular highlights                    |    ✅     | Animate lenses           |    ✅     |\n| Interactive tilt effect                |    ✅     | `on.init` callback       |    ✅     |\n\n---\n\n## Install\n\nGlassworks ships two builds. Most projects want the default.\n\n### Default — `liquidGL.js`\n\nsnapdom is the capture backend. html2canvas is lazy-loaded only when the page actually needs it.\n\n```html\n<!-- snapdom: required by default -->\n<script src=\"https://cdn.jsdelivr.net/npm/@zumer/snapdom/dist/snapdom.js\" defer></script>\n\n<!-- Glassworks -->\n<script src=\"/scripts/liquidGL.js\" defer></script>\n```\n\nIf the snapshot target contains a `position: absolute` descendant whose containing block is the page viewport (a common pattern in hero overlays and reveal sections), Glassworks lazy-loads `html2canvas` from cdnjs and composites that element on top of the snapdom base canvas. Pages without that pattern never trigger the fetch.\n\nTo self-host or pin a version, set the URL on `window` before the library loads:\n\n```html\n<script>\n  window.LIQUIDGL_HTML2CANVAS_URL = \"/vendor/html2canvas.min.js\";\n</script>\n```\n\nIf `window.html2canvas` is already defined when Glassworks needs it, the lazy-load is skipped.\n\nYou can also force the html2canvas pipeline at runtime without swapping files:\n\n```js\nliquidGL({\n  target: \".liquidGL\",\n  engine: \"html2canvas\", // default is \"snapdom\"\n});\n```\n\n### Legacy — `liquidGL-legacy.js`\n\nThe pure html2canvas pipeline (with the drift fix). Use this if you'd rather pre-load html2canvas and skip the snapdom code path entirely.\n\n```html\n<!-- html2canvas: required for the legacy build -->\n<script\n  src=\"https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js\"\n  defer\n></script>\n\n<!-- Glassworks: legacy build -->\n<script src=\"/scripts/liquidGL-legacy.js\" defer></script>\n```\n\nBoth builds expose the same global and accept the same options.\n\n---\n\n## Quick start\n\nSet up your HTML. You'll have a `target` element that becomes the glass pane and a child element for content (content sits on top, isn't refracted into the lens).\n\n```html\n<body>\n  <!-- Target (glassified) -->\n  <div class=\"liquidGL\">\n    <!-- Content -->\n    <div class=\"content\">\n      <img src=\"/example.svg\" alt=\"Alt Text\" />\n      <p>This text content will appear on top of the glass.</p>\n    </div>\n  </div>\n</body>\n```\n\n> Give the `target` a high `z-index` so it sits over your page content. Anything with a higher `z-index` than the target is excluded from the lens (e.g. a modal video player you don't want staining the glass).\n\nThen initialise:\n\n```html\n<script>\n  document.addEventListener(\"DOMContentLoaded\", () => {\n    const glass = liquidGL({\n      snapshot: \"body\",     // area used for refraction; <body> is the default\n      target: \".liquidGL\",  // CSS selector for the element(s) to glassify\n      resolution: 2.0,      // snapshot quality\n      refraction: 0.01,     // base refraction strength (0–1)\n      bevelDepth: 0.08,     // edge bevel intensity (0–1)\n      bevelWidth: 0.15,     // bevel width as a proportion of the element (0–1)\n      frost: 0,             // subtle blur radius in px (0 = crystal clear)\n      shadow: true,         // soft drop-shadow under the pane\n      specular: true,       // animated light highlights (slightly more GPU)\n      reveal: \"fade\",       // reveal animation\n      tilt: false,          // tilt on hover\n      tiltFactor: 5,        // if tilt is enabled, how much\n      magnify: 1,           // magnification of lens content\n      on: {\n        init(instance) {\n          // Fires once Glassworks has taken its snapshot and rendered the\n          // first frame. Right place to chain reveal animations — content\n          // is captured before you hide it from the user.\n          console.log(\"Glassworks ready!\", instance);\n        },\n      },\n    });\n  });\n</script>\n```\n\n---\n\n## Dynamic content\n\nGlassworks refracts dynamic content like animations in real time. To make this work, register any dynamic elements that will intersect with your glass pane. That tells the renderer to monitor them and update the texture when they change.\n\n> Videos are auto-detected and don't need to be registered.\n\nRegister after initialising `liquidGL()` and before calling `liquidGL.syncWith()` (if you use it). You can register by CSS selector or by passing an array of DOM elements.\n\n```js\nconst glass = liquidGL({ target: \".liquidGL\" /* ... */ });\n\n// By CSS selector\nliquidGL.registerDynamic(\".my-animated-element\");\n\n// Multiple elements (e.g. from GSAP SplitText)\nconst split = SplitText.create(\".my-text\", { type: \"lines\" });\nliquidGL.registerDynamic(split.lines);\n```\n\n> **snapdom note:** dynamic elements that aren't currently painted in the viewport (e.g. lines waiting for a ScrollTrigger far below the fold) are captured via an `IntersectionObserver`-driven recapture path so they populate as they enter view. First paint inside the lens may lag by one capture frame on a fast scroll. The legacy build doesn't have this constraint.\n\n---\n\n## Sync with smooth scrolling (optional)\n\n`syncWith()` auto-detects [Lenis](https://github.com/darkroomengineering/lenis) or [Locomotive Scroll](https://github.com/locomotivemtl/locomotive-scroll) and handles render-loop synchronisation for you.\n\n```html\n<script>\n  document.addEventListener(\"DOMContentLoaded\", () => {\n    const glass = liquidGL({ target: \".liquidGL\" /* ... */ });\n\n    const { lenis, locomotiveScroll } = liquidGL.syncWith();\n  });\n</script>\n```\n\n> Load the scroll library scripts (Lenis, GSAP, etc.) before your main script. `syncWith()` must be called after `liquidGL()`.\n\n---\n\n## Options\n\n| Option       | Type     | Default       | Description                                                                                                                                                          |\n| ------------ | -------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `target`     | string   | `'.liquidGL'` | **Required.** CSS selector for the element(s) to glassify.                                                                                                           |\n| `snapshot`   | string   | `'body'`      | CSS selector for the element to snapshot.                                                                                                                            |\n| `resolution` | number   | `2.0`         | Resolution of the background snapshot (clamped 0.1–3.0). Higher is sharper but uses more memory.                                                                     |\n| `refraction` | number   | `0.01`        | Base refraction offset applied across the pane (0–1).                                                                                                                |\n| `bevelDepth` | number   | `0.08`        | Additional refraction on the edge to simulate depth (0–1).                                                                                                           |\n| `bevelWidth` | number   | `0.15`        | Width of the bevel zone as a fraction of the shortest side (0–1).                                                                                                    |\n| `frost`      | number   | `0`           | Blur radius in pixels for a frosted look. `0` is clear.                                                                                                              |\n| `shadow`     | boolean  | `true`        | Toggles a subtle drop-shadow under the pane.                                                                                                                         |\n| `specular`   | boolean  | `true`        | Enables animated specular highlights that move with time.                                                                                                            |\n| `reveal`     | string   | `'fade'`      | Reveal animation. `'none'`: renders immediately. `'fade'`: smoothly fades in.                                                                                        |\n| `tilt`       | boolean  | `false`       | Enables 3D tilt interaction on cursor movement.                                                                                                                      |\n| `tiltFactor` | number   | `5`           | Depth of the tilt in degrees (0–25 recommended).                                                                                                                     |\n| `magnify`    | number   | `1`           | Magnification factor of the lens (clamped 0.001–3.0). `1` is no magnification.                                                                                       |\n| `engine`     | string   | `'snapdom'`   | Capture backend. `'snapdom'` (default) uses snapdom + lazy html2canvas hybrid. `'html2canvas'` forces the html2canvas-only path. Locked at the first `liquidGL()` call. |\n| `on.init`    | function | `-`           | Callback that runs once the first render completes. Receives the lens instance.                                                                                      |\n\n> `target` is required; everything else is optional.\n\n---\n\n## Presets\n\nReady-made configurations you can copy-paste. Tweak to taste.\n\n| Name        | Settings                                                                                               | Purpose                                          |\n| ----------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------------ |\n| **Default** | `{ refraction: 0, bevelDepth: 0.052, bevelWidth: 0.211, frost: 2, shadow: true, specular: true }`      | Balanced default used in the demo.               |\n| **Alien**   | `{ refraction: 0.073, bevelDepth: 0.2, bevelWidth: 0.156, frost: 2, shadow: true, specular: false }`   | Strong refraction & deep bevel for sci-fi looks. |\n| **Pulse**   | `{ refraction: 0.03, bevelDepth: 0, bevelWidth: 0.273, frost: 0, shadow: false, specular: false }`     | Flat pane with wide bevel. Good for pulsing UI.  |\n| **Frost**   | `{ refraction: 0, bevelDepth: 0.035, bevelWidth: 0.119, frost: 0.9, shadow: true, specular: true }`    | Softly diffused, privacy-glass style.            |\n| **Edge**    | `{ refraction: 0.047, bevelDepth: 0.136, bevelWidth: 0.076, frost: 2, shadow: true, specular: false }` | Thin bevel, bright rim highlights.               |\n\n---\n\n## FAQ\n\n| Question                                                              | Answer                                                                                                                                                                                                                                                                                                                            |\n| :-------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Is there a resize handler?                                            | Yes, debounced to 250ms.                                                                                                                                                                                                                                                                                                          |\n| Does the effect work on mobile?                                       | Yes. The library handles all three WebGL versions and falls back to a frosted CSS `backdrop-filter` on older devices.                                                                                                                                                                                                             |\n| I have a preloader — how should I initialise?                         | Add `data-liquid-ignore` to your preloader's top-level container to exclude it from the snapshot. Then call `liquidGL()` inside a `DOMContentLoaded` listener as you normally would.                                                                                                                                              |\n| What's the right way to use Glassworks with page animations?          | Say you have a preloader, above-the-fold intro animations, and scroll animations. The order is: 1) set `data-liquid-ignore` on the preloader, 2) animate the preloader and set up initial states, 3) call `liquidGL()`, 4) optionally, in `on.init()`, run any post-snapshot scripts (e.g. animate the target element).            |\n| Can I use Glassworks on multiple elements?                            | Yes. Any element matching `target` gets glassified. **All lenses must share the same `z-index`** because of shared-canvas optimisations. If you specify different `z-index` values, the highest one wins.                                                                                                                          |\n| Will I exceed WebGL contexts or hit other perf walls?                 | No. The library uses a shared canvas for all instances. Tested up to 30 elements on one page without crashes or perf problems.                                                                                                                                                                                                    |\n| Are there animation limitations?                                      | Rotation and scale are expensive. `shadow`, `specular`, and `tilt` should be used carefully on pages with many instances or complex animations — they can clog the render pipeline.                                                                                                                                                |\n| Why does my page lazy-load html2canvas on the first capture?          | The snapdom build detected a `position: absolute` descendant on your snapshot target, which is a layout pattern snapdom's foreignObject pipeline can't render correctly. Glassworks fetches html2canvas at that moment and composites the absolute element on top of the snapdom base. To avoid the runtime fetch, pre-load html2canvas yourself or use the legacy build. |\n\n---\n\n## Browser support\n\nGlassworks runs on every WebGL-enabled browser on desktop, tablet, and mobile.\n\n| Browser        | Supported |\n| :------------- | :-------: |\n| Google Chrome  |    Yes    |\n| Safari         |    Yes    |\n| Firefox        |    Yes    |\n| Microsoft Edge |    Yes    |\n\n> [!NOTE]\n> Performance varies between browsers. Safari can be unstable when the liquid element(s) take more than ~50% of viewport width or height. Practical issues are rare — test on your target devices.\n\n---\n\n## Notes\n\n- For dynamic content to refract in real time, register it with `liquidGL.registerDynamic()`. Set initial animation states **before** calling `liquidGL()` so they're captured correctly.\n- The library ignores `fixed`-position elements. This is a safety net for a known bug between html2canvas and mobile browsers that can prevent the snapshot from running. It shouldn't get in your way.\n- Multiple instances must share the same `z-index`. Different values fall back to the highest. The shared canvas prevents WebGL context blow-ups — there's no work-around for that.\n- For better performance on complex pages, snapshot a smaller, specific element instead of the whole page (e.g. `snapshot: '.my-background'`). Less texture memory, faster captures.\n- The initial capture is async. Call `liquidGL()` inside a `DOMContentLoaded` or `load` handler so content is available to the snapshot.\n- Very long documents can exceed GPU texture limits and cause memory or performance issues. Segment long pages or lower `resolution`.\n- `shadow` and `tilt` create new stacking layers behind the `target` (`shadow` at `z-index - 2`, the `tilt` helper canvas at `z-index - 1`). Leave room in your `z-index` values so they don't get clipped.\n- Like any WebGL effect, image content inside the `target` needs permissive `Access-Control-Allow-Origin` headers to avoid CORS issues.\n\n**Exclude elements** — set `data-liquid-ignore` on the parent container of whatever you want to keep out of the refraction.\n\n**Content visibility** — use `z-index: 3;` on the content inside your target so it sits on top of the lens. Pair with `mix-blend-mode: difference;` for better legibility.\n\n**Border-radius** — Glassworks inherits the `border-radius` of the `target` automatically. If you animate it (e.g. on scroll), the bevel animates in real time to stay in sync.\n\n---\n\n## Under the hood\n\nA couple of things worth knowing if you're curious how the snapdom rewrite hangs together.\n\n**The drift fix.** Upstream passes lens elements to `html2canvas` via the `ignoreElements` callback, which the library implements by setting `display: none` on those elements in its cloned DOM. That collapses real layout space, so every section below a lens shifts up in the snapshot. The lens math reads positions from the live DOM, so live coords stop matching snapshot coords, and every lens refracts the wrong region — the offset compounds the further down the page each lens sits. Glassworks tags lens elements with a data attribute and uses html2canvas's `onclone` hook (and the snapdom equivalent) to apply `visibility: hidden` to the clone instead. Layout is preserved, no drift, no flicker.\n\n**The hybrid capture.** snapdom's `<foreignObject>` pipeline can't render `position: absolute` descendants of the snapshot target the way html2canvas does. For those, Glassworks lazy-loads html2canvas only when an absolute descendant is detected and composites that element on top of the snapdom base canvas. Pages without that pattern never trigger the html2canvas fetch and stay at ~50KB.\n\n---\n\n## Credits & licence\n\nGlassworks is a fork of [`liquidGL`](https://github.com/naughtyduk/liquidGL) by NaughtyDuk©. The original library and its WebGL renderer were authored by NaughtyDuk. Glassworks adds the snapdom default capture pipeline, the html2canvas hybrid fallback, the long-page drift fix, browser-compat tuning, and ongoing maintenance.\n\nMIT © NaughtyDuk (original) · MIT © Codedgar (fork)\n","readmeFilename":"README.md","_rev":"1-41779511cdcbd05f649595078e126d38"}