🌐 Website: spookiui.rooksnet.uk
A live configurator for the Ghostty terminal. Browse and edit every option Ghostty supports from an interactive terminal UI, and watch your changes apply live — when you run it inside a Ghostty window, the very terminal you're in repaints as you edit.
./spookiui.py
Requires Python 3.8+ (standard library only — no pip installs) and the
ghostty binary on your PATH (or in /Applications/Ghostty.app).
This started as a Claude project — a quick way to see the idea working. It didn't stay one. After 20-odd years writing software, I pulled it back to the keyboard and now maintain it by hand, the old-school way: I read every line, I own every line. Claude helped it exist; the craft since then is mine.
SpookiUI has no third-party Python dependencies — it's a single script that runs on the Python 3.8+ standard library. You can run it straight from the repo:
git clone https://github.com/mattj85/SpookiUI.git
cd SpookiUI
./spookiui.pyOr run the installer (macOS & Linux) to check prerequisites and put a spookiui
command on your PATH:
./install.sh # installs to ~/.local/bin
PREFIX=/usr/local ./install.sh # system-wide (may need sudo)The installer verifies Python 3.8+, checks for the ghostty binary (warning
with install hints if it's missing), and symlinks spookiui.py into your bin
directory. After it runs, spookiui and spookiui --help work from anywhere.
To reverse it, run the uninstaller (pass the same PREFIX you installed with):
./uninstall.sh # remove the `spookiui` command
PREFIX=/usr/local ./uninstall.sh # if you installed there
./uninstall.sh --purge # also delete SpookiUI's cache + saved profilesIt only removes the symlink it recognises as its own (never a file it didn't
create, and never a Homebrew install — use brew uninstall for that), and
leaves your Ghostty config and the repo untouched.
A Homebrew formula lives in homebrew/spookiui.rb. Once
it's published to a tap repo named homebrew-spookiui, install with:
brew install mattj85/spookiui/spookiuiHomebrew installs are updated with brew upgrade spookiui; the in-app updater
detects a Homebrew install and defers to it.
Ghostty is configured through a plain-text file (~/.config/ghostty/config) and
cannot auto-reload on file change — you have to trigger a reload yourself.
SpookiUI closes that loop: it writes the config file and triggers a Ghostty
reload for you — clicking the Reload Configuration menu item on macOS, or
sending the running process the SIGUSR2 signal it reloads on under Linux —
so editing feels live. It runs on both macOS and Linux.
Every option is discovered dynamically from your installed Ghostty
(ghostty +show-config --default --docs), so the tool always matches your
version — nothing is hard-coded. On this machine that's ~200 options across 13
categories. Options that only apply to the other operating system (macOS-only
settings on Linux, GTK/X11 settings on macOS) are hidden automatically so you
only ever see what's relevant to the machine you're on.
you edit a value ─▶ SpookiUI writes the config file
│
├─▶ validates it with `ghostty +validate-config`
│ (an invalid value is rejected & rolled back)
│
└─▶ reloads Ghostty ─▶ your terminal repaints
(macOS: "Reload Configuration" menu item via
AppleScript · Linux: SIGUSR2 to the process)
- Safe: every change is validated by Ghostty itself before it's saved. Bad values never reach your config.
- Reversible: a dated backup (
config.spookiui.YYYYMMDD.bak) is made on the first change of the day, the TUI can revert an entire session withR, and you can wipe the config back to Ghostty's built-in defaults withX(a backup is still kept). - Live preview: while picking a theme, font, or enum value, each highlighted option is applied as you scroll — cancel and it snaps back to where you were.
Ghostty can't watch its config file for changes, so SpookiUI triggers the reload for you. How that happens — and what it needs — depends on your OS:
| Platform | How the reload fires | Requirements |
|---|---|---|
| macOS | Clicks the Reload Configuration menu item via AppleScript (osascript) |
Ghostty must be running; your terminal needs Accessibility permission (System Settings → Privacy & Security → Accessibility). Ghostty is located on PATH or at /Applications/Ghostty.app. |
| Linux | Sends SIGUSR2 to the running Ghostty process(es), which Ghostty reloads on |
Ghostty must be running; pgrep (from procps/procps-ng, present on essentially every distro) is used to find it. No extra permission needed. Works on any distribution — detection is generic (sys.platform), with no distro-specific code. |
| Other | No auto-reload — the file is still written and validated | Trigger your own reload_config keybind in Ghostty to apply. |
On Linux, Ghostty is found via PATH (shutil.which), falling back to
/usr/bin/ghostty and /usr/local/bin/ghostty. Only Python 3.8+ (standard
library) and the ghostty binary are required; live reload additionally needs
pgrep and a running Ghostty instance.
If a reload can't be triggered (Ghostty isn't running, missing permission, or an
unsupported platform), your change is still written and validated safely —
SpookiUI just tells you to reload manually. A few options (e.g. language) can't
be applied without a restart at all; the UI flags these as needs restart /
new windows only so there are no surprises.
SpookiUI · live Ghostty configurator AUTO-APPLY:ON · live
Colors & Theme │ ● theme Catppuccin Mocha │ theme
Font │ background #1e1e2e │ type: theme
Cursor │ foreground #cdd6f4 │ value: Catppuccin…
Window │ ● background-opacity 0.95 │ ─ docs ───────────
… │ … │ Set the color …
| Key | Action |
|---|---|
↑/↓ or j/k |
move · Tab switch pane |
→/Enter |
into options / edit the selected option |
← |
back to categories |
/ |
search all options by name or documentation |
u |
reset the selected option to its default |
a |
toggle auto-apply (live ↔ staged) |
s |
save + reload now · r re-trigger reload |
R |
revert everything to session start |
X |
wipe config & restore all Ghostty defaults (backup kept) |
U |
update SpookiUI in place to the latest release |
p |
profiles — save / load / delete named configs · t toggles light↔dark |
c |
config check (doctor) — health-check for issues |
v |
utils — one-shot fixes (e.g. Fix SSH) |
t |
treats — fun animated background shaders (all off by default) |
d |
show everything you've changed |
? |
help · q quit |
When your terminal font is a Nerd Font, each category in the left column
gets an icon (palette, font, cursor, apple/linux, …). SpookiUI detects this from
Ghostty's font-family; if no Nerd Font is set it shows a one-time note on how
to install one for your platform and then runs without icons. Force it either
way with SPOOKIUI_ICONS=1 / SPOOKIUI_ICONS=0.
Editors are typed to each option:
- booleans toggle instantly
- enums / font open a searchable picker with live preview, listing every
valid choice Ghostty documents (e.g. all 11
macos-iconstyles) - theme opens the picker with a live colour card for the highlighted theme — its 16-colour palette and a foreground-on-background sample, rendered right beside the list so you see a theme before applying it
- bounded numbers (opacity,
minimum-contrast, …) open a visual slider —←/→to adjust,PgUp/PgDnfor larger jumps,Home/Endfor the ends, all previewed live - other numbers step with
↑/↓or+/-, or type a value - colors / text take a typed value (
#rrggbbor a named color); colours show a swatch, and colour options preview the active palette in the detail pane - keybindings open a guided builder — toggle modifiers (
super/ctrl/alt/shift, wheresuperis ⌘ on macOS), press or pick the key, and choose the action from Ghostty's own action list; the result is validated before it's added - other lists (
palette,env, font fallbacks, …) get an add/edit/delete editor. It only ever saves your entries — Ghostty's own defaults are listed below them, read-only, andccopies one down into your list to tweak
Auto-apply off stages your edits in memory instead of touching disk; press
s to write + reload them all at once.
On macOS you can also restyle the app icon from here: pick a macos-icon
style (official, blueprint, chalkboard, microchip, glass,
holographic, paper, retro, xray, …) and, with custom-style, tweak
macos-icon-frame plus the macos-icon-ghost-color / macos-icon-screen-color
(which get live swatches). See the icon gallery at
https://noahskelton.github.io/ghostty-icons/.
Profiles are named snapshots of your whole config — press p in the TUI (or
use spookiui profile …) to save the current setup, then load it back later.
Save a light and a dark profile and the t key (or spookiui profile toggle) flips between them instantly. Loading a profile is validated and backs
up your current config first, like every other change. Profiles live in
$XDG_DATA_HOME/spookiui/profiles (~/.local/share/… by default), outside
Ghostty's own config dir so it never reads them.
spookiui doctor (or c in the TUI) health-checks your config and reports:
invalid settings, unknown/typo'd options, options set more than once (dead
lines), settings that just repeat a default, and keybind triggers bound twice or
shadowing a Ghostty default. Findings are grouped by severity; it exits non-zero
when there are errors, so it drops cleanly into a pre-commit hook for dotfiles.
The Utils category (last entry in the left pane, or press v anywhere)
collects small, one-shot maintenance actions that aren't Ghostty config options.
Highlight an action to read what it does; press Enter/→ to run it.
Ghostty tells programs it is xterm-ghostty (via the TERM variable). When
you SSH into another machine, that host looks xterm-ghostty up in its own
terminfo database — and most remote boxes have never heard of it. The remote
shell then misbehaves: garbled or dead keys, missing colour, broken
clear/tput, or the classic Error opening terminal: xterm-ghostty.
Fix SSH adds one line to your shell rc (~/.zshrc or ~/.bashrc, whichever
matches your login shell):
alias ssh="TERM=xterm-256color ssh"so the ssh command runs with xterm-256color — a terminfo entry
essentially every host already ships. Your local Ghostty session keeps its full
xterm-ghostty features; only the outbound SSH connection is downgraded to the
universally-understood value. Nothing on the remote host is changed.
It's safe and idempotent: it first scans your shell rc files (.zshrc,
.bashrc, .bash_profile, …) and does nothing if the alias is already there;
otherwise it appends the line and syntax-checks the file. Because a running shell
can't be modified from outside, it then tells you to source the file or open a
new terminal for the alias to take effect. To undo, delete the alias line. (A
more thorough alternative is copying Ghostty's terminfo to each host, but this
alias is the quick fix that needs no remote access.)
Run it from the CLI too:
./spookiui.py fix-ssh # add the alias if it's missing
./spookiui.py fix-ssh --check # report whether it's present; change nothing
./spookiui.py fix-ssh --explain # print the full what/why, then exitThe Treats category (press t anywhere, or find it in the left pane)
bundles a handful of nostalgic, animated background shaders. Ghostty can run a
ShaderToy-style GLSL shader behind the terminal grid; a treat is one SpookiUI
writes for you into <ghostty-config-dir>/shaders/spookiui/ and toggles on via
custom-shader.
Current treats:
- Matrix Rain — falling green glyph columns,
cmatrix-style - Pipes — a neon homage to the Win95 3D Pipes screensaver
- Mystify — bouncing, colour-trailing polygons (the Windows Mystify saver)
- Plasma — a rolling demoscene / After Dark plasma field
- Lava Lamp — slow rising metaball blobs (a 70s lava lamp / Bubbles saver)
- Fireworks — rockets bursting into fading, gravity-pulled sparks
- Chomper — a chomping wedge eats a row of pellets, chased by a ghost (a nod to the 1980 maze arcade classic)
- Barrels — barrels tumble down slanted girders (a wink at the 1981 platform arcade classic)
- Jumper — a hero hops beneath scrolling question-blocks (a nostalgic nod to side-scrolling platformers)
Seasonal packs:
- Ghosts (Halloween) — friendly cartoon ghosts drift by, bobbing and fading
- Spooky Eyes (Halloween) — pairs of glowing eyes blink open in the dark
- Cobwebs (Halloween) — faint spider webs shimmer across the corners
- Snow (Winter) — soft snowflakes drift down across a few depth layers
The game-inspired treats (Chomper / Barrels / Jumper) are drawn from plain shapes only — they evoke the vibe of those classics without using any game artwork, in the same "homage" spirit as the Pipes and Mystify treats. SpookiUI gently points out the pack that fits the season (e.g. the Halloween treats in October) in the treats list and overlay — nothing is auto-enabled.
They're designed to stay out of your way:
-
All off by default — nothing is enabled unless you ask.
-
One at a time — only a single treat can be active; enabling one turns off whichever was on, so background effects never stack.
-
Text stays legible — every treat composites additively through a tight luminance mask, so only the darkest background pixels are tinted; your text, cursor, and borders are left alone. Brightness is kept deliberately low.
-
Light on resources — SpookiUI sets
custom-shader-animation = true, which animates only the focused window. Open (and focus) another terminal and the treat pauses in the ones you're no longer looking at, so at most one window is ever animating. Each treat is also deliberately cheap — one texture fetch and small, fixed per-frame work. -
Non-destructive — treats are namespaced under
shaders/spookiui/, so anycustom-shaderyou added yourself is preserved, and toggling follows the same validate → back up → write → reload → rollback discipline as every other edit. -
Adjustable vibrancy — a single 0–100% control fades the active treat's animation up or down (100% is the tuned default, 0% is invisible). Pick the Vibrancy row in the treats overlay to open the slider (or press
v), or usetreats vibrancy <0-100>from the CLI. It's a global preference, so it applies to whichever treat you turn on. -
Dim unfocused windows — the treat animation only ever plays in the focused window; this setting controls what the others show. On, unfocused windows dim + desaturate so the active one stands out (a tiny no-op shader handles it even with no treat running); off, they show the plain terminal. Toggle it from the Dim unfocused row in the overlay or
treats dim on|off, and it persists across sessions. Requires a Ghostty build that exposes the focus shader uniform.
Toggle them in the TUI (Space/Enter, v for vibrancy), or from the CLI:
./spookiui.py treats list # show treats, vibrancy, dim, and which one is on
./spookiui.py treats enable matrix-rain # make this the active treat (replaces any other)
./spookiui.py treats disable pipes # turn the active treat off
./spookiui.py treats only plasma # same as enable — one treat at a time
./spookiui.py treats clear # turn the treat off
./spookiui.py treats vibrancy 60 # fade the animation to 60% (blank prints current)
./spookiui.py treats dim off # stop dimming unfocused windows (blank prints current)Everything the TUI does is also available non-interactively:
./spookiui.py list [category] # list options (* = changed from default)
./spookiui.py list [category] --all # include options for the other OS
./spookiui.py get <key> # print an option's current value
./spookiui.py doc <key> # show an option's documentation + choices
./spookiui.py set <key> <value>… # set (writes + reloads live); repeat value for lists
./spookiui.py set <key> <v> --no-reload # write without reloading
./spookiui.py reset --yes # clear config & restore all Ghostty defaults (backup kept)
./spookiui.py version # print version & check GitHub for a newer release
./spookiui.py update # update in place to the latest release (git pull or download)
./spookiui.py profile save <name> # snapshot the current config as a named profile
./spookiui.py profile load <name> # apply a saved profile (validated, backed up)
./spookiui.py profile list # list saved profiles (also: show / delete / toggle)
./spookiui.py profile toggle # flip between the 'light' and 'dark' profiles
./spookiui.py doctor # health-check the config (duplicates, unknown keys, keybind clashes…)
./spookiui.py fix-ssh # fix garbled SSH sessions (adds a TERM=xterm-256color ssh alias)
./spookiui.py fix-ssh --check # report whether the SSH alias is present; change nothing
./spookiui.py treats list # fun background shaders (also: enable / disable / only / clear)
./spookiui.py reload # trigger a live reload
./spookiui.py validate # validate the current config
./spookiui.py themes # list installed themes
./spookiui.py fonts # list monospace font families
./spookiui.py path # print the config file in useExamples:
./spookiui.py set theme "Catppuccin Latte"
./spookiui.py set font-size 15
./spookiui.py set font-family "JetBrains Mono" "Symbols Nerd Font" # primary + fallback
./spookiui.py doc background-opacity- Live reload works on macOS and Linux (and degrades safely elsewhere) — see Live reload by platform above for the per-OS mechanism and requirements.
- Edits to single-value options are made in place, preserving your file's
comments and layout. New options and list options are written under a
clearly-marked
# added by SpookiUIsection. - The config path is auto-detected (
$XDG_CONFIG_HOME/ghostty/config, then~/.config/ghostty/config, then the macOS app-support path).
On startup SpookiUI quietly checks GitHub for a newer release. If one exists, the
TUI shows a UPDATE vX.Y.Z badge in the header (and press U to update on the
status line); the help screen (?) always shows your current version. Run
spookiui version any time to check on demand.
The check is best-effort and non-blocking — it runs on a background thread,
times out quickly, and stays silent if you're offline or GitHub is unreachable.
The result is cached for a day (under $XDG_CACHE_HOME/spookiui/) so it never
hammers the API. To turn it off entirely, set SPOOKIUI_NO_UPDATE_CHECK=1.
Press U in the TUI, or run spookiui update. No update server is involved —
GitHub is the source, and SpookiUI is a single file, so updating just swaps that
file:
- Git checkout (the default
install.shlayout) → it runsgit pullfor you. - Standalone copy → it downloads the latest release's
spookiui.py, verifies it compiles, then atomically replaces the file (keeping a.prevbackup). A truncated or bad download can never leave you with a broken tool. - No write permission (e.g. installed system-wide as root) → it tells you the exact command to run instead of failing silently.
Restart SpookiUI afterwards to run the new version.
Maintainers: notifications and updates only pick up a version once a matching
GitHub Release is published — see RELEASING.md for the
bump-and-release flow.
MIT — see LICENSE.
