Over two years ago, in one of my first public talks about Ghostty, I shared my vision for libghostty
: an embeddable library so any application can embed their own fully functional, modern, and fast terminal emulator. Libghostty is finally starting to take shape, and I’m excited to share more details about my plans for it.
The first libghostty library will be libghostty-vt
: a zero-dependency library that provides an API for parsing terminal sequences and maintaining terminal state. It doesn’t even require libc!
Why libghostty?
Let’s start with some background on why I believe libghostty must exist.
There are hundreds of programs that implement some form of terminal emulation. The most obvious are the actua…
Over two years ago, in one of my first public talks about Ghostty, I shared my vision for libghostty
: an embeddable library so any application can embed their own fully functional, modern, and fast terminal emulator. Libghostty is finally starting to take shape, and I’m excited to share more details about my plans for it.
The first libghostty library will be libghostty-vt
: a zero-dependency library that provides an API for parsing terminal sequences and maintaining terminal state. It doesn’t even require libc!
Why libghostty?
Let’s start with some background on why I believe libghostty must exist.
There are hundreds of programs that implement some form of terminal emulation. The most obvious are the actual general purpose terminal emulators like Ghostty, Kitty, iTerm2, etc. But terminal multiplexers like tmux or zellij are also full terminal emulators!1 Editors embed their own terminal emulators too, such as jediterm for IntelliJ products, Xterm.js for VS Code, or Alacritty in Zed.
In addition to fully functional terminal emulators, many websites and applications implement read-only terminal emulation to display logs or command output. For example, GitHub Actions output parses simple color sequences (but not much else). And hosting providers like Vercel or Render implement a simple form of terminal emulation allowing line clearing and redrawing within build logs as well as parsing colors.
Many of these implementations are ad-hoc, one-off solutions. They aren’t using any shared library or codebase.2 Terminal emulation is a classic problem that appears simple on the surface but is riddled with unexpected complexities and edge cases.3 As a result, most of these implementations are incomplete, buggy, and slow.4
Beyond correctness, implementing any form of terminal emulation is a waste of time for most developers. Terminal emulation is not the core business of IntelliJ, Visual Studio Code, GitHub, Vercel, Render, etc. It’d benefit them if they could have a stable, reusable solution that’s consistent everywhere.
My answer to this is libghostty: a cross-platform, minimal dependency library that exposes a C API so feature-rich, correct, and fast terminal functionality can be embedded by any application anywhere.
The Beginning: libghostty-vt
The first libghostty library will be libghostty-vt
: a zero-dependency (not even libc) library that provides an API for parsing terminal sequences and maintaining terminal state.
Parsing terminal sequences is the most core functionality of a terminal emulator, and is required by full terminal emulators like Ghostty down to simple read-only style-only views such as GitHub Actions or Vercel build output.
The state diagram might appear relatively simple at first glance, but an implementation is unexpectedly challenging to get right. For example, Jediterm doesn’t handle intermedites correctly, causing the widely supported “change cursor shape” sequence to swallow a character in every IntelliJ editor at the time of this post.
For style-only parsing, many developers skip the full state diagram. Instad, they do some light web searching, parse simple ANSI sequences such as \e[31
or \e[41m
, and claim “color support.” But style-only sequences are vastly more complex than that, for example they support RGB which itself can be in a dozen formats. And I still haven’t found a single web console that renders this complex style sequence correctly.5
libghostty-vt
aims to fix all of this. libghostty-vt
is the fast and robust terminal emulation core of Ghostty extracted into a single zero-dependency C API (it doesn’t even rely on libc), allowing it to be easily embedded into any popular language ecosystem.
If you’re willing to take on a libc dependency, you can conditionally compile libghostty-vt
with full SIMD support which improves parsing throughput by many multiples depending on the CPU architecture. For example, on Apple Silicon, plain ASCII sequences are 6x faster and sequences that contain multi-byte UTF-8 encodings are over 10x faster.
The Long Term
libghostty-vt
is just the beginning. Longer term, we will provide more libghostty-<x>
libs that expose additional functionality such as input handling (keyboard encoding is a big one), GPU rendering (provide us with an OpenGL or Metal surface and we’ll take care of the rest), GTK widgets and Swift frameworks that handle the entire terminal view, and more.
As fundamental pieces stabilize, we will continue to offer more and more functionality. These will be structured as a family of libraries to minimize dependency requirements, code size, and overall maintenance complexity.
libghostty-vt
Status
I just merged the pull request exposing libghostty-vt as a Zig module. This PR includes a minimal example program, too. If you’re a Zig developer, you can start experimenting with libghostty-vt
immediately.
The C API isn’t ready yet, but it is what I’m working on right now and it’ll be available for testing soon. All of the work required is defining the C API, since the core logic is of course all there and has been used by Ghostty for years. Plus, the Ghostty macOS app already consumes an internal-only C API.
I plan to version libghostty
separately from Ghostty the application. This blog post marks the public alpha (not promising API stability) and I’m hoping to motivate some developers to come use it and eventually write some language bindings once the C API is ready.
I hope to ship a tagged version of libghostty-vt
within the next 6 months, but it’ll all depend on if its ready or not.
If you look at the internal-only C header, please ignore the mess. It isn’t a good C API. It is internal-only and exists to satisfy the needs of the macOS application. This isn’t a generally consumable libghostty, although it is used by real commercial products already to embed Ghostty. We’ll take a clean slate approach defining the C API for wider usage.
Looking for Feedback
We’re at the critical stage of libghostty where we’re designing the API, and the best way to design an API is with feedback from real consumers. Ghostty is one consumer, we have some community members working on other libghostty-consuming projects, but we could use as many as we can get!
If you see a use case in your projects or organization for libghostty, please join the Ghostty Discord and collaborate with the developers working on this. If you don’t want to join Discord, email me (email in the footer of this website).
The Next Frontier
I’m super excited that Ghostty the application has finally reached the stability where we can start moving towards the libghostty
goal. libghostty
is the next frontier for Ghostty and I think it has the ability to make a far larger impact than Ghostty can as a standalone application itself.
Don’t worry, there’s plenty of changes for Ghostty the application and none of this diminishes my excitement or plans for that. Wider usage of libghostty
will result in a more feature rich and stable Ghostty application, too, since Ghostty itself is a consumer of libghostty
.
Boo. 👻
Footnotes
These own the pty to their children, parse all the escape codes, manage screen state, and ultimately “render” by emitting their own escape codes to a parent temrinal emulator. ↩ 1. Many websites do use Xterm.js and the GTK ecosystem has libvte. But this only covers a tiny fraction of the landscape and each of the aforementaioned examples are limited in their own scope. ↩ 1. I’ve spent the better part of 3 years working on terminal emulation and we’re still finding weird edge cases. ↩ 1. Here are a couple real examples of impactful issues: Jediterm didn’t handle intermediates properly, Apple’s Terminal.app just bleeds DCS sequences into the output. I’m not calling anyone out specifically, just showing that these issues exist and terminal emulation is very hard to get right. ↩ 1. A lot of general terminal emulators also got this wrong, including Ghostty just 9 months ago. ↩