Skip to main content

xterm.js as Renderer

In MonoTerm’s Atomic Architecture, xterm.js is demoted from a full terminal emulator to a specialized display component.

What is xterm.js?

xterm.js is a complete terminal emulator written in TypeScript.
╔═══════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  xterm.js - COMPLETE TERMINAL EMULATOR                                 ║
║                                                                        ║
║                                                                        ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │                                                          │       ║
║    │                     xterm.js                             │       ║
║    │                                                          │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           VTE Parser Layer                       │   │       ║
║    │   │                                                  │   │       ║
║    │   │   * EscapeSequenceParser                         │   │       ║
║    │   │   * InputHandler                                 │   │       ║
║    │   │   * OSC/CSI/DCS handlers                         │   │       ║
║    │   │   * UTF-8 decoder                                │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                        │                                 │       ║
║    │                        v                                 │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           Storage Layer                          │   │       ║
║    │   │                                                  │   │       ║
║    │   │   * Primary/Alternate buffers                    │   │       ║
║    │   │   * Line storage                                 │   │       ║
║    │   │   * Circular scrollback                          │   │       ║
║    │   │   * Cursor state                                 │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                        │                                 │       ║
║    │                        v                                 │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           Renderer Layer                         │   │       ║
║    │   │                                                  │   │       ║
║    │   │   * WebGL Renderer (fast)                        │   │       ║
║    │   │   * Canvas Renderer (fallback)                   │   │       ║
║    │   │   * DOM Renderer (accessible)                    │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                        │                                 │       ║
║    │                        v                                 │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           Interaction Layer                      │   │       ║
║    │   │                                                  │   │       ║
║    │   │   * Selection                                    │   │       ║
║    │   │   * Mouse handling                               │   │       ║
║    │   │   * Keyboard handling                            │   │       ║
║    │   │   * Clipboard                                    │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                                                          │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║                                                                        ║
║                                                                        ║
║    xterm.js can work standalone - no backend needed.                   ║
║    term.write("\x1b[32mHello\x1b[0m") displays green text.             ║
║                                                                        ║
╚═══════════════════════════════════════════════════════════════════════╝

The Demotion

In Atomic Architecture, we use only parts of xterm.js.
╔═══════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  xterm.js IN ATOMIC ARCHITECTURE                                       ║
║                                                                        ║
║                                                                        ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │                                                          │       ║
║    │                     xterm.js                             │       ║
║    │                                                          │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           VTE Parser Layer                       │   │       ║
║    │   │                                                  │   │       ║
║    │   │           ╔═════════════════════╗                │   │       ║
║    │   │           ║                     ║                │   │       ║
║    │   │           ║     BYPASSED        ║                │   │       ║
║    │   │           ║                     ║                │   │       ║
║    │   │           ║   Not used at       ║                │   │       ║
║    │   │           ║   all in Atomic     ║                │   │       ║
║    │   │           ║   Architecture      ║                │   │       ║
║    │   │           ║                     ║                │   │       ║
║    │   │           ╚═════════════════════╝                │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                                                          │       ║
║    │                        ┌──────────────────────┐          │       ║
║    │                        │  Direct injection    │          │       ║
║    │                        │  from Rust backend   │          │       ║
║    │                        │                      │          │       ║
║    │                        │  Pre-parsed cells    │          │       ║
║    │                        │  injected directly   │          │       ║
║    │                        └──────────┬───────────┘          │       ║
║    │                                   │                      │       ║
║    │                                   v                      │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           Storage Layer                          │   │       ║
║    │   │                                                  │   │       ║
║    │   │   ┌──────────────────────────────────────────┐   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   │     USED AS STORAGE                      │   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   │   Receives pre-parsed cells              │   │   │       ║
║    │   │   │   from Grid Injector                     │   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   └──────────────────────────────────────────┘   │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                        │                                 │       ║
║    │                        v                                 │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           Renderer Layer                         │   │       ║
║    │   │                                                  │   │       ║
║    │   │   ┌──────────────────────────────────────────┐   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   │     USED FOR DISPLAY                     │   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   │   WebGL renders stored cells             │   │   │       ║
║    │   │   │   GPU-accelerated display                │   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   └──────────────────────────────────────────┘   │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                        │                                 │       ║
║    │                        v                                 │       ║
║    │   ┌──────────────────────────────────────────────────┐   │       ║
║    │   │           Interaction Layer                      │   │       ║
║    │   │                                                  │   │       ║
║    │   │   ┌──────────────────────────────────────────┐   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   │     USED AS-IS                           │   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   │   Selection, scrollbar,                  │   │   │       ║
║    │   │   │   clipboard all work normally            │   │   │       ║
║    │   │   │                                          │   │   │       ║
║    │   │   └──────────────────────────────────────────┘   │   │       ║
║    │   │                                                  │   │       ║
║    │   └──────────────────────────────────────────────────┘   │       ║
║    │                                                          │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║                                                                        ║
╚═══════════════════════════════════════════════════════════════════════╝

Feature-by-Feature Breakdown

What we use and what we bypass.
╔═══════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  xterm.js FEATURE USAGE IN ATOMIC ARCHITECTURE                         ║
║                                                                        ║
║                                                                        ║
║  ┌─────────────────────────────────────────────────────────────┐      ║
║  │                                                             │      ║
║  │  BYPASSED (VTE Parsing) - Replaced by Alacritty             │      ║
║  │                                                             │      ║
║  │  These components are NEVER called in Atomic Architecture:  │      ║
║  │                                                             │      ║
║  │  * EscapeSequenceParser                                     │      ║
║  │      State machine for escape sequences                     │      ║
║  │                                                             │      ║
║  │  * InputHandler                                             │      ║
║  │      50+ CSI handlers (cursorUp, eraseInDisplay, etc)       │      ║
║  │                                                             │      ║
║  │  * OscParser, DcsParser                                     │      ║
║  │      Operating system commands, device control strings      │      ║
║  │                                                             │      ║
║  │  * term.write()                                             │      ║
║  │      We NEVER call this method in Atomic Mode               │      ║
║  │                                                             │      ║
║  └─────────────────────────────────────────────────────────────┘      ║
║                                                                        ║
║  ┌─────────────────────────────────────────────────────────────┐      ║
║  │                                                             │      ║
║  │  USED (Storage)                                             │      ║
║  │                                                             │      ║
║  │  These components store the data we inject:                 │      ║
║  │                                                             │      ║
║  │  * Primary and alternate screen buffers                     │      ║
║  │                                                             │      ║
║  │  * Line storage with internal data arrays                   │      ║
║  │                                                             │      ║
║  │  * Cursor position (we set these directly)                  │      ║
║  │                                                             │      ║
║  │  * Scrollback offset                                        │      ║
║  │                                                             │      ║
║  └─────────────────────────────────────────────────────────────┘      ║
║                                                                        ║
║  ┌─────────────────────────────────────────────────────────────┐      ║
║  │                                                             │      ║
║  │  USED (Rendering)                                           │      ║
║  │                                                             │      ║
║  │  These components display the stored cells:                 │      ║
║  │                                                             │      ║
║  │  * WebGL Renderer                                           │      ║
║  │      GPU-accelerated rendering (via addon)                  │      ║
║  │                                                             │      ║
║  │  * Canvas Renderer                                          │      ║
║  │      Fallback for non-WebGL browsers                        │      ║
║  │                                                             │      ║
║  │  * term.refresh()                                           │      ║
║  │      Triggers re-render after direct injection              │      ║
║  │                                                             │      ║
║  └─────────────────────────────────────────────────────────────┘      ║
║                                                                        ║
║  ┌─────────────────────────────────────────────────────────────┐      ║
║  │                                                             │      ║
║  │  USED (Interaction)                                         │      ║
║  │                                                             │      ║
║  │  User interaction features work unchanged:                  │      ║
║  │                                                             │      ║
║  │  * SelectionService                                         │      ║
║  │      Text selection with mouse/keyboard                     │      ║
║  │                                                             │      ║
║  │  * Viewport (scrollbar)                                     │      ║
║  │      Scrolling through history                              │      ║
║  │                                                             │      ║
║  │  * Clipboard integration                                    │      ║
║  │      Copy/paste functionality                               │      ║
║  │                                                             │      ║
║  │  * FitAddon (auto-resize)                                   │      ║
║  │                                                             │      ║
║  │  * WebLinksAddon (clickable URLs)                           │      ║
║  │                                                             │      ║
║  │  * SearchAddon (find in scrollback)                         │      ║
║  │                                                             │      ║
║  └─────────────────────────────────────────────────────────────┘      ║
║                                                                        ║
╚═══════════════════════════════════════════════════════════════════════╝

The Data Path Comparison

Visual comparison of how data flows.
╔═══════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  TRADITIONAL xterm.js DATA PATH                                        ║
║                                                                        ║
║                                                                        ║
║    Raw PTY bytes: "\x1b[32mHello\x1b[0m World"                         ║
║         │                                                              ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  term.write(bytes)                                        │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║         │                                                              ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  EscapeSequenceParser                                     │       ║
║    │                                                           │       ║
║    │  State machine processes byte by byte:                    │       ║
║    │    \x1b ──▶ ESCAPE state                                   │       ║
║    │    [   ──▶ CSI state                                       │       ║
║    │    3   ──▶ param = 3                                       │       ║
║    │    2   ──▶ param = 32                                      │       ║
║    │    m   ──▶ SGR action (set color 32 = green)               │       ║
║    │    H   ──▶ GROUND state, print 'H'                         │       ║
║    │    ...                                                    │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║         │                                                              ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  Storage Layer                                            │       ║
║    │  Cells populated one at a time                            │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║         │                                                              ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  WebGL Renderer                                           │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║                                                                        ║
║                                                                        ║
║    SLOW: JavaScript processes every byte individually                  ║
║                                                                        ║
╚═══════════════════════════════════════════════════════════════════════╝


╔═══════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  ATOMIC ARCHITECTURE DATA PATH                                         ║
║                                                                        ║
║                                                                        ║
║    Raw PTY bytes: "\x1b[32mHello\x1b[0m World"                         ║
║         │                                                              ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  Rust Alacritty VTE Parser                                │       ║
║    │                                                           │       ║
║    │  Parses everything at native speed                        │       ║
║    │  Updates internal grid state                              │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║         │                                                              ║
║         │  GridUpdate { rows: [...], cursor: {...} }                   ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  Grid Injector                                            │       ║
║    │                                                           │       ║
║    │  Receives pre-parsed cell data                            │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║         │                                                              ║
║         │  (BYPASSES term.write, EscapeSequenceParser,                 ║
║         │   InputHandler - all skipped entirely)                       ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  Storage Layer (Direct Write)                             │       ║
║    │                                                           │       ║
║    │  Cells written directly to internal arrays                │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║         │                                                              ║
║         v                                                              ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │  WebGL Renderer                                           │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║                                                                        ║
║                                                                        ║
║    FAST: Rust does the heavy lifting                                   ║
║                                                                        ║
╚═══════════════════════════════════════════════════════════════════════╝

Why Not Replace xterm.js Entirely?

If we bypass the parser, why keep xterm.js at all?
╔═══════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  WHY WE STILL NEED xterm.js                                            ║
║                                                                        ║
║                                                                        ║
║    We could theoretically replace xterm.js with a custom               ║
║    renderer, but xterm.js provides many features for free:             ║
║                                                                        ║
║                                                                        ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │                                                          │       ║
║    │  FEATURES WE GET FOR FREE                                │       ║
║    │                                                          │       ║
║    │  ┌────────────────────────────────────────────────────┐  │       ║
║    │  │                                                    │  │       ║
║    │  │  WebGL Renderer                                    │  │       ║
║    │  │  * GPU-accelerated text rendering                  │  │       ║
║    │  │  * Glyph atlas management                          │  │       ║
║    │  │  * Efficient dirty-rectangle updates               │  │       ║
║    │  │                                                    │  │       ║
║    │  └────────────────────────────────────────────────────┘  │       ║
║    │                                                          │       ║
║    │  ┌────────────────────────────────────────────────────┐  │       ║
║    │  │                                                    │  │       ║
║    │  │  Text Selection                                    │  │       ║
║    │  │  * Mouse drag selection                            │  │       ║
║    │  │  * Double-click word select                        │  │       ║
║    │  │  * Triple-click line select                        │  │       ║
║    │  │  * Rectangle select (Alt+drag)                     │  │       ║
║    │  │                                                    │  │       ║
║    │  └────────────────────────────────────────────────────┘  │       ║
║    │                                                          │       ║
║    │  ┌────────────────────────────────────────────────────┐  │       ║
║    │  │                                                    │  │       ║
║    │  │  Scrolling                                         │  │       ║
║    │  │  * Smooth scrolling                                │  │       ║
║    │  │  * Scrollbar with drag support                     │  │       ║
║    │  │  * Wheel scroll with momentum                      │  │       ║
║    │  │                                                    │  │       ║
║    │  └────────────────────────────────────────────────────┘  │       ║
║    │                                                          │       ║
║    │  ┌────────────────────────────────────────────────────┐  │       ║
║    │  │                                                    │  │       ║
║    │  │  Addons                                            │  │       ║
║    │  │  * FitAddon (auto-resize)                          │  │       ║
║    │  │  * WebLinksAddon (clickable URLs)                  │  │       ║
║    │  │  * SearchAddon (find in scrollback)                │  │       ║
║    │  │  * Unicode11Addon (wide character support)         │  │       ║
║    │  │                                                    │  │       ║
║    │  └────────────────────────────────────────────────────┘  │       ║
║    │                                                          │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║                                                                        ║
║                                                                        ║
║    Reimplementing all of this would take months.                       ║
║    Better to reuse what works and bypass only the slow parts.          ║
║                                                                        ║
╚═══════════════════════════════════════════════════════════════════════╝

The “Dumb Renderer” Concept

A summary of xterm.js’s new role.
╔═══════════════════════════════════════════════════════════════════════╗
║                                                                        ║
║  xterm.js: FROM "TERMINAL EMULATOR" TO "TERMINAL RENDERER"             ║
║                                                                        ║
║                                                                        ║
║                                                                        ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │                                                          │       ║
║    │  BEFORE (Traditional Usage)                              │       ║
║    │                                                          │       ║
║    │                                                          │       ║
║    │      xterm.js                                            │       ║
║    │    ┌──────────────────────────────────────────────────┐  │       ║
║    │    │                                                  │  │       ║
║    │    │  "I am a complete terminal emulator.             │  │       ║
║    │    │   Give me raw bytes, I will parse them,          │  │       ║
║    │    │   update my storage, and render them."           │  │       ║
║    │    │                                                  │  │       ║
║    │    │   Input:  Raw PTY bytes                          │  │       ║
║    │    │   Output: Rendered terminal                      │  │       ║
║    │    │                                                  │  │       ║
║    │    └──────────────────────────────────────────────────┘  │       ║
║    │                                                          │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║                                                                        ║
║                                                                        ║
║    ┌──────────────────────────────────────────────────────────┐       ║
║    │                                                          │       ║
║    │  AFTER (Atomic Architecture)                             │       ║
║    │                                                          │       ║
║    │                                                          │       ║
║    │      xterm.js                                            │       ║
║    │    ┌──────────────────────────────────────────────────┐  │       ║
║    │    │                                                  │  │       ║
║    │    │  "I am now a display component.                  │  │       ║
║    │    │   Give me cell data, I will store it             │  │       ║
║    │    │   and render it. No parsing needed."             │  │       ║
║    │    │                                                  │  │       ║
║    │    │   Input:  Pre-parsed cell grid                   │  │       ║
║    │    │   Output: Rendered terminal                      │  │       ║
║    │    │                                                  │  │       ║
║    │    └──────────────────────────────────────────────────┘  │       ║
║    │                                                          │       ║
║    └──────────────────────────────────────────────────────────┘       ║
║                                                                        ║
║                                                                        ║
║                                                                        ║
║    The "intelligence" (VTE parsing) moved to Rust.                     ║
║    xterm.js is now a "dumb" but capable display.                       ║
║                                                                        ║
║    This is NOT a criticism of xterm.js.                                ║
║    It's an optimization for our specific use case.                     ║
║                                                                        ║
╚═══════════════════════════════════════════════════════════════════════╝

Summary

FeatureTraditional xterm.jsMonoTerm Atomic Architecture
VTE ParsingJavaScriptRust (Alacritty) - 10x faster
term.write()UsedBypassed
StorageUsedUsed (direct injection)
WebGL RenderingUsedUsed
SelectionUsedUsed
ScrollingUsedUsed
AddonsUsedUsed