[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-94719":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":14,"stars30d":14,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":15,"rankGlobal":9,"rankLanguage":9,"license":16,"archived":17,"fork":17,"defaultBranch":18,"hasWiki":19,"hasPages":19,"topics":20,"createdAt":9,"pushedAt":9,"updatedAt":21,"readmeContent":22,"aiSummary":23,"trendingCount":14,"starSnapshotCount":14,"syncStatus":12,"lastSyncTime":24,"discoverSource":25},94719,"webgl-apple-liquid-glass","Oliverrr2424\u002Fwebgl-apple-liquid-glass","Oliverrr2424","WebGL2 replica of Apple's Liquid Glass: squircle SDF thickness field -> Snell refraction with a meniscus rim, dispersion, variable-blur scattering, Fresnel rim",null,"JavaScript",106,2,4,0,41.43,"MIT License",false,"main",true,[],"2026-08-24 04:01:22","# apple-liquid-glass-webgl\n\nReusable WebGL2 liquid glass surfaces for folders, rectangles, pills, and circles.\n\nThis package is framework-free and renders Apple-inspired translucent surfaces with screen-space refraction, variable blur, Fresnel reflection, chromatic dispersion, edge highlights, and contact shadows.\n\n## Install\n\n```bash\nnpm install apple-liquid-glass-webgl\n```\n\nWebGL2 is required. Give the canvas a CSS width and height before rendering.\n\nCheck support before constructing, so a browser without WebGL2 can fall back instead of catching a constructor throw:\n\n```js\nif (LiquidGlassWebGL.isSupported()) {\n  const glass = new LiquidGlassWebGL(canvas);\n} else {\n  panel.classList.add('css-fallback');\n}\n```\n\n## Usage\n\n```js\nimport { LiquidGlassWebGL } from 'apple-liquid-glass-webgl';\n\nconst canvas = document.querySelector('canvas');\nconst glass = new LiquidGlassWebGL(canvas, {\n  material: 'regular',\n  fusion: true,\n});\n\nawait glass.setWallpaper('\u002Fimages\u002Fwallpaper.jpg');\nglass.setElements([\n  { id: 'folder', shape: 'folder', x: 80, y: 80, width: 220, height: 220 },\n  { id: 'rect', shape: 'rect', x: 360, y: 100, width: 280, height: 190 },\n  { id: 'pill', shape: 'pill', x: 700, y: 130, width: 250, height: 110 },\n  { id: 'circle', shape: 'circle', x: 1000, y: 130, size: 110 },\n]);\nglass.render();\n```\n\nThe component accepts CSS-pixel coordinates. Content such as app icons, labels, or buttons can be drawn in a separate canvas layer above the WebGL canvas. Optical lengths automatically scale down when a component's short side is too small for the configured bevel: the refracting rim is capped at 30% of that side, and glass height, blur, highlights, and their backdrop probes follow the same scale. Large components and materials that already use a narrow bevel are unchanged.\n\n## Default material parameters\n\n`getDefaultMaterial()` returns a fresh copy of the package's default material parameters on every call. This makes it safe to customize the result without mutating the package defaults.\n\n```js\nimport { getDefaultMaterial, LiquidGlassWebGL } from 'apple-liquid-glass-webgl';\n\nconst material = getDefaultMaterial();\nmaterial.blurRim = 32;\n\nconst glass = new LiquidGlassWebGL(canvas, { material });\n```\n\n`makeMaterial()` with no argument is also equivalent to `getDefaultMaterial()`. The exported `DEFAULT_MATERIAL` constant contains the same values for read-only inspection.\n\n| Group | Parameter | Default |\n| --- | --- | ---: |\n| Shape | `radius` | `64.00` |\n| Shape | `squircle` | `2.00` |\n| Shape | `mergeRadius` | `52.00` |\n| Shape | `bevel` | `34.00` |\n| Shape | `height` | `21.00` |\n| Shape | `sizeAdaptation` | `1.00` |\n| Optics | `ior` | `2.00` |\n| Optics | `dispersion` | `0.06` |\n| Optics | `refractScale` | `3.00` |\n| Optics | `meniscus` | `1.00` |\n| Optics | `blurPlateau` | `8.00` |\n| Optics | `blurRim` | `48.00` |\n| Optics | `opticalDensity` | `0.65` |\n| Lighting | `specular` | `0.89` |\n| Lighting | `specPower` | `11.50` |\n| Lighting | `highlightAdapt` | `0.83` |\n| Lighting | `highlightWidth` | `0.76` |\n| Lighting | `highlightSharpness` | `0.55` |\n| Lighting | `highlightBase` | `0.30` |\n| Lighting | `fresnel` | `0.65` |\n| Lighting | `saturation` | `1.35` |\n| Lighting | `brightness` | `0.00` |\n| Lighting | `tintAmount` | `0.02` |\n| Lighting | `tintAdapt` | `0.14` |\n| Edge | `shadow` | `0.09` |\n| Edge | `shadowSize` | `4.00` |\n| Edge | `shadowOffset` | `0.00` |\n| Edge | `lightX` | `-0.18` |\n| Edge | `lightY` | `0.08` |\n| Edge | `edgeLine` | `0.30` |\n| Edge | `edgeWidth` | `0.50` |\n| Edge | `edgeDark` | `0.02` |\n\nThe returned object also includes `tintColor: [1, 1, 1]` and `debug: 0`. Parameter names use the JavaScript API names; for example, `highlightAdapt` is the “Light adaptation” control and `edgeLine` is the “Edge highlight” control. Set `sizeAdaptation` to `0` when material lengths must remain absolute; intermediate values blend between absolute and fitted optics.\n\nBackdrop RGB is stored in `SRGB8_ALPHA8`: image uploads decode to linear light, every downsample and tent-upsample pass filters linear radiance, and writes encode back to sRGB. Alpha remains linear for the optical-density channel. Wide blur blends in the reconstructed chain to avoid coarse-mip breathing; final glass output receives a sub-LSB triangular dither to suppress dark-gradient banding. `tintAdapt` controls the component-level light\u002Fdark material switch (`0` keeps `tintColor` fixed, `1` fully follows the backdrop below the component).\n\n## Live backdrops and overlay mode\n\nUse `compositeMode: 'overlay'` when the original backdrop remains visible underneath the WebGL canvas. Pixels outside the glass stay transparent, while the supplied backdrop source is sampled for refraction and blur.\n\n```js\nconst glass = new LiquidGlassWebGL(canvas, {\n  compositeMode: 'overlay',\n  elements: [\n    { id: 'panel', shape: 'rect', x: 80, y: 80, width: 520, height: 360 },\n  ],\n});\n\n\u002F\u002F Canvas, OffscreenCanvas, and video sources are detected as live. The\n\u002F\u002F renderer starts automatically and uploads their latest frame before drawing.\nglass.setBackdrop(animatedCanvas);\n\n\u002F\u002F Stop the render loop when the view is hidden or unmounted.\nglass.stop();\n```\n\nStatic image sources upload once:\n\n```js\nawait glass.loadBackdrop('\u002Fimages\u002Fwallpaper.jpg');\n```\n\nThe update behavior can be selected explicitly:\n\n```js\nglass.setBackdrop(source, { update: 'live' });\nglass.setBackdrop(source, { update: 'static', autoStart: false });\nglass.updateBackdrop(); \u002F\u002F manually upload the latest static-source pixels\nglass.start();\nglass.stop();\n```\n\nThe default `compositeMode: 'replace'` preserves the original behavior and draws the supplied backdrop across the full WebGL canvas. Browsers do not expose arbitrary composited DOM\u002FCSS pixels to WebGL, so the backdrop must be supplied explicitly as an image, canvas, video, ImageBitmap, or OffscreenCanvas. Cross-origin sources must permit CORS access.\n\n## Visual preview\n\nThese screenshots are captured from the playground with the inspector hidden. Each scene uses the same folder, rect, pill, and circle surfaces:\n\n### Smooth-union fusion\n\nNearby components can share one continuous distance field, so the silhouette, refraction, highlights, and shadow flow through the merged surface.\n\n![Smooth-union liquid glass fusion](https:\u002F\u002Fcdn.jsdelivr.net\u002Fgh\u002FOliverrr2424\u002Fwebgl-apple-liquid-glass@main\u002Fassets\u002Freadme\u002Fsmooth-union.jpg)\n\n### Individual scene previews\n\n| Natural landscape | Abstract lines |\n| --- | --- |\n| ![Natural landscape](https:\u002F\u002Fcdn.jsdelivr.net\u002Fgh\u002FOliverrr2424\u002Fwebgl-apple-liquid-glass@main\u002Fassets\u002Freadme\u002Fnatural-lake.jpg) | ![Abstract lines](https:\u002F\u002Fcdn.jsdelivr.net\u002Fgh\u002FOliverrr2424\u002Fwebgl-apple-liquid-glass@main\u002Fassets\u002Freadme\u002Fabstract-lines.jpg) |\n\n| Color blocks | Night city |\n| --- | --- |\n| ![Color blocks](https:\u002F\u002Fcdn.jsdelivr.net\u002Fgh\u002FOliverrr2424\u002Fwebgl-apple-liquid-glass@main\u002Fassets\u002Freadme\u002Fcolor-blocks.jpg) | ![Night city](https:\u002F\u002Fcdn.jsdelivr.net\u002Fgh\u002FOliverrr2424\u002Fwebgl-apple-liquid-glass@main\u002Fassets\u002Freadme\u002Fnight-city.jpg) |\n\n## Hit testing\n\n`hitTest()` evaluates the same signed distance field as the shader, so a pointer lands on the shape rather than on its bounding box: the corners of a circle are not clickable, and inside a fused group the bridge between two components is.\n\n```js\ncanvas.addEventListener('pointerdown', (event) => {\n  const element = glass.hitTestEvent(event); \u002F\u002F null outside the surface\n  if (element) startDragging(element.id);\n});\n\nconst { x, y } = glass.pointerPosition(event); \u002F\u002F canvas-relative CSS pixels\nglass.hitTest(x, y, { tolerance: 8 });         \u002F\u002F slack for coarse pointers\nglass.distanceAt(x, y);                        \u002F\u002F signed distance, negative inside\n```\n\nA gap between two components only closes into a bridge while it is narrower than about half the fusion distance; past that, `mergeRadius` only softens the approach.\n\n## Rendering behaviour\n\n`render()` returns without touching the GPU when nothing changed since the last frame, so an animation loop over a static scene is free. Every mutator marks the component dirty; a live backdrop always redraws. The sampled backdrop and its mip chain have a separate dirty flag, so moving a shape or changing its material only redraws the visible glass passes.\n\n```js\nglass.render();                  \u002F\u002F no-op when clean\nglass.render({ force: true });   \u002F\u002F always draws, for pixel read-back\nglass.markDirty();               \u002F\u002F after mutating glass.material in place\nglass.markBackdropDirty();       \u002F\u002F after changing a backdrop source in place\n```\n\nPass `preserveDrawingBuffer: true` if you read the canvas back with `readPixels` or `toDataURL` after the frame has been composited.\n\n## Context loss and accessibility\n\nA GPU context can be lost at any time. The component takes over recovery: the canvas holds its last frame, every call is a safe no-op while the context is gone, and programs, render targets and backdrop textures are rebuilt when the browser restores it.\n\n```js\nconst glass = new LiquidGlassWebGL(canvas, {\n  onContextLost: () => showPlaceholder(),\n  onContextRestored: () => hidePlaceholder(),\n});\n\nglass.contextLost; \u002F\u002F true while the surface is frozen\n```\n\nUnder `prefers-reduced-transparency: reduce` the material falls back to a near-opaque surface: refraction, dispersion and scattering are removed while the shape, edge and shadow stay. Opt out with `respectReducedTransparency: false`, and read `glass.effectiveMaterial` for the parameters actually in use.\n\n`autoResize` (on by default) redraws when the canvas element is resized, which dirty tracking would otherwise miss in an app that renders on demand.\n\n## API\n\n```js\nLiquidGlassWebGL.isSupported();\n\nglass.setMaterial('clear');\nglass.setMaterial({ blurPlateau: 4, edgeLine: 0.2 });\nglass.setBackdrop(animatedCanvas, { update: 'live' });\nglass.updateBackdrop();\nglass.start();\nglass.stop();\nglass.setFusion(true, 52); \u002F\u002F smooth-union distance in CSS pixels\nglass.setWallpaperIndex(0);\nglass.addElement({ id: 'new-folder', shape: 'folder', x: 20, y: 20, size: 180 });\nglass.updateElement('new-folder', { x: 40 });\nglass.removeElement('new-folder');\nglass.hitTest(x, y);\nglass.resize();\nglass.destroy();\n```\n\nAvailable presets are `regular`, `clear`, and `lens`. Available shapes are `folder`, `rect`, `pill`, and `circle`. With `fusion` enabled, nearby elements are evaluated as one smooth-union distance field, so their silhouette, normals, refraction, highlights, and shadow merge continuously.\n\nThe shader carries 16 shapes per pass. Elements too far apart to influence each other are split into separate passes automatically, so the 16 shape limit applies per fused cluster rather than per scene; a cluster larger than that is still split, and logs a warning explaining that the silhouette will not bridge across every one of them.\n\nThe geometry helpers behind all of this are exported for use without a canvas — `sdGroup`, `hitTestElements`, `connectedElementGroups` and `groupElements`.\n\n## Playground\n\nThe interactive demo used to develop the material is deployed at\n[oliverrr2424.github.io\u002Fwebgl-apple-liquid-glass](https:\u002F\u002Foliverrr2424.github.io\u002Fwebgl-apple-liquid-glass\u002F), or run it locally:\n\n```bash\nnpm install\nnpm run serve\n```\n\nOpen [http:\u002F\u002Flocalhost:8765](http:\u002F\u002Flocalhost:8765). It drives the published component through its public API, and the inspector covers:\n\n- Eight scenes: four wallpapers, plus a tab bar over app content, a notification, a control-centre grid, and a scrolling feed that exercises the live backdrop path.\n- Component editing: add, retype, resize and delete surfaces; drag them, or select one and use the arrow keys (`Shift` for ten pixels, `Alt` to resize, `[` and `]` to cycle, `Delete` to remove).\n- Every material parameter as both a slider and a typed value. Double click a parameter name to reset just that one; modified parameters are marked.\n- **Copy link** puts the whole session in the URL, **Copy code** emits the snippet that reproduces it.\n- A frame-rate, CPU-per-frame, drawing-buffer and pass-count readout. A static scene reports `idle`, because dirty tracking skips the GPU entirely.\n- Local image or looping-video uploads, and the thickness, normals and dispersion debug outputs.\n\n## Development\n\n```bash\nnpm test                     # unit tests, then the browser test pages\nnpm run test:visual          # golden image comparison\nnpm run test:visual:update   # record a baseline for this renderer\nnpm run shot shots\u002Fliquid-glass.png -- --scene 0 --size 1200x720 --no-panel\nnpm run pack:check\n```\n\n`tests\u002F*.test.mjs` are Node unit tests over the geometry and material rules. `tests\u002F*.html` are browser pages, each exporting `window.runTest()`; they cover live and static backdrops, context loss and recovery, and the dirty-tracking contract by counting draw calls.\n\nVisual regression forces ANGLE's deterministic SwiftShader backend. Baselines live in `shots\u002Fbaseline\u002F\u003Crenderer>\u002F`; Linux's Subzero JIT and macOS's LLVM JIT have separate golden sets because a small number of edge pixels round differently. A missing baseline fails the run, and rejected frames are written to `shots\u002Ftmp-*.png`.\n\n## Automated npm publishing\n\nEvery push to `main` runs [`.github\u002Fworkflows\u002Fci.yml`](.github\u002Fworkflows\u002Fci.yml) and then [`.github\u002Fworkflows\u002Fpublish-npm.yml`](.github\u002Fworkflows\u002Fpublish-npm.yml), which publishes with provenance when the version in `package.json` is newer than the version already on npm. Publishing depends on the test job, so a failing test blocks the release.\n\nTo enable publishing, add a repository secret named `NPM_TOKEN` containing an npm token with permission to publish `apple-liquid-glass-webgl`. Bump the package version before pushing a release to `main`.\n\n## License\n\nMIT\n","这是一个基于WebGL2实现苹果Liquid Glass视觉效果的轻量级JavaScript库，用于在网页中渲染具有真实光学特性的液态玻璃材质界面元素。核心功能包括基于SDF的squircle形状建模、斯涅尔折射、色散（chromatic dispersion）、可变模糊散射、菲涅尔边缘高光及弯月形边缘（meniscus rim）模拟，并支持文件夹、矩形、胶囊、圆形等多种形状。项目无框架依赖，提供CSS像素坐标接口与自动缩放适配机制，适用于需要高端UI质感的桌面端Web应用、系统级面板、启动器或设计演示场景。","2026-08-14 02:30:08","CREATED_QUERY"]