Kill it. Crash it.
Your tabs are still there.
guiltty is a Linux-only, GPU-accelerated terminal emulator written in Elixir and C. Every tab is a supervised process tree and the renderer is its own OS process, so when a part crashes, its supervisor restarts it and your tabs stay.
- milestones implemented
- 15
- milestones implemented
- renderers, the same pixels
- 2
- renderers, the same pixels
- rows of scrollback
- 10,000
- rows of scrollback
- book chapters on how it works
- 24
- book chapters on how it works
How it works
Elixir does the thinking. C draws the pixels.
Your shell writes bytes into a pseudoterminal. A NIF hands them to the BEAM, where a parser and a screen, plain Elixir processes, turn them into cells. The renderer is a separate program that only draws: if it dies, guiltty starts a new one and your tabs are still there.
Your shell
bash, zsh, fish: your login shell
the BEAM
PTY NIF
C ยท enif_select
Parser
DEC state machine
Screen
grid, scrollback
Renderer
its own process ยท Vulkan or GL
Features
A daily driver, with the BEAM underneath.
The things you expect from a modern terminal, and a few that come almost for free with a supervision tree.
Vulkan 1.3, or OpenGL. Same pixels.
guiltty draws with Vulkan by default. No driver? It falls back to OpenGL by itself and says so in the tab bar. A test replays recorded frames through both and checks they match, pixel for pixel.
Lockable tabs
Lock a tab and close its window: it keeps running, and the next
guiltty
brings that window back with it.
Safe broadcast
Type once into every tab in the group. If a connection drops, typing goes nowhere, so a command meant for your servers doesn't land on your own machine.
Unicode, properly
Grapheme clusters, wide characters, HarfBuzz shaping, ligatures, fallback fonts, color emoji and Nerd Font icons that spill into the next cell.
Live config, in Elixir
config.exs
is plain Elixir, applied to the open window on save. Mistakes show in red in the tab
bar, with a suggestion for misspelled settings.
Full-screen programs
Alternate screen, scroll regions, mouse reporting, bracketed paste, cursor shapes. Tested with vim, less, htop, btop and tmux.
Many windows, one guiltty
Each window has its own tabs, locked tabs and broadcast group. Closing one never touches the others; guiltty stops after the last.
A window in 0.1 s
guiltty
is a small client that asks the running guiltty for a window over a private socket,
like emacsclient. About 0.1 s with guiltty running, 0.7 s from
cold, on the developer's machine.
Measured, then faster
Floods of short lines (seq, logs) land about 3x faster
than in v0.13, laid down together and scrolled once. Heavy output is still slow: a big
cat
runs at 3 to 4 MB/s, and make bench
says so.
Scrollback, search, selection
10,000 rows per tab, searched with Enter and Shift+Enter. Select to copy to the primary selection, middle click to paste. Backpressure from the shell to the screen means a flood of output can't outrun the screen.
Self-healing
Go on. Kill something.
A simulation of guiltty's real supervision tree. Click a process to kill it and watch what its supervisor restarts. In the real thing, you'd barely notice.
-
Each tab is its own
rest_for_onetree: Pty, Screen, Parser. - Every window is its own Desk: crash one and the others never notice.
-
kill -9the renderer and its Desk starts a new Window. The screen heals.
Processes killed: 0
- Guiltty.Supervisor <0.212.0>
- Guiltty.Registry <0.219.0>
- Guiltty.Config <0.226.0>
- Guiltty.Sessions <0.233.0>
- <0.240.0>
- <0.247.0>
- <0.254.0>
- <0.261.0>
- Session.Supervisor 2 <0.268.0>
- Guiltty.Desks <0.275.0>
- <0.282.0>
- <0.289.0>
- Guiltty.Window <0.296.0>
- pid 9393
- Desk 2 <0.310.0>
- Guiltty.Lifeline <0.317.0>
Themes
Built-in themes. Or bring your own.
Pick one by name, or start from one and change single colors. Every terminal on this page follows your pick.
Tomorrow Night is what guiltty uses without a config.
[
font: [family: "JetBrainsMono Nerd Font", size: 14],
ligatures: true,
theme: "tomorrow-nightcatppuccin-mochadraculagruvbox-darksolarized-dark",
cursor: [style: :block, blink: false],
renderer: :vulkan
]
Keys
Rebind any of them.
The defaults, as they ship; paste (Ctrl+Shift+V) is the one fixed key. Forgot one? Ctrl + Shift + / opens the cheatsheet, built from your own config.
- New window
- Ctrl + Shift + N
- New tab
- Ctrl + Shift + T
- Close tab
- Ctrl + Shift + W
- Previous tab
- Ctrl + PgUp
- Next tab
- Ctrl + PgDn
- Scroll back
- Shift + PgUp
- Search the scrollback
- Ctrl + Shift + F
- Copy
- Ctrl + Shift + C
- Paste
- Ctrl + Shift + V
- Zoom in
- Ctrl + =
- Zoom out
- Ctrl + -
- Reset zoom
- Ctrl + 0
- Open the config file
- Ctrl + ,
- The cheatsheet: every key
- Ctrl + Shift + /
- Lock the tab ๐
- Ctrl + Shift + L
- Broadcast tabs
- Ctrl + Shift + B
Install
Built on your machine. Runs without Elixir.
guiltty builds from source, so you need Elixir 1.20 and GCC 15+ once. The installed copy carries its own Erlang runtime and needs no root.
1 What to install first
sudo zypper install gcc make pkg-config SDL3-devel freetype2-devel \
fontconfig-devel harfbuzz-devel vulkan-devel shaderc elixir erlang-epmd
The one guiltty is developed on.
sudo dnf install gcc make pkgconf-pkg-config SDL3-devel freetype-devel \
fontconfig-devel harfbuzz-devel vulkan-loader-devel vulkan-headers glslc elixir
Fedora 43 and 44 have everything but Elixir 1.20: leave out elixir and get it from asdf.
sudo apt install build-essential pkg-config libsdl3-dev libfreetype-dev \
libfontconfig-dev libharfbuzz-dev libvulkan-dev glslc
26.04's Elixir is 1.18: get Elixir 1.20 and Erlang from asdf. From 26.10 on, add elixir erlang-dev instead.
sudo pacman -S --needed base-devel sdl3 freetype2 fontconfig harfbuzz \
vulkan-icd-loader vulkan-headers shaderc elixir
Everything is in the official repositories.
2 Clone it and run the installer
git clone https://github.com/z7ealth/guiltty.git
cd guiltty
./install.sh
Update
git
pull
&&
./install.sh
Uninstall
./install.sh
--uninstall
Elsewhere
PREFIX=/opt/guiltty
./install.sh
3 Run it
Type guiltty
or find it in your applications menu. It needs a GPU with OpenGL 3.3, and draws with
Vulkan where there's a 1.3 driver. It looks best with a Nerd Font, Noto Color Emoji
and a font for Chinese, Japanese and Korean.
The build
Implemented in milestones.
Each version is one milestone of the build plan, from the first byte through a PTY to a client and server. The devlog tells how each one went.
-
0.1
First light
Oct 29The whole pipeline: a PTY NIF, a parser, the screen in Elixir and an OpenGL renderer. ASCII only, 80ร24.
-
0.2
Color
Oct 2916, 256 and 24-bit color, Paul Williams' DEC parser, and a resizable window.
-
0.3
The vim fight
Oct 29Alternate screen, scroll regions, mouse reporting. vim, less, htop, btop and tmux work.
-
0.4
The BEAM advantage
Oct 29Tabs, self-healing sessions, detach and reattach, live config, backpressure.
-
0.5
Daily driver
Oct 30Unicode, HarfBuzz shaping, color emoji, 10,000 rows of scrollback, search, HiDPI.
-
0.6
Flat tabs
Oct 30The flat tab bar, named themes, cursor and window settings, the cheatsheet.
-
0.7
The installer
Oct 30./install.sh, a menu entry and the logo.
-
0.8
Lockable tabs
Oct 30Lock a tab and it outlives the window.
-
0.9
Broadcast
Oct 30Type once into every tab in the group, without typing into your own machine by mistake.
-
0.10
Vulkan
Oct 30A Vulkan 1.3 renderer, pixel-for-pixel the same as OpenGL, with automatic fallback.
-
0.11
Ligatures
Oct 30With a font that has them, |>, ->, != and === are drawn as one symbol. Cleaner tabs, too.
-
0.12.4
Security
Oct 30Hardened C, a localhost-only node with a private cookie, a pidfd for the shell, and titles that can't fake a locked tab.
-
0.13
Multi window
Oct 30Several windows, each with its own tabs, locked tabs and broadcast group. Ctrl+Shift+N opens one.
-
0.14
Performance
Oct 31Floods of short lines show up 3x faster, startup is 100 ms sooner, and make bench measures it all.
-
0.15.3
Client and server
Oct 31guiltty is a small client that asks the running guiltty for a window, like emacsclient: 0.1 s.
The book
Learn how a terminal works, from the kernel up.
guiltty comes with a book: twenty-four chapters with diagrams, one for every part, from pseudoterminals to the client and server.
- 00 Overview: what a terminal emulator actually is
- 01 Pseudoterminals: the kernel half of a terminal
- 02 The PTY NIF: a pseudoterminal inside the BEAM
- 03 The parser: bytes in, commands out
- 04 The screen and the grid
- 05 The port protocol: Elixir and the renderer
- 06 The renderer: from cells to pixels
- 07 Input: from a key press to bash
- 08 Colors: from SGR to pixels
- 09 Resize: from a dragged window to SIGWINCH
- 10 Testing a terminal with real bytes
- 11 Full-screen programs: what vim needs from a terminal
- 12 The BEAM advantage: tabs, crashes, detaching, live config
- 13 Unicode: bytes, characters, clusters and columns
- 14 Fonts and the atlas: every glyph, from any font
- 15 Scrollback, search and the clipboard
- 16 Configuration: every setting, from file to pixels
- 17 Installing: the release, the menu entry, the icon
- 18 Broadcast: typing into many servers, safely
- 19 Vulkan: the same picture, with nothing hidden
- 20 Security: making the C hard to break
- 21 Multi window: every window its own desk
- 22 Performance: measure, then fix what the numbers say
- 23 Client and server: the guiltty command
Go on, try to break it.
Linux. Tried on openSUSE Tumbleweed, X11 and an AMD GPU; Wayland, Intel and NVIDIA should work but haven't been tried. Open source, MIT licensed.