Shitty ships two binaries that differ only in their name
A serious terminal emulator with a stupid name
At a glance
- What is it?
- A native terminal emulator with a compute-rendered cell pipeline, a total and fuzzed parser, and a benchmark table it publishes against its competitors. It is also the only terminal here that renames itself twice, once to something ruder and once to something polite.
- Who is it for?
- Shitty is worth trying if the things other terminals do badly are the things you notice: flicker on resize, parser fragility on hostile input, or font dependency on a machine with no fonts installed. It handles all three by design rather than by accident, and it publishes the measurements.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Two binaries, one terminal, and a name the author calls stupid
The repository description is one sentence and it is not modest: a serious terminal emulator with a stupid name. The banner above it is blunter still, promising low latency, fast startup, and predictable resource use, then adding a clause that reads as a jab at a specific competitor.
The technical claim underneath that is specific and unusual. Terminal state stays on the CPU, and cells are rendered with native compute backends rather than with a conventional graphics pipeline: a compute backend on Linux and a different one on macOS.
Then there are two user-facing brands built from the same source. One binary is named for the rude word. The other is named for a polite alternative, for people who would rather not have that name in their dock.
What separates them is enumerated and it is a short list: the name, the application identity, the configuration and public environment names, the help and version text, the desktop entry, and the icon. Everything else is shared, including all of the terminal code.
That is the whole trick. There is no fork, no feature gate, and no separate build target. If you install the polite one you get the same program, and the only observable difference is what it calls itself and what icon appears in your application menu.
The two naming schemes also continue into the filesystem and the tooling, which is where it starts to matter for scripts.
It loses the printable text table and wins the malformed input table
The benchmark section publishes two tables and they tell opposite stories, which is more useful than two tables that agree.
The setup is stated in enough detail to reproduce: a hundred megabytes piped through the graphical interface on an Apple-silicon laptop, every terminal equalised first to the same font, the same cell size, an eighty by twenty-four grid, and five hundred lines of scrollback, with the best of three runs reported.
On printable text, which the readme calls the scroll path, the terminal under discussion is second. A nightly build of a competitor is first at just over half a second, this one is second at just over four fifths of a second, and three more follow. It is last of the five.
On random bytes, described as the parser's worst case with invalid UTF-8 throughout, the same terminal is first, at just under two seconds, ahead of four competitors.
There is one more detail in the first table that is easy to miss. The terminal under discussion has the lowest user time of all five, at half a second, while its wall time is second. Lowest CPU with second-best wall time is the signature of a process that is not the bottleneck, which is consistent with a compute-rendered design where the compositor owns the frame.
So the honest summary is: it gives up some throughput on the easy path and takes it back on the hard one.
The missing row is a refusal and the nightly row was measured on request
Two entries in those tables deserve explanation rather than reading.
The first is the one competitor that does not appear in the second table at all. The readme says it sits the random payload out, reacting to the embedded escape junk with title changes and bells instead of drawing.
That is a behavioural choice by that terminal, not a measurement failure and not an omission. It declines to render the payload. So the second table has four rows rather than five, and the missing row is arguably a data point about how terminals treat untrusted input: one of them refuses and the others draw.
The second is the nightly build. The readme identifies it as the official tip build with a specific commit hash, and says it was measured at the request of that project's author. The released version of the same competitor is kept in the table as a separate row for comparison.
Measuring a competitor's unreleased build because they asked you to is not something most projects do, and it cuts against the project's own promotional framing. It is also the reason the first table has a build that nobody can install.
Both choices point the same way: the benchmark section is written by someone who wanted the comparison to be checkable rather than flattering. The reproduction script is part of that, and it is described as verifying the equalised configuration from inside every terminal before measuring anything, which is the step most benchmarks skip.
The parser is total and random bytes are a benchmark
The correctness claim in the readme is two sentences and both are unusual.
The first says the parser state machine is total, and that it is fuzzed with corpora committed to the repository. Total, in the formal sense: every input sequence is defined to lead to some state rather than to an undefined one. That is a property you either prove or you do not claim, so the word carries weight.
The second sentence is the memorable one: feeding random device bytes is described as a benchmark here rather than as a crash report.
That reframing is the point. A terminal receives whatever bytes arrive on a pseudo-terminal, including from a program that has lost the plot, and a parser with undefined states is a crash, a hang, or worse. Making the fuzz corpus part of the repository means the input that once broke it is still in the test set.
Two related claims sit next to this one. Resize frames are rendered inside the same transaction as the bounds change, and updates are damage-driven, which is what the flicker-free claim rests on. And cells are grapheme clusters rather than code points, which is the Unicode claim: emoji sequences, variation selectors, combining marks, and wide CJK characters are all single cells, not two.
The Unicode work goes further than the summary line suggests. The readme names the current cluster standard and mentions selectable historical width tables, which exist so that a character width computed by a remote host running an older standard still lines up locally.
More than five thousand tests borrowed from a dozen other terminals
The test suite is described by where its tests came from rather than by how many there are, and the number given is more than five thousand.
They are harvested from over a dozen existing suites belonging to other projects, and the readme names them: a competitor terminal's suite, a standalone conformance test for escape sequences, a long-standing set of terminal validation tests from another emulator, a terminal emulation test suite, a terminal attack test kit, two terminal emulator libraries, and suites from four more terminals and one remote shell client.
The second half of the sentence is the important part. The tests are driven black-box through a real pseudo-terminal.
Black-box through a real PTY means the implementation is exercised the way a user exercises it, through a process boundary, rather than by calling internal functions. For a terminal that is the only test shape that proves anything about the parts that matter, since almost all of its behaviour is about reacting to bytes on a file descriptor.
The directory name for this is worth noting if you are browsing the repository. The test directory is abbreviated to three letters rather than spelled out.
Combined with the fuzz corpora and the reproduction benchmark script, there are three independent ways this project tries to be trustworthy: conformance suites from others, fuzzing of its own state machine, and a published measurement. That is an unusual density of evidence for a project with integer release numbers.
Fonts are embedded and clipboard reads are gated
Two claims here are about what the terminal refuses to do.
The first is fonts. The terminal embeds a monospace fallback and an emoji fallback, so it remains usable on a machine with no system fonts installed at all. The readme puts it plainly: it starts on a machine with no fonts installed.
That is a real deployment property rather than a feature checkbox. A minimal container or a freshly installed system has no font configuration, and a terminal that assumes otherwise either renders nothing or renders boxes. The rest of the font support is layered on top of it: per-cluster fallback so a glyph missing from one font comes from the next, the four standard faces, ligatures that cross cell boundaries, colour emoji, runtime zoom, and optional unhinted subpixel rendering.
The second is the security default. Applications cannot read selections and cannot drive the host window unless explicitly allowed. The same paragraph continues into the clipboard: the two clipboard protocols are implemented, including gated reads, and window operations by the application are separately gated and off by default.
That combination is the right one for a program that runs arbitrary code from the internet on your desktop. Terminals have been a favoured target precisely because the escape-sequence surface lets a program read your clipboard and learn what you copied. Gating the read path, rather than only the write path, is the part most implementations skip.
Hyperlinks get the same treatment: detected plain URIs are clickable, with a configurable list of allowed schemes and hover feedback, so a hostile output stream cannot turn arbitrary text into a link to wherever it likes.
Configuration reload is atomic and a bad reload changes nothing
The configuration story is a single dense sentence and every clause in it is a decision.
The format is TOML, and it supports imports, so one file can pull in others; environment expansion, so a value can come from the environment; and command line overrides, so a flag wins over both. Colour schemes and font fallback lists are part of the same file rather than separate systems.
Then the reload. It is a signal, so a running terminal reconfigures without restarting, and it is atomic, and an invalid configuration leaves the current one active.
That last clause is the one that makes the feature safe. A reload that can leave you with no configuration is a reload you will stop using. Rejecting the bad file and keeping the old one means you can experiment with a live terminal and a typo costs you nothing.
There is a second configuration mechanism in the escape protocol, and it is the one this project is most distinctive about. Alongside the usual version and terminal-capability queries, it offers a feature-discovery string that a program can read to learn what the terminal supports, without the program having to guess from the terminal's name.
That matters because terminal capability detection has always been an act of inference from a name. A program that only knows the name has to assume, and every new feature is therefore invisible until the program learns about it by other means. Making the answer queryable turns a naming problem into a protocol problem.
The rest of the in-band reporting follows the same logic, covering working directory, semantic prompt markers, attention notifications, progress state, and both cell and pixel resize reports.
Editorial conclusion
Shitty is worth trying if the things other terminals do badly are the things you notice: flicker on resize, parser fragility on hostile input, or font dependency on a machine with no fonts installed. It handles all three by design rather than by accident, and it publishes the measurements. Two caveats before you adopt it. The benchmark table is honest to the point of unflattering, since the terminal loses the printable text comparison to a nightly build of a competitor measured at that competitor author's request, and it wins the malformed input comparison outright. And the project is early in absolute terms: integer release numbers, a licence directory the hosting platform could not classify, and a parser described as total, which is a strong word to attach to a state machine that has not yet seen the input you will give it.
Frequently asked questions
What is the Shitty terminal emulator?
It is a native terminal emulator that keeps terminal state on the CPU and renders cells with compute backends, using Vulkan on Linux and Metal on macOS. It ships as one small self-contained binary with no windowing toolkit and embedded fonts, built for low latency and predictable resource use.
What is the difference between the st and pt binaries?
Nothing functional. They are two user-facing brands of the same terminal and share all terminal code. They differ only in name, application identity, configuration and public environment names, help and version text, desktop entry, and icon.
How does Shitty perform compared to other terminals?
On a published benchmark with all terminals equalised to the same font, cell size, grid, and scrollback, it is second on a hundred megabytes of printable text and first on a hundred megabytes of random bytes. It also records the lowest user time of all five terminals on the text test despite a second-place wall time.
How does Shitty handle untrusted terminal output?
The parser state machine is described as total and is fuzzed with corpora committed to the repository, so random bytes are treated as a benchmark rather than a crash report. Applications cannot read selections or drive the host window unless explicitly allowed, clipboard reads are gated, and hyperlinks use a configurable allowlist of schemes.
What licence is Shitty under?
The repository contains three licence files at the root, one general and two named for the MIT and GNU GPL variants, while the hosting platform reports the licence as unclassified. Releases are tagged with bare integers, with the three most recent being 15, 16, and 17.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pg83-shitty)