Rendering - rahulpandita/react-term GitHub Wiki

react-term supports three rendering strategies that are automatically selected based on browser capabilities. All strategies share the same CellGrid data source; only the paint path differs.

Strategy Auto-Detection

flowchart TD
  Start([Terminal Mount]) --> CheckSAB{SharedArrayBuffer\navailable?}
  CheckSAB -->|Yes| CheckOC{OffscreenCanvas\navailable?}
  CheckSAB -->|No| MTFallback["Main Thread Strategy\nCanvas 2D renderer\nParser on main thread"]
  CheckOC -->|Yes| CheckWebGL{WebGL2\navailable?}
  CheckOC -->|No| ParserWorker["Parser Worker Strategy\nParser in worker\nWebGL2 on main thread"]
  CheckWebGL -->|Yes| FullWorker["Full Worker Strategy\nParser + Render in workers\nOffscreenCanvas + WebGL2"]
  CheckWebGL -->|No| ParserWorker

The renderMode prop on Terminal (or the renderer option on WebTerminal) can override automatic selection to force 'webgl' or 'canvas2d'.

WebGL2 Renderer

WebGLRenderer achieves high throughput through instanced rendering.

Draw calls per frame: exactly 2.

  1. Background pass — one instanced draw renders all cell backgrounds as colored rectangles.
  2. Foreground pass — one instanced draw renders all glyphs from a shared atlas texture.

Glyph atlas: glyphs are rasterized lazily into an alpha-only OffscreenCanvas-backed texture. Atlas entries are keyed by (codepoint, bold, italic). Each glyph is rasterized once.

Wide character support: spacer cells (right half of 2-column characters) are skipped entirely. The background instance for a wide character spans 2 × cellWidth. The glyph instance is rendered at double width.

Color parsing (hexToFloat4): accepts #rrggbb, #rgb, rgb(), rgba(), hsl(), oklch(), and CSS named colors. For complex expressions, a 1×1 OffscreenCanvas is used as a CSS color engine — this works inside workers and falls back to HTMLCanvasElement on the main thread.

Instance buffer layout:

Buffer Floats/instance Contents
Background 6 x, y, w, h + RGBA color
Foreground 12 x, y, cellW, cellH, u, v, glyphW, glyphH + RGBA color

Canvas 2D Renderer

Canvas2DRenderer is the universal fallback, rendering via CanvasRenderingContext2D.

  • Renders only dirty rows, skipping clean rows entirely.
  • Wide characters: isSpacerCell() skips the right-half cell; isWide() doubles the effective render width.
  • Supports fontWeight (normal) and fontWeightBold for fine-grained weight control.
  • Builds a 256-entry ANSI palette on construction.

OffscreenCanvas Render Worker

In Full Worker strategy, render-worker.ts runs inside a DedicatedWorkerGlobalScope and owns the OffscreenCanvas.

  1. Receives the OffscreenCanvas via transferControlToOffscreen().
  2. Reads cell data from the SharedArrayBuffer directly — no serialization overhead.
  3. Runs a requestAnimationFrame loop via self.requestAnimationFrame in the worker.
  4. Handles init, update (cursor/selection), resize, theme, font, and dispose messages.
  5. Posts frame (FPS stats) and error messages back to the main thread.

Wide character handling is consistent with the main-thread renderers: spacer cells are skipped, wide characters are drawn at 2× width.

SharedWebGLContext

SharedWebGLContext allows multiple terminal panes to share a single WebGL2RenderingContext, working around Chrome's hard limit of 16 simultaneous WebGL contexts.

  • preserveDrawingBuffer: true preserves each pane's canvas content after compositing.
  • Per-terminal dirty-row caches prevent full redraws when only one pane changes; at 16 panes this allows ~120 fps.
  • Wide characters: consistent handling with the standalone WebGLRenderer — spacer cells skipped, background instances at 2× width.
  • Software renderer detection: if WebGL2 is backed by a software rasterizer, SharedWebGLContext throws to trigger automatic Canvas 2D fallback.

Instance buffer layout extends the standalone renderer with two extra floats (pane offsetX, offsetY) per instance for viewport positioning.