Skip to content

wiiiimm/glass-gl

Repository files navigation

glass-gl — a glass prism refracting a beam of light

glass-gl

Real WebGL liquid glass for the web — a tiny, framework-agnostic engine that makes any DOM element refract the background behind it like a physical glass lens (bend + magnify + frost + chromatic edge + rim light), not just a backdrop-filter blur.

glassOS — a desktop made of glass-gl surfaces: menu bar, dock, and draggable app windows (including the Spotify Music and YouTube Video apps) refracting a floral wallpaper with the glassOS wordmark baked in

⚠️ Experimental. Early and evolving — the API, parameters, and visuals may change without notice, and it isn't production-hardened yet. See Limitations.

Get started

Building with an AI coding agent? This is the easiest path — install the skill, then just ask for liquid glass. Your agent gets the full API, the parameters, and the one rule, and wires it up correctly the first time:

npx skills add wiiiimm/glass-gl

Wiring it by hand? No install needed — import straight from a CDN in any .html file (the @latest tag always resolves to the newest release):

import { createGlass } from "https://esm.sh/glass-gl@latest";

Using a bundler? npm i glass-gl instead. The full reference — the one rule, API, every parameter, live backgrounds, gotchas — lives in skills/glass-gl/SKILL.md.

How it works

One fullscreen WebGL <canvas> sits behind your page. Every frame, the engine reads each registered element's on-screen rectangle and draws a glass lens at that spot — so the glass tracks elements as they move, drag, or resize. Your content sits on the glass, crisp and untouched; only the background gets bent.

Each lens runs this pipeline over the background you gave it:

  1. Rounded-box mask — the glass shape (an SDF rounded rectangle), matched to the element's border-radius.
  2. Droplet refraction — bends and magnifies the background; curve shapes the profile (optically flat centre, steep bend at the rim).
  3. Blur — a soft frost (blur).
  4. Chromatic dispersion — splits colour at the rim so white edges fringe into R/G/B, like real glass (dispersion).
  5. Vibrancy — saturates the refracted backdrop so it reads luminous (saturation).
  6. Rim light — a directional specular glint from the surface normal (edgeLight + lightAngle), plus a frosted rim band (edgeFrost), and an optional milky tint.

A <canvas> or <video> background is live — re-uploaded every frame — so a playing video, a Ken Burns slideshow, or anything you paint into a canvas refracts in real time. To make the glass bend text or graphics, draw them into that background canvas (the lens only refracts the background layer — see the one rule).

Usage

import { createGlass } from "https://esm.sh/glass-gl@latest";

const glass = createGlass({ canvas, background: "/bg.jpg" });

glass.register(el, { radius: 24 });   // any element becomes liquid glass (match its border-radius)

glass.setParams({ refraction: 0.22, blur: 1.2, dispersion: 0.2, curve: 2.8, tint: [1, 1, 1] });

glass.unregister(el);                 // stop treating el as glass
glass.setBackground("/other.jpg");    // string / <img> = static
glass.setBackground(videoEl);         // <video> or <canvas> = LIVE, refracted every frame
glass.destroy();                      // stop the render loop and release

setParams — the look

Param Range What it does
refraction ~0–0.5 Lens strength — how much the background bends / magnifies.
blur px Frost (sample spread).
liquidness 0–~0.6 Milky mix toward tint (dark tint + liquidness = smoked glass).
edgeLight ~0–2 Strength of the directional rim glint.
edgeFrost 0–1 Frosted rim band. 0 = none.
dispersion 0–1 Chromatic aberration at the rim (white → RGB fringe). Off by default.
saturation ~0.8–1.7 Vibrancy of the refracted backdrop. 1 = neutral, ~1.2 = luminous.
curve 1–4 Lens profile: 1 = linear bend, ~3 = droplet (flat centre, steep rim).
lightAngle deg Direction the rim glint comes from (0 = top, 90 = right).
radius px Default corner radius; override per element via register(el, { radius }).
tint [r,g,b] 0–1 Glass colour ([1,1,1] = clear/light; dark values = dark/smoked glass).

createGlass options

  • dpr — retina-sharp by default (renders at devicePixelRatio, clamped to 2×). Pass a number to cap it, or false for 1:1.
  • transparent: true — draw only the lenses and leave every other pixel transparent, for when the background you refract is the page's own live canvas (e.g. a three.js scene) so the page stays crisp.
  • size: () => ({ w, h }) — size the canvas to a positioned/scrolled box instead of the viewport (e.g. an immersive iOS 26 Safari layout where the canvas covers a scroll band taller than the screen, so the background bleeds under the bars).

The one rule

It bends a background, not the page. The shader only samples the background texture you give it — a photo, video, gradient, or <canvas>. It has no access to your DOM, so it can not refract arbitrary live HTML sitting behind it (that would mean rasterising the page every frame — slow and CORS-bound, which is why even Apple's Liquid Glass runs in the OS compositor, not the browser).

Arrangement Works?
Content (text, images) on top of a glass surface ✅ yes
Glass refracting a background image / canvas / video / gradient ✅ yes
Glass refracting a live DOM element behind it (e.g. a scrolling <p>) ❌ no — bake it into the background instead

Want a frosted panel over live, scrolling content (a feed, an article)? Reach for the browser's own backdrop-filter: blur() — it blurs real DOM, live and for free. It just can't refract. Use glass-gl for glass over a media background: hero sections, photo/video backdrops, draggable tiles, showpieces.

Limitations

Beyond the one rule, know these before you reach for it:

  • Rounded rectangles only. Every surface is a rounded-box lens (a circle is just radius = size / 2). Arbitrary shapes — triangles, blobs — aren't supported; the shape is baked into the shader.
  • Up to 16 glass surfaces at once (the shader's array size). Register more and the extras are skipped.
  • It runs a continuous render loop. One requestAnimationFrame keeps the GPU awake so the glass tracks moving/resizing elements every frame. Fine for a desktop showpiece; for mobile or always-on UI, pause it when nothing's moving or the page is offscreen.
  • External images need CORS. A cross-origin background texture must be served with Access-Control-Allow-Origin (and loaded crossOrigin="anonymous"), or the WebGL upload fails. Same-origin / bundled images are simplest.
  • Needs WebGL. createGlass throws if a WebGL context can't be created — feature-detect or guard it if you support very old browsers.
  • Glass needs a busy/colourful background to read — put it over a photo, video, or gradient, never a flat colour.
  • Experimental — not production-hardened; API and defaults may still change.

Demo

The playground/ directory is the live demo site (deployed at glass-gl.wiiiimm.codes), and every page is a plain consumer of the same glass-gl.js you'd install:

  • index.html — the landing page: glass cards over a dark Ken Burns photo, pick one of the three experiences.
  • os.html — glassOS. A desktop where everything is glass: menu bar, dock, and draggable app windows (About, How it works, Skills, Wallpapers, Settings, a Spotify Music player, a YouTube Video player, and a Terminal).
  • playground.html — tune every parameter live (presets, sliders, copy-as-code). Add ?transparent to see transparent mode over the page's own live canvas.
  • mobile.html — an iPhone-style home screen of glass: draggable icons, a working music/video player, and a Camera app that feeds your real camera to the engine so every tile refracts the world. Immersive iOS 26 full-bleed layout.
cd playground && python3 -m http.server 8000
# open http://localhost:8000                  (landing)
# /os.html · /playground.html · /mobile.html  (the three experiences)

Releasing

glass-gl ships from packages/glass-gl, kept simple while the project is experimental:

  1. Bump the version in packages/glass-gl/package.json and commit (Conventional Commits).
  2. Run the Publish glass-gl workflow — GitHub → Actions → Run workflow, or gh workflow run "Publish glass-gl".
  3. CI publishes to npm and cuts a matching GitHub Release — only when that version isn't already on npm. It does not run on push, so a routine commit can't surprise-publish.

The engine lives once: packages/glass-gl/glass-gl.js is canonical and playground/glass-gl.js is a symlink to it — edit the package file. See AGENTS.md for the full layout and conventions.

License

AGPL-3.0-or-later © wiiiimm — see LICENSE. If you run a modified version as part of a network service, the AGPL requires you to offer its source to your users. (Versions ≤ 0.0.2 were published under MIT and remain so.)

About

Real WebGL liquid glass for the web — any element refracts the background like a glass lens (not just backdrop-filter blur).

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors