Apple Liquid Glass style surfaces for egui, rendered on the GPU
through egui-wgpu paint callbacks. One fragment shader does everything: continuous-corner
signed-distance shape, edge refraction (lensing) with optional chromatic aberration, mip-based
backdrop blur, tint / vibrancy, specular rim, inner border and drop shadow. No animation.

Requirements
| Rust | 1.92 or newer (MSRV of eframe / wgpu) |
| Backend | eframe with the wgpu renderer (egui-wgpu); the glow backend is not supported |
| GPU | anything wgpu drives: Metal, Vulkan, DX12 (the demo was developed on macOS) |
Runtime dependencies of the library: egui, egui-wgpu, wgpu, bytemuck, and serde
(optional, feature serde). The demo additionally uses eframe, image, serde_json, rfd
and log.
Run the demo
The first build compiles wgpu and takes a few minutes. To package it as a macOS app see
Build the macOS app. Left panel: sliders for every style
parameter, the presets (Regular, Clear, Dark, Panel), a Live backdrop toggle, and Export /
Import buttons that save the settings as JSON. A dark tint switches the page to a macOS-style dark
theme. Sidebar items switch between the bundled photos (examples/asset); drop an image file onto
the window to load your own. Drag the glass panels around.
Environment knobs for screenshots: LG_PHOTO=<index>, LG_SCROLL=<px>, LG_PRESET=dark|clear,
LG_LIVE=0 (start with the live backdrop off).
Build the macOS app
The demo can be packaged as a regular .app (Dock icon, Launchpad, Finder):
| Command | What it does |
|---|---|
make / make bundle |
release build, Egui Glass.app in target/release/bundle |
make install |
same, then replaces /Applications/Egui Glass.app |
make run / make test / make lint |
cargo run -p egui_glass_demo, tests, clippy |
scripts/bundle-macos.sh does the work: uses the icon from assets/branding (the prebuilt
EguiGlass.icns, or generates one from a 1024 px PNG you pass as an argument), copies the sample
photos into Contents/Resources/asset, writes Info.plist, ad-hoc signs the bundle and, with
--install, copies it to /Applications and strips any quarantine flag. It needs sips,
iconutil and codesign, all part of macOS.
On the first launch from Finder, macOS shows "Apple could not verify ... is free of malware" because the app is not notarized: open System Settings > Privacy & Security and click Open Anyway once. To ship a build that opens without the prompt, sign with a Developer ID and notarize:
CODESIGN_IDENTITY="Developer ID Application: <name> (<team id>)" \
NOTARY_PROFILE="<keychain profile from: xcrun notarytool store-credentials>" \
Uninstall by deleting /Applications/Egui Glass.app; the app stores nothing else.
Use the library in your own app
You do not need this repository checked out; add the crate as a git dependency (pin a rev or
tag for reproducible builds) and make sure eframe uses the wgpu backend:
[]
= { = "0.35", = false, = ["default_fonts", "wgpu"] }
= "0.35"
= "0.1" # from crates.io
# egui_glass = { version = "0.1", features = ["serde"] } # GlassStyle: Serialize / Deserialize
# egui_glass = { git = "https://github.com/heonny/egui-glass.git" } # or track the repository
Minimal app (also in the repo as examples/demo/examples/minimal.rs, run it with
cargo run -p egui_glass_demo --example minimal): register the pipeline and a backdrop once, then
draw glass wherever you like.
use egui;
use ;
;
API at a glance:
init(render_state, msaa) |
once; installs the wgpu pipeline |
set_backdrop(ctx, rs, &ColorImage) |
upload / replace the image glass refracts |
show_backdrop(ui, rect) |
draw it aspect-filled in rect and record the mapping |
show_backdrop_mapped(ui, image_rect, clip, outside) |
place it yourself; outside is what glass shows beyond it |
Glass::new(style).inner_margin(..).show(ui, ..) |
card / section / sidebar container |
GlassButton::new(text).style(..).icon().show(ui) |
capsule or round button |
GlassToolbar::new(style).show(ui, ..) |
capsule bar of flat buttons |
paint_glass(ui, rect, &style) |
just the surface, for custom widgets |
LiveBackdrop::new(rs, fonts) / .run(ui, page_color, ..) |
glass refracts live content, see below |
register_native_texture(rs, &view) |
egui texture id usable in both normal and live passes |
GlassStyle fields (all in logical points): corner_radius (f32::INFINITY = capsule),
corner_smoothing (0 = circular arc, 0.6 = the continuous look of iOS corners, 1 = max), blur,
refraction, edge_width, chromatic, tint, brightness, saturation, specular, border,
shadow, shadow_radius, shadow_offset, shadow_spread. Presets: regular(), clear(), dark(), panel(), panel_dark().
Use panel() for large surfaces (sidebars, sheets): heavy blur, almost no lensing. Text on glass
picks a light colour automatically when the tint is dark.
Corners use the corner-smoothing model popularised by Figma (a Bezier ease into a shorter circular
arc, spanning (1 + smoothing) * radius along each edge), evaluated as a signed-distance field in
the shader. No Apple curve constants are used.
How it works / limits
egui paints in a single render pass, so a shader cannot read the pixels already drawn behind a
widget in the same frame. By default glass therefore refracts a backdrop image you register
with set_backdrop (a wallpaper, a photo) and place with show_backdrop; anything egui draws on
top of it shows as the page colour through the glass. This is cheap and matches Apple's guidance
that glass floats above content and is never stacked on glass.
Each glass surface costs one draw call (a single triangle) and one 256-byte uniform slot; the backdrop is uploaded once and turned into a 13-tap (Jimenez) mip pyramid on the GPU; blur is a 9-tap disc sampled at a fractional level, which reads like a Gaussian without banding.
Live backdrop (optional)
LiveBackdrop lifts that limit: the content you pass to LiveBackdrop::run is laid out a second
time in a twin egui context (same memory, no input events) and rendered off screen without the
glass, then used as this frame's backdrop. Glass then refracts everything beneath it, text
included, with no frame of lag. The cost is one extra layout and draw of that content per frame.
// once, after init and before set_backdrop / register_native_texture
let live = new;
// every frame: content inside, glass outside
live.run;
new.show;
Images drawn inside the content must be registered with egui_glass::register_native_texture
(or set_backdrop) so the off-screen pass can draw them too.
Development
See CLAUDE.md for the project layout and the design rules the code follows.
Trademark note
"Liquid Glass" is Apple's name for its design language; egui_glass is an independent
reimplementation of the look, not affiliated with or endorsed by Apple. The term is only used
descriptively in this documentation.
License
MIT. See LICENSE.