Everything behind this text is rendered on a terminal.

Spooky fast
web terminal.

Ghostty's terminal engine and a Rust WebGPU renderer, compiled into one Wasm module. Drop it in where xterm.js was, or run Ghostty itself in a worker.

↓
01 · CPU per update

Nearly 4× less CPU
than xterm WebGL.

One dense screen: 240×80 cells rewritten every update, with about 4,800 style changes. Lower is better. Bars show milliseconds of CPU for parsing plus redraw.

xterm.jsDOM renderer
37.7ms
xterm.js+ WebGL addon
9.8ms
Wraithnative · libghostty
3.1ms
Wraithxterm-compatible
2.5ms
0.6msWraith redraw of the full screen, versus 8.8 ms for xterm WebGL
20bytesper GPU cell, so a full 240×80 screen uploads 375 KiB
1rowuploaded when one cell changes; unchanged and scrolled rows are reused
4.2→2.5mscompatible path, from the first recorded build to this one

Medians from interleaved runs, 10 Oct 2026: Chrome 155, Apple M5 Max, hardware WebGPU, Wraith build c6a77bd, xterm 6.0 with WebGL addon 0.19. GPU-completed updates, not display frames. Native figures drive the engine directly, without the worker hop. Your machine will differ, so race it yourself below.

02 · Live, in this browser

Race it.
On your hardware.

This runs the repository's own benchmark harness: real xterm.js 6.0, its official WebGL addon, and both Wraith paths, each fed the same bytes and fenced on the GPU.

Needs a WebGPU browser. Takes about 20 s.

Stage idle
03 · How it works

From bytes to pixels.

Six steps, all off your page's main thread, and nothing drawn twice.

  1. 01Your bytes

    Output from your shell, batched into one message per burst.

  2. 02A worker

    Everything after this runs off your page's main thread.

  3. 03Ghostty's engine

    libghostty-vt reads every escape sequence the way Ghostty does.

  4. 04Only what changed

    Unchanged rows are skipped. Scrolled rows are reused, not redrawn.

  5. 05Tiny cells

    Each cell is 20 bytes on the GPU: its colors, its style and one glyph.

  6. 06Two GPU passes

    Backgrounds, then glyphs. Empty cells cost nothing.

04 · Two ways in

Keep your xterm code.
Or go full ghost.

Drop-inxterm.js 6.0 API
import { Terminal } from 'wraithterm/xterm';
import { FitAddon } from '@xterm/addon-fit';

const term = new Terminal({ fontSize: 14 });
term.loadAddon(new FitAddon());
term.open(element);
term.write('same API, WebGPU underneath');

xterm's parser and buffer, Wraith's renderer. Addons such as fit, search, serialize and web links keep working.

NativeGhostty in a worker
import { Wraith } from 'wraithterm';

const term = await Wraith.mount(element);
term.connect(new WebSocket(ptyUrl));
term.onResize(({ cols, rows }) => {
  // tell the PTY
});

Ghostty's terminal semantics, Kitty keyboard encoding and reflow, with the whole terminal off the main thread.

05 · Labs

Take it for a spin.

Two hands-on labs, both running the real thing in your browser.