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

lgimgui

v0.1.3

Published

liquid glass immediate mode gui for the browser

Readme

lgimgui

An immediate mode GUI for the browser whose navigation layer is liquid glass. Rendering is WebGL2. The optical model is a port of liquid-glass-studio (the same parameters, ranges, and formulas), with the role presets tuned in lgwm. The composition rules come from Apple's Liquid Glass guidance and are enforced by the library rather than left to the caller.

npm install
make client     # demo on http://localhost:5173
make test

The demo is a desktop: a dock, windows with sidebars and scroll edges, a merged toolbar group, menus that pop from their buttons, a slider whose thumb lifts into clear glass while held, and a material editor written with the library itself that edits every studio parameter live for each role.

Writing a frame

import { createUI } from 'lgimgui'

const ui = createUI(canvas)
const state = { volume: 0.6, wifi: true, tab: 0 }

ui.start(() => {
  ui.image(wallpaper, { x: 0, y: 0, w: 'fill', h: 'fill' })

  const win = ui.window({ key: 'settings', title: 'Settings', x: 80, y: 60, w: 520, h: 360 }, () => {
    ui.column({ pad: 16, gap: 12 }, () => {
      state.tab = ui.segmented(['General', 'Display'], state.tab, { key: 'tab' })
      state.volume = ui.slider(state.volume, { key: 'volume' })
      state.wifi = ui.toggle(state.wifi, { key: 'wifi' })
      if (ui.button('Done', { primary: true })) close()
    })
  })

  ui.glass({ key: 'dock', role: 'dock', shape: 'pill', direction: 'row', gap: 8, pad: 8, y: 800, alignSelf: 'center' }, () => {
    if (ui.button(null, { key: 'mail', icon: 'grid' })) openMail()
  })
})

Every call records a node for this frame. After the frame callback returns the tree is measured and positioned, painted into strata, rendered, and hit tested for the next frame. Widget state (springs, scroll offsets, carets, menu open flags) lives in a store keyed by id and is collected when the id stops appearing.

Layout

Containers take direction (row, column, stack), gap, pad, align (start, center, end, stretch), justify (start, center, end, between). Children size with w/h as a number, 'fill', or omitted for intrinsic size, plus fr to share leftover space. Setting x or y positions a child absolutely inside its parent; alignSelf aligns the other axis.

Ids

Ids are paths built from the enclosing containers and the widget label. Pass key when labels repeat or change. Repeated keys in one scope are disambiguated in order, so a list of identical buttons still works as long as its order is stable.

Glass

ui.glass(props, fn) draws one glass element and lays out its children as overlays on it.

  • role: window, pane, toolbar, menu, control, thumb, dock, clear, thin, studio. A role is a material preset; every preset exposes all studio parameters in ui.materials[role], with independent unfocused and dark variants.
  • variant: regular (default) adapts to its backdrop; clear never adapts, always dims, and forces bright foreground.
  • shape: pill, circle, or a numeric radius with roundness (2 is round, 5 is the squircle default).
  • merge: true turns each direct child into a shape of one smooth-min union with mergeRate pixels of bridging.
  • focused: false switches to the unfocused material so a whole hierarchy recedes together.
  • tint: [r, g, b, a] applies a semantic tint mapped to backdrop brightness.
  • surface: small flips light and dark with the backdrop; large holds polarity.
  • lift: true lets a transient element render as glass while inside glass.

Controls called inside glass paint as fills, vibrancy, and strokes on that glass. Controls called outside glass bring their own glass backing in the control role. Nesting ui.glass inside ui.glass without lift throws.

Widgets

text, label, heading, icon, image, rect, separator, spacer, box, row, column, stack, scroll, popup, button, iconButton, capsule, toolbar, trafficLights, toggle, slider, segmented, progress, item, menu, menuButton, textInput, pane, window, dragHandle, hitArea.

Interactive widgets return their result immediately: button returns whether it was clicked, slider and toggle return the new value, menuButton returns the chosen item, window returns { closed, moved, dx, dy, focusRequested, minimized, zoomed }.

scroll accepts edge: { size } to fade its content out at the edges; the content's own pixels become transparent, nothing is painted over them.

Windows and the desktop

ui.desktop() returns a window manager. Register each window every frame with desktop.window(key, spec, fn) and call desktop.draw() once; the manager owns geometry (with cascade placement for new keys), z-order, focus (a press on nothing unfocuses everything), drag, resize, close, minimize, and zoom. spec takes title or a toolbar callback, sidebar and sidebarWidth, w, h, optional x, y, open: false to start hidden, minSize, and resizable. desktop.open(key), close, toggle, isOpen, and focused drive it from the outside (a dock, a menu).

const desktop = ui.desktop()

ui.start(() => {
  desktop.window('sliders', { title: 'Sliders', w: 360, h: 200 }, () => {
    state.volume = ui.slider(state.volume, { key: 'volume' })
  })
  desktop.draw()
})

ui.window underneath composes the sheet: traffic lights at the corner, a full-height pane for the sidebar, a toolbar band (also the drag region), and the content below. Buttons and capsules inside a toolbar take the toolbar material; everything else inside a window is a fill.

Settings

ui.settings.reduceTransparency, ui.settings.increaseContrast, and ui.settings.reduceMotion are material modifiers applied everywhere.

How rendering works

Each frame is a list of strata. Stratum 0 is the content layer. A glass element samples the completed stratum beneath it and writes itself and its overlays into the stratum above. Glass elements that overlap earlier glass are pushed up a stratum automatically, so a higher window sees a lower window only as flattened pixels and two independent windows never refract each other. Popups and lifted elements follow the same rule.

Per stratum the renderer draws the content, the shadows of the glass above (into the backdrop, so the rim refracts them like the studio does), builds a gaussian mip pyramid of the result, and moves on. Blur radius selects a fractional pyramid level, so every element can carry its own blur without extra passes. A tiny level of each stratum's pyramid is read back asynchronously every frame and feeds the adaptation of small Regular glass sitting on it: polarity with hysteresis, shadow depth over detail, and tint opacity.

Text is rasterized into a glyph atlas at device pixel ratio and drawn sharp above the glass. UI fills, strokes, images, glyphs, and gradients go through one instanced batch.

Project layout

src/index.js            createUI
src/core/               ids, state, springs, input, layout, frame driver, theme
src/material/           studio parameter schema, role presets, material transforms, backdrop adaptation, optics
src/render/             WebGL2 context, batch, glyph atlas, blur pyramid, probe, renderer, shaders
src/widgets/            glass, primitives, controls, composite widgets, desktop manager
demo/                   the desktop demo, material editor, and showcase windows
test/                   vitest suites for the pure modules

ui.glass({ ..., shadow: false }) draws a shape without its material's drop shadow.