Ultra-smooth WebGL terminal renderer with MSDF font rendering
npm install terme




Ultra-smooth, high-performance terminal renderer using WebGL 2 and MSDF font rendering. Capable of handling millions of lines via virtual scrolling with sustained 120+ FPS.
Terme is a WebGL-based terminal renderer optimized for extreme performance. Unlike traditional DOM-based terminals, Terme renders text using GPU-accelerated instancing and MSDF fonts, achieving 120+ FPS even with millions of lines.
Use Cases:
- Terminal emulators (xterm.js alternative)
- Log viewers with millions of lines
- Browser-based code editors
- REPL/Shell interfaces
- Real-time data monitoring dashboards
Not a full terminal emulator: Terme focuses on rendering. For complete terminal functionality with ANSI support, PTY handling, etc., you'll need to implement or integrate those layers.
See USAGE.md for detailed documentation.
Installation:
``bash`
npm install terme
Vanilla TypeScript:
`typescript
import { Terminal } from 'terme';
const terminal = await Terminal.create(
canvas,
'/fonts/cascadia-code.png',
'/fonts/cascadia-code.json'
);
terminal.setContent(['Hello, World!']);
terminal.onInput((text) => console.log('Input:', text));
`
React:
`tsx
import { TerminalCanvas } from 'terme/react';
metricsUrl="/fonts/cascadia-code.json"
config={{ fontSize: 14 }}
onTerminalReady={(terminal) => {
terminal.setContent(['Hello from React!']);
}}
/>
`
Font Setup:
`bash`Copy bundled fonts to your public directory
cp -r node_modules/terme/dist/fonts public/fonts
- WebGL 2 Rendering: Efficient GPU-accelerated text rendering using MSDF (Multi-channel Signed Distance Field) fonts
- Virtual Scrolling: Handles millions of lines with O(1) access and minimal memory overhead
- High Performance: Target 120+ FPS with zero allocations in hot paths
- Instanced Rendering: Single draw call per frame for optimal GPU utilization
- Text Input: Keyboard input with caret positioning
- Responsive: Automatic DPI handling and resize support
1. Install msdfgen (required for font atlas generation):
`bash
# macOS
brew install msdfgen
# Arch Linux
sudo pacman -S msdfgen
# From source (Linux/Windows)
git clone https://github.com/Chlumsky/msdfgen.git
cd msdfgen
cmake -S . -B build
cmake --build build
sudo cmake --install build
`
2. Node.js: v18+ recommended
`bash`
npm install terme
For React integration:
`bash`
npm install terme react react-dom
`bashClone repository
git clone https://github.com/victorqueiroz/Terme.git
cd Terme
Usage
$3
The demo application generates 1 million lines of sample content to showcase performance:
`bash
npm run dev
`Open http://localhost:5173 in your browser.
Controls:
- Scroll: Mouse wheel or trackpad
- Navigate: Arrow keys move the caret
- Input: Type any key (logged to console)
$3
`typescript
import { TerminalCanvas } from 'terme/react';
import type { Terminal } from 'terme';function MyApp() {
const handleTerminalReady = (terminal: Terminal) => {
// Set initial content
terminal.writeLine('Hello, World!');
terminal.writeLine('This is a terminal renderer');
terminal.writeLine('Powered by WebGL and MSDF');
// Setup callbacks
terminal.onInput((text) => console.log('Input:', text));
terminal.onScroll(() => console.log('Scrolled'));
};
return (
fontAtlasUrl="/fonts/cascadia-code.png"
metricsUrl="/fonts/cascadia-code.json"
config={{
fontSize: 14,
lineHeight: 1.5,
backgroundColor: [0, 0, 0, 1],
overscan: 10,
}}
onTerminalReady={handleTerminalReady}
/>
);
}
`$3
`typescript
import { Terminal } from 'terme';// Create terminal (automatically initializes and attaches to canvas)
const terminal = await Terminal.create(
canvas,
'/fonts/cascadia-code.png',
'/fonts/cascadia-code.json',
{
fontSize: 14,
lineHeight: 1.5,
}
);
// Write content
terminal.writeLine('Hello, World!');
terminal.writeLine('This is line 2');
// Control scrolling
terminal.scrollTo(100);
// Setup callbacks
terminal.onInput((text) => console.log('Input:', text));
terminal.onScroll(() => console.log('Scrolled'));
// Cleanup when done
terminal.dispose();
`Architecture
`
src/
├── core/
│ ├── Terminal.ts # Main orchestrator
│ ├── font/ # Font atlas loading, metrics
│ ├── text/ # Text buffer, layout engine
│ ├── scroll/ # Virtual scrolling, viewport
│ ├── input/ # Caret, keyboard handling
│ └── renderer/ # WebGL pipeline
├── ui/react/ # React wrapper
└── demo/ # Demo application
`$3
1. Virtual Scrolling: Paged array (1024 lines/page) for O(1) access
2. Rendering: Instanced rendering with single quad geometry
3. Font Pipeline: msdfgen → Node.js packing → PNG atlas + JSON metrics
4. Performance: Reuse typed arrays, zero allocations in hot paths
5. React Integration: Terminal owns RAF loop, React is thin wrapper
Font Generation
The font generation script calls msdfgen for each character and packs them into a texture atlas:
`bash
npm run generate:font
`This creates:
-
public/fonts/cascadia-code.png - MSDF texture atlas
- public/fonts/cascadia-code.json - Glyph metricsCustomization:
Edit
scripts/generate-font-atlas.mjs to:
- Change font file path
- Add/remove characters
- Adjust glyph size or MSDF range
- Modify atlas packing algorithmPerformance
$3
- ✅ Zero allocations in scroll/layout hot paths
- ✅ Reuse typed arrays and object pools
- ✅ Single texture bind per frame
- ✅ Single draw call via instancing
- ✅ Dirty flag prevents unnecessary renders
- ✅ Overscan reduces layout thrashing
$3
- 1M lines: Smooth 120 FPS scrolling
- Memory: ~200MB for 1M lines @ 100 chars/line
- Init time: <100ms for font loading + WebGL setup
- Draw call: 1 per frame for all visible glyphs
Troubleshooting
$3
`
Error: msdfgen not found in PATH
`
Install msdfgen (see Prerequisites above).$3
`
Error: Failed to load font metrics
`
Run npm run generate:font` before starting the dev server.- React 18.2+ - UI framework
- TypeScript 5.2+ - Type safety
- Vite 5.0+ - Build tool
- WebGL 2 - GPU rendering
- twgl.js 5.5+ - WebGL utilities
- gl-matrix 3.4+ - Matrix math
- msdfgen - Font atlas generation
MIT
- MSDF font rendering: Based on Chlumsky/msdfgen
- Cascadia Code font: microsoft/cascadia-code