Native macOS Liquid Glass HUD for Hammerspoon
(and the command line). It renders toasts, titled panels, and a wide
cheat-sheet panel using real Apple NSGlassEffectView on macOS 26+ (Tahoe),
with a clean NSVisualEffectView frosted fallback on older systems.
The typical use case: replace Hammerspoon's hand-drawn hs.canvas / hs.alert
popups with polished, translucent, native glass — without touching your hotkeys
or window-management logic. It also works standalone as a CLI (glasshud toast "hello"), so any script or app can throw a native glass HUD on screen.
- Real Liquid Glass (
NSGlassEffectView) on macOS 26+, automaticNSVisualEffectViewfallback below. The active backend is reported honestly viaglasshud provider— it never claims glass when it isn't. - Three styles:
toast(small, auto-dismiss),panel(titled, persistent), andhelp(wide, monospaced cheat-sheet with aligned shortcut columns). - Non-activating floating panels — showing a toast never steals focus. They ride across Spaces and appear over fullscreen apps.
- Identified windows — address HUDs by
--idto show, update in place, and hide individually. - Adapts to light/dark automatically (semantic AppKit colors, real
NSTextFieldtext — no webview). - Lightweight background agent (
LSUIElement, no Dock icon) with a tiny CLI client. Non-blocking, single-instance, auto-spawns on demand, and optionally autostarts at login via alaunchdLaunchAgent.
your caller (Hammerspoon Lua, a shell script, anything)
│ glasshud toast/show/update/hide ...
▼
glasshud (CLI client) ── JSON over a Unix socket ──▶ GlassHUD.app (agent)
NSPanel + Backdrop
→ NSGlassEffectView
(or NSVisualEffectView)
- One Swift binary, two modes: the agent (
glasshud --serve) and the client (glasshud <command>). The client auto-spawns the agent if needed and returns immediately, so callers never block. - Commands are one-line JSON over
~/.local/state/glasshud/glasshud.sock. - Toasts auto-dismiss (~1.6 s default); panels and help persist until hidden.
- Single instance guaranteed by a
flocklock;SIGPIPEignored so short-lived client probes can't take the agent down.
- macOS 13+ (real glass needs macOS 26+ / Tahoe; older systems get the frosted fallback).
- Xcode Command Line Tools (
swiftc) — full Xcode is not required:xcode-select --install
- Hammerspoon only if you want the Lua integration; the CLI works on its own.
Clone anywhere (the ~/.hammerspoon/GlassHUD location is recommended if you use
Hammerspoon), then run the installer:
git clone https://github.com/renner-c/GlassHUD.git ~/.hammerspoon/GlassHUD
cd ~/.hammerspoon/GlassHUD
./install.shinstall.sh builds GlassHUD.app, installs the glasshud CLI shim into
~/.local/bin, installs the Hammerspoon adapter to ~/.hammerspoon/glasshud.lua
(pointed at your checkout), and loads the login LaunchAgent.
Add ~/.local/bin to your PATH for the bare glasshud command:
export PATH="$HOME/.local/bin:$PATH"To rebuild only (no install): ./build.sh.
glasshud toast "Hello from GlassHUD"
glasshud show --id note --title "Reminder" --body "Standup at 10am"
glasshud update --id note --title "Reminder" --body "Standup now"
glasshud hide --id note
glasshud hideAll
glasshud help-panel --body $'Launch\ncmd+enter terminal' # wide cheat-sheet
glasshud provider # print the active backend (glass vs. fallback)
glasshud ping # exit 0 if the agent is running
glasshud helpOptions (glasshud help for the full list):
--id ID window identity (default "hud"); reused for update/hide
--title T panel title (bold)
--body B body text (\n for line breaks)
--style S toast | panel | help
--duration SECS auto-dismiss after SECS (<=0 or omitted on panels = persistent)
--mono force a monospaced body
--click-through let mouse events pass through
--anchor A toast | help | center | top | bottom | topRight
--x --y --w --h explicit placement; x/y are top-left GLOBAL pixels
--screen-x/y/w/h visible frame of the screen to draw on (top-left coords)
--tint #RRGGBB tint the glass
--json PATH read the whole command payload from a JSON file
The installer drops a glasshud.lua adapter into ~/.hammerspoon/. It calls the
binary through hs.task (non-blocking), so your config never stalls.
local glasshud = require("glasshud")
glasshud.toast("hi") -- auto-dismiss glass toast
glasshud.show("note", "Reminder", "Standup at 10am") -- persistent titled panel
glasshud.update("note", "Reminder", "Standup now") -- update in place
glasshud.hide("note")
glasshud.helpPanel("help", body) -- wide monospaced cheat-sheet
glasshud.hideAll()To upgrade an existing setup, point your current popup function at the adapter.
For example, if your config shows messages through a say()/showPopup() helper,
reroute its body to glasshud.toast(...) (or glasshud.render{...} for panels)
and keep the same function name and call sites — every trigger and message stays
the same, only the rendering changes. Wrapping such edits in
-- BEGIN GlassHUD integration / -- END GlassHUD integration markers keeps them
easy to find and revert.
The adapter positions HUDs on the focused window's screen and reproduces a "one popup at a time" feel by funneling toasts through a shared window id.
If you use the Amethyst tiling window
manager, lua/amethyst_glass.lua replaces Amethyst's built-in layout HUD (the
little "Tall / Wide / Row…" popup) with a native glass toast.
Amethyst has no plugin hook, so the module watches Amethyst's own layout
keyboard shortcuts with a non-consuming hs.eventtap — the keypress still
reaches Amethyst, which performs the real layout change — and shows a glass
toast with the layout name. It reads Amethyst's shortcuts and layout order live
from its preferences (so rebinding stays in sync) and forces Amethyst's own HUD
off (enables-layout-hud), re-applying it whenever Amethyst relaunches, so you
get a single toast.
Enable it by requiring the module from your ~/.hammerspoon/init.lua (the
installer copies it into ~/.hammerspoon/):
pcall(require, "amethyst_glass")Then reload Hammerspoon and press your Amethyst layout keys (e.g. ⌥⇧Space to
cycle). Notes:
- The direct
select-*-layoutshortcuts always show the exact layout name. - The cycle shortcut tracks the layout order; it's exact once you've pressed any select shortcut or completed a full cycle (Amethyst exposes no way to read the current layout, so the very first cycle after a reload is best-effort).
- Requires Hammerspoon's Accessibility permission (which it already needs for window management).
install.sh installs a per-user LaunchAgent at
~/Library/LaunchAgents/com.glasshud.agent.plist and loads it immediately:
RunAtLoad— starts at every login, in the GUI (Aqua) session, independent of Hammerspoon.KeepAlive(SuccessfulExit = false) — relaunches on a crash, but respects a clean exit (so the single-instance lock andglasshud quitbehave, no restart loop).
It's also spawned on demand, so it works even before the first reboot. Manage it:
launchctl print gui/$(id -u)/com.glasshud.agent # status
launchctl kickstart -k gui/$(id -u)/com.glasshud.agent # restart (e.g. after rebuild)
launchctl bootout gui/$(id -u)/com.glasshud.agent # stop + unload./uninstall.shUnloads and removes the LaunchAgent, stops the agent, and removes the CLI shim,
the Hammerspoon adapter, and runtime state. It leaves the project folder (delete
it yourself) and any edits you made to your own init.lua.
- macOS 26+ (Tahoe): real
NSGlassEffectViewLiquid Glass. - macOS 13–25:
NSVisualEffectViewfrosted fallback — same layout and behavior, just the public blur instead of glass. - Apple Silicon and Intel (pure AppKit).
- No private APIs, no SIP changes, no entitlements. The agent draws its own windows and needs no special permissions. (Hammerspoon's own Accessibility permission, for its window management, is unrelated to GlassHUD.)
Sources/
Protocol.swift # command model, paths, socket addr, logging
Backdrop.swift # glass vs. visual-effect provider
HUDContent.swift # AppKit text layout (toast / panel / help)
HUDWindow.swift # non-activating NSPanel
HUDController.swift # window registry, positioning, auto-dismiss
Server.swift # agent: socket + singleton lock + NSApp
Client.swift # CLI parsing + agent spawn + send
main.swift # mode dispatch (--serve vs. client)
lua/glasshud.lua # Hammerspoon adapter (installed to ~/.hammerspoon)
lua/amethyst_glass.lua # optional Amethyst layout-HUD integration
build.sh install.sh uninstall.sh test.sh
Runtime files live in ~/.local/state/glasshud/ (glasshud.sock,
glasshud.lock, glasshud.log, launchd.log). The log records each window's
frame, visibility, and the active glass backend — handy for debugging.
Run ./test.sh for a manual smoke test of every style.
Sources/HUDWindow.swift→ per-style corner radius, window level/shadow.Sources/HUDContent.swift→ fonts, sizes, spacing, padding, help-row styling.Sources/Backdrop.swift→ glassstyle(.regular/.clear), tint, and the fallback materials.
Rebuild with ./build.sh, then restart the agent
(launchctl kickstart -k gui/$(id -u)/com.glasshud.agent).
MIT — see LICENSE.