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.
- Background pass — one instanced draw renders all cell backgrounds as colored rectangles.
- 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) andfontWeightBoldfor 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.
- Receives the
OffscreenCanvasviatransferControlToOffscreen(). - Reads cell data from the
SharedArrayBufferdirectly — no serialization overhead. - Runs a
requestAnimationFrameloop viaself.requestAnimationFramein the worker. - Handles
init,update(cursor/selection),resize,theme,font, anddisposemessages. - Posts
frame(FPS stats) anderrormessages 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: truepreserves 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,
SharedWebGLContextthrows 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.