v0.15.3 Client and server: a new window in 0.1 s

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.

you@linux:~/src
โ•ญโ”€ โ”‚ ~/src โœ” โ”‚ at 16:24:55 โ”€โ•ฎ
โ•ฐโ”€
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.

Ctrl + Shift + L

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.

Ctrl + Shift + B

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.

Ctrl + Shift + N

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.

search
[info] worker 3 started
[info] request GET /health 200 in 1ms
[info] worker 4 started
[info] request GET /health 200 in 2ms

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_one tree: Pty, Screen, Parser.
  • Every window is its own Desk: crash one and the others never notice.
  • kill -9 the renderer and its Desk starts a new Window. The screen heals.

Processes killed: 0

iex(guiltty@127.0.0.1)1> :observer
  • 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>
log

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.

~/.config/guiltty/config.exs
# the last expression is the 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

shell
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.

2 Clone it and run the installer

shell
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.

  1. 0.1

    First light

    Oct 29

    The whole pipeline: a PTY NIF, a parser, the screen in Elixir and an OpenGL renderer. ASCII only, 80ร—24.

  2. 0.2

    Color

    Oct 29

    16, 256 and 24-bit color, Paul Williams' DEC parser, and a resizable window.

  3. 0.3

    The vim fight

    Oct 29

    Alternate screen, scroll regions, mouse reporting. vim, less, htop, btop and tmux work.

  4. 0.4

    The BEAM advantage

    Oct 29

    Tabs, self-healing sessions, detach and reattach, live config, backpressure.

  5. 0.5

    Daily driver

    Oct 30

    Unicode, HarfBuzz shaping, color emoji, 10,000 rows of scrollback, search, HiDPI.

  6. 0.6

    Flat tabs

    Oct 30

    The flat tab bar, named themes, cursor and window settings, the cheatsheet.

  7. 0.7

    The installer

    Oct 30

    ./install.sh, a menu entry and the logo.

  8. 0.8

    Lockable tabs

    Oct 30

    Lock a tab and it outlives the window.

  9. 0.9

    Broadcast

    Oct 30

    Type once into every tab in the group, without typing into your own machine by mistake.

  10. 0.10

    Vulkan

    Oct 30

    A Vulkan 1.3 renderer, pixel-for-pixel the same as OpenGL, with automatic fallback.

  11. 0.11

    Ligatures

    Oct 30

    With a font that has them, |>, ->, != and === are drawn as one symbol. Cleaner tabs, too.

  12. 0.12.4

    Security

    Oct 30

    Hardened C, a localhost-only node with a private cookie, a pidfd for the shell, and titles that can't fake a locked tab.

  13. 0.13

    Multi window

    Oct 30

    Several windows, each with its own tabs, locked tabs and broadcast group. Ctrl+Shift+N opens one.

  14. 0.14

    Performance

    Oct 31

    Floods of short lines show up 3x faster, startup is 100 ms sooner, and make bench measures it all.

  15. 0.15.3

    Client and server

    Oct 31

    guiltty is a small client that asks the running guiltty for a window, like emacsclient: 0.1 s.

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.