A cross-platform terminal, built for speed and efficiency.
Linux, Windows and macOS — iOS coming soon. Measured faster than ghostty on Linux and macOS, and Windows Terminal on Windows, at about half the CPU. Below: each pair on one machine, matched grids, three tests, one take.
Wayland or X11 on Linux, a native Win32 window on Windows, a Cocoa window on macOS — the same terminal, and the same speed, on each. iOS is next.
Ten workloads at steady state: 1.41× Windows Terminal on Windows, 1.38× ghostty on Linux, 1.28× ghostty on macOS — matched grid and font, at about half the CPU each time.
Zero ucs-detect errors in 107k+ measurements — wide characters,
emoji families, 118 languages of conjuncts — where the ghostty nightly
logs 40.
curl -LO https://github.com/starling-build/starling/releases/download/terminal-v0.2.0/starling-terminal_0.2.0_amd64.deb
sudo apt install ./starling-terminal_0.2.0_amd64.deb
Everything the terminal loads — engine, Swift runtime,
fonts — rides inside the package under
/usr/lib/starling-terminal; apt resolves the
few system libraries. It opens from the app grid as
Starling Terminal, or as starling-terminal
from any prompt. On the Starling desktop there
is nothing to install — the terminal is part of the desktop.
curl -LO https://github.com/starling-build/starling/releases/download/terminal-v0.2.0/starling-terminal-0.2.0-macos-arm64.zip
ditto -xk starling-terminal-0.2.0-macos-arm64.zip /Applications
Or unzip it and drag Starling Terminal.app
into Applications yourself. The bundle is signed but not notarized, so
macOS holds the first launch: approve it under System Settings →
Privacy & Security → Open Anyway, or clear the
quarantine bit with
xattr -dr com.apple.quarantine "/Applications/Starling Terminal.app".
curl.exe -LO https://github.com/starling-build/starling/releases/download/terminal-v0.2.0/starling-terminal-0.2.0-windows-x86_64.zip
Expand-Archive starling-terminal-0.2.0-windows-x86_64.zip -DestinationPath StarlingTerminal
.\StarlingTerminal\TerminalApp.exe
A native Win32 window, no installer and nothing to put on
PATH. Give it a folder of its own: the archive extracts
flat, and all 52 files have to stay together — Windows
resolves imports from the executable's own directory, and the bundled
ConPTY console host sits beside it.
Thirteen tests — ten workloads, two 500 MB cats, DOOM-Fire — against the fastest terminal on each platform. Matched fonts, grids verified on both sides, every test at steady state. Amber, rightward — Starling is ahead; grey, leftward — the other terminal is. The setup and every raw number are below the charts.
Speed — thirteen tests at steady state, three platforms
Hover any row for the raw seconds. Sustained is the regime that
matters, and it is not always the flattering one — the two workloads ghostty
wins in ten-second bursts (light and dense cells, 0.85× and 0.69×
short-range) converge to parity here, but ghostty also gains nothing back:
its Linux unicode throughput halves at scale (184 to 97 MB/s) while ours
sheds only a quarter (228 to 173 MB/s). Across all three platforms and all
thirty-nine cells above, the one test the other side keeps by more than
noise is the Linux ascii cat, by ~6% — ghostty's
io_uring read path gets under the read(2) floor
ours sits on. macOS has no io_uring, and the cat flips to us; on Windows
every test is a win.
The cost — CPU and memory for the same work
The wall wins are not bought with cores. In ten-second bursts the gap is wider still: 2.17× and 3.37×.
On Windows the CPU figures include the console host, which does a large share of the work there and would flatter us if left out; the memory gap is the widest of the three platforms — 73 MB against 1,026 MB after the same ten workloads, a figure Windows Terminal reproduces across machines. CPU-seconds are otherwise charged to the terminal's own process, read around each workload. Memory is flat on our side of all three platforms — the same resident set no matter how much has been drawn — while the other side's grows with load; only the light Linux legs leave ghostty below us.
The setup
Who: the ghostty nightly on Linux and macOS, configured onto our Roboto Mono at our cell metrics so both terminals raster the same pixels; Windows Terminal Preview 1.25 on Windows, in its own process (the stable build hands new windows to whatever Windows Terminal is already running). Where: Linux — Ryzen 7 8845HS, GNOME/Wayland; macOS — MacBook Pro on macOS 26.6.1; Windows — a Ryzen 7 8845HS laptop with a Radeon 780M, the first Windows round on real hardware rather than a VM. Grids verified on both sides before every leg: 47×201 on Linux and macOS, 40×120 on Windows. How: every test at steady state — each workload repeated until the slower side sustains one to two minutes (~159 GB of escape-sequence traffic per terminal on Linux, ~30 GB on macOS), the cats exact 500,000,000-byte corpora, DOOM-Fire run to 150,000 frames three times over. Both sides measured back to back on one machine; bars are multiples of the other terminal's pace on a log scale, so a 2× win and a 2× loss have equal length.
The rounds behind the numbers
The Linux and Windows legs ran on 30 Hz displays, so raw walls compare
within a platform; the ratios carry. The macOS numbers are the rerun taken
after the macOS 26.6.1 upgrade — which moved nothing beyond run noise
against the same-day pre-upgrade round. The Windows round runs the console
on the UTF-8 code page, so the unicode workloads draw real CJK text rather
than the mojibake every earlier VM round was unknowingly measuring, and a
fixed compute loop timed inside every leg proves both sides saw the
machine in the same state; the archived VM rounds remain, but they measure
the legacy code page. The raw runs are checked in at
docs/perf
(Windows: -windows-realgpu-2026-08-16; Linux:
-2026-08-14-long; macOS:
-macos-2026-08-14-postupgrade, confirmed against
-macos-2026-08-14-long; short range:
-2026-08-13-atlas and
-macos-2026-08-13-samefont).
Raw speed — seconds to drain each workload
| Linux | macOS | |||||
|---|---|---|---|---|---|---|
| Workload | Starling | ghostty | Speedup | Starling | ghostty | Speedup |
| Light cells | 0.586 | 0.497 | 0.85× | 0.111 | 0.149 | 1.34× |
| Dense cells | 0.249 | 0.173 | 0.69× | 0.129 | 0.145 | 1.12× |
| Coloured text | 0.387 | 1.039 | 2.68× | 0.438 | 0.642 | 1.47× |
| Truecolour | 0.390 | 0.761 | 1.95× | 0.619 | 0.713 | 1.15× |
| Unicode | 0.334 | 0.316 | 0.95× | 0.323 | 0.305 | 0.94× |
| Cursor motion | 0.022 | 0.034 | 1.55× | 0.031 | 0.037 | 1.19× |
| Alt screen | 0.117 | 0.135 | 1.15× | 0.207 | 0.180 | 0.87× |
| Scroll region | 0.510 | 0.457 | 0.90× | 0.181 | 0.217 | 1.20× |
| Long lines | 0.145 | 0.147 | 1.01× | 0.163 | 0.181 | 1.11× |
| Binary dump | 0.479 | 1.624 | 3.39× | 0.189 | 0.545 | 2.88× |
| All ten workloads | 3.219 | 5.183 | 1.61× | 2.391 | 3.114 | 1.30× |
Seconds compare within a platform, not across it: the Linux round ran on a 30 Hz display, which slows both terminals' absolute times equally. The ratios carry.
CPU — what the suite costs the machine
| Ten workloads | Starling | ghostty | Speedup |
|---|---|---|---|
| Linux | 4.46 s | 9.66 s | 2.17× |
| macOS | 2.38 s | 8.02 s | 3.37× |
Memory — resident set
| Sampled at | Starling | ghostty | Delta |
|---|---|---|---|
| Linux, after the suite | 225 MB | 209 MB | +16 MB |
| Linux, after 159 GB of suite | 225 MB | 212 MB | +13 MB |
| Linux, after two 500 MB cats | 225 MB | 256 MB | −31 MB |
| macOS, after 30 GB of suite | 121 MB | 155 MB | −34 MB |
| macOS, after two 500 MB cats | 121 MB | 177 MB | −56 MB |
The macOS rows are the 08-13 long round, the one macOS round that sampled RSS at both points. The current build measures 130 MB against ghostty's 155 after the suite — the paced reader's 8 MB ring and thread stacks — and stays flat the same way.
Steady state — every test run for minutes, not seconds
| Linux | macOS | Windows | |||||||
|---|---|---|---|---|---|---|---|---|---|
| Test | Starling | ghostty | Speedup | Starling | ghostty | Speedup | Starling | Win Terminal | Speedup |
| Light cells | 101.2 s | 102.0 s | 1.01× | 53.9 s | 96.9 s | 1.80× | 142.7 s | 170.1 s | 1.19× |
| Dense cells | 86.8 s | 86.2 s | 0.99× | 92.0 s | 96.7 s | 1.05× | 97.3 s | 129.6 s | 1.33× |
| Coloured text | 34.0 s | 108.3 s | 3.19× | 76.9 s | 112.4 s | 1.46× | 179.7 s | 188.8 s | 1.05× |
| Truecolour | 54.2 s | 122.8 s | 2.27× | 81.1 s | 106.5 s | 1.31× | 163.5 s | 215.4 s | 1.32× |
| Unicode | 97.1 s | 107.3 s | 1.10× | 81.9 s | 98.3 s | 1.20× | 96.6 s | 146.0 s | 1.51× |
| Cursor motion | 49.9 s | 74.3 s | 1.49× | 70.4 s | 71.2 s | 1.01× | 177.4 s | 203.0 s | 1.14× |
| Alt screen | 80.8 s | 85.9 s | 1.06× | 88.8 s | 88.4 s | 1.00× | 118.6 s | 222.3 s | 1.87× |
| Scroll region | 80.4 s | 78.5 s | 0.98× | 90.3 s | 96.6 s | 1.07× | 167.0 s | 222.7 s | 1.33× |
| Long lines | 85.0 s | 84.3 s | 0.99× | 93.7 s | 97.3 s | 1.04× | 102.7 s | 136.0 s | 1.32× |
| Binary dump | 26.7 s | 109.9 s | 4.12× | 31.8 s | 110.1 s | 3.46× | 40.9 s | 177.6 s | 4.34× |
| All ten workloads, wall | 696 s | 960 s | 1.38× | 761 s | 974 s | 1.28× | 1286 s | 1812 s | 1.41× |
| All ten workloads, CPU | 1030 s | 1930 s | 1.87× | 1081 s | 2436 s | 2.25× | 1581 s | 3182 s | 2.01× |
| ascii cat, 500 MB | 2.97 s | 2.79 s | 0.94× | 1.82 s | 2.01 s | 1.11× | 1.98 s | 2.71 s | 1.37× |
| unicode cat, 500 MB | 2.88 s | 5.15 s | 1.79× | 1.76 s | 2.45 s | 1.39× | 4.40 s | 6.46 s | 1.47× |
| DOOM-Fire, 150,000 frames | 1610 fps | 830 fps | 1.94× | 1119 fps | 1071 fps | 1.05× | 482 fps | 390 fps | 1.24× |
Cats and DOOM-Fire at short range — 150 MB cats
| Linux | macOS | |||||
|---|---|---|---|---|---|---|
| Test | Starling | ghostty | Speedup | Starling | ghostty | Speedup |
| ascii cat, 150 MB | 0.869 s | 0.829 s | 0.95× | 0.611 s | 0.692 s | 1.13× |
| unicode cat, 150 MB | 0.659 s | 0.815 s | 1.24× | 0.806 s | 0.826 s | 1.02× |
| DOOM-Fire | 1200 fps | 701 fps | 1.71× | 1058 fps | 960 fps | 1.10× |
Raw values are seconds for the cats and frames per second for DOOM-Fire; the speedup is Starling's multiple either way. The fire rows run 600 frames on Linux and 6,000 on macOS — at 600 the macOS pair reads ~1025 vs ~845 fps (1.21×), but the longer run amortizes ghostty's warm-up and is the fairer number. CPU is ours on all six legs — ghostty spends 1.4–2.8× as much.
The emulator is about 2,000 lines of C we wrote — no libvte, no libvterm, nothing borrowed. The pixels come from the Flutter engine through the Starling SDK: a general-purpose UI engine, not a terminal-specific one.
That is the unusual part. Terminals this fast are normally built on a stack that can only ever draw a terminal. This one matches or beats them on their own benchmarks — and the engine underneath hands us the rest for free: the same code on every platform, and a real widget tree around the grid, which is where the splits, the floating frames and the cover flow come from.
The Starling SDK has a terminal widget — shell, escape sequences, fonts, scrollback and selection included — so you can write your own terminal app on top of it. Download one file, run it, and you have a terminal of your own, on Linux, macOS or Windows:
curl -fLO https://github.com/starling-build/starling/releases/download/sdk-v0.3.1/starling-create
chmod +x starling-create
./starling-create myterm
cd myterm && swift run -c release
Those four lines are the same on Linux, macOS and Windows
— on Windows in Git bash, which is where a curl instruction
lands anyway. starling-create is an asset on the SDK
release above — a different release from the terminal's, which is why
the tag says sdk-. It checks your
toolchain, fetches the SDK bundle for the machine it is run on — the
framework plus the engine binaries it links against, 14 MB on macOS,
17 MB on Windows, 23 MB on Linux — and writes the SwiftPM project
around it. No repo to clone, no engine checkout, no Flutter install; a
Swift toolchain is the only prerequisite. The SDK is cached once under
~/.starling/sdk and shared, so the second project costs no
download; --template app scaffolds a plain window instead of
a terminal, and --sdk takes a URL or a local archive when you
want a bundle other than the release's. What it writes is this, and this
is the whole app:
import ExampleHost
import Flutter
let session = TerminalSession()
session.startShell()
runExampleApp(title: "Terminal", width: 940, height: 620) {
TerminalView(session: session)
}
That is the same widget the films above run on, and the same one the shipped terminal is built from — the release archives are assembled against this SDK bundle, not against a private tree. The workspace is it with geometry around it — splits, frames and a cover flow, and no terminal code of its own.