npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@beamterm/renderer

v1.0.0

Published

High-performance WebGL2 terminal renderer with sub-millisecond render times

Readme

@beamterm/renderer

npm version License: MIT

High-performance WebGL2 terminal renderer achieving sub-millisecond render times through GPU-accelerated instanced rendering. Pure WASM + WebGL2 with zero runtime dependencies.

Features

  • Rich text styling — bold, italic, underline, strikethrough with 24-bit color
  • Batch updates — all cell changes are collected and uploaded to the GPU in a single pass
  • Two font atlas modes — pre-rasterized static atlas or on-demand dynamic atlas with any browser font
  • Built-in text selection — linear and block modes with clipboard integration and URL detection
  • Responsive — automatic terminal resizing with HiDPI support
  • TypeScript definitions included

Requires a browser with WebGL2 and WASM support (any modern browser).

Installation

npm install @beamterm/renderer

Or via CDN:

<script src="https://unpkg.com/@beamterm/renderer@latest/dist/cdn/beamterm.min.js"></script>
<script>
  await Beamterm.init();
  const renderer = new Beamterm.BeamtermRenderer('#terminal');
</script>

Quick Start

import {
  main as init,
  style,
  cell,
  BeamtermRenderer,
} from "@beamterm/renderer";

await init();

// Create renderer — uses the embedded static font atlas by default
const renderer = new BeamtermRenderer("#terminal");

// Or use a dynamic atlas that rasterizes any browser font on demand
// const renderer = BeamtermRenderer.withDynamicAtlas('#terminal', ['JetBrains Mono', 'Fira Code'], 16.0);

const { cols, rows } = renderer.terminalSize();
console.log(`Terminal: ${cols}x${rows} cells`);

const batch = renderer.batch();

batch.clear(0x1a1b26);
batch.text(
  2,
  1,
  "Hello, Beamterm!",
  style().bold().underline().fg(0x7aa2f7).bg(0x1a1b26),
);
batch.cell(0, 0, cell("🚀", style().fg(0xffffff)));
batch.fill(1, 3, 18, 1, cell("─", style().fg(0x565f89).bg(0x1a1b26)));

renderer.render();

API Reference

BeamtermRenderer

The main renderer class. Manages the WebGL2 context and rendering pipeline.

Constructors

| Signature | Description | | ------------------------------------------------------------------------------------------- | -------------------------------------------------------- | | new BeamtermRenderer(canvasSelector) | Create with the embedded static font atlas | | BeamtermRenderer.withDynamicAtlas(canvasSelector, fontFamilies, fontSize, autoResizeCss?) | Create with a dynamic atlas using browser fonts | | BeamtermRenderer.withStaticAtlas(canvasSelector, atlasData?, autoResizeCss?) | Create with custom .atlas data (or null for default) |

Rendering

| Method | Description | | ----------------------- | ------------------------------------------------------- | | batch() | Create a new Batch for buffering cell updates | | render() | Render the current frame to the canvas | | resize(width, height) | Resize the canvas and recalculate terminal dimensions | | terminalSize() | Returns { cols, rows } — terminal dimensions in cells | | cellSize() | Returns { width, height } — cell dimensions in pixels |

Font Switching

Swap the font atlas at runtime. Existing cell content is preserved and translated to the new atlas.

| Method | Description | | ------------------------------------------------- | ------------------------------------------------------------- | | replaceWithDynamicAtlas(fontFamilies, fontSize) | Switch to a dynamic atlas with the given fonts | | replaceWithStaticAtlas(atlasData?) | Switch to a static atlas (Uint8Array or null for default) |

Selection & Mouse

| Method | Description | | ------------------------------------------------------------- | --------------------------------------------------------------------------- | | enableSelection(mode, trimWhitespace) | Enable built-in text selection | | enableSelectionWithOptions(mode, trimWhitespace, modifiers) | Selection that requires modifier keys (e.g. Shift+click) | | setMouseHandler(callback) | Set a custom mouse event handler (receives MouseEvent) | | getText(query) | Extract text for a CellQuery region | | findUrlAt(col, row) | Detect a URL at the given cell position — returns UrlMatch or undefined | | copyToClipboard(text) | Copy text to the system clipboard | | clearSelection() | Clear any active selection | | hasSelection() | Check if there is an active selection |

Batch

Buffers cell updates for efficient GPU upload. Create one per frame via renderer.batch().

| Method | Description | | ------------------------------------- | ----------------------------------------------------------------------- | | clear(bgColor) | Clear the entire terminal with a background color | | text(x, y, text, style) | Write a string at (x, y) with uniform styling — fastest for text runs | | cell(x, y, cellData) | Update a single cell | | cells(array) | Update multiple cells — each element is [x, y, cellData] | | fill(x, y, width, height, cellData) | Fill a rectangular region |

All changes are automatically uploaded when renderer.render() is called.

CellStyle

Fluent builder for text styling. Create one with style().

const heading = style().bold().fg(0x7aa2f7).bg(0x1a1b26);
const warning = style().bold().italic().fg(0xe0af68);

| Method | Description | | ----------------- | ------------------------------------------ | | fg(color) | Foreground color (0xRRGGBB) | | bg(color) | Background color (0xRRGGBB) | | bold() | Bold | | italic() | Italic | | underline() | Underline | | strikethrough() | Strikethrough | | bits | (property) Combined style bits as a number |

CellQuery

Describes a rectangular or linear region of cells for text extraction.

const query = new CellQuery(SelectionMode.Linear)
  .start(0, 2)
  .end(40, 5)
  .trimTrailingWhitespace(true);

const text = renderer.getText(query);

ModifierKeys

Modifier key flags for enableSelectionWithOptions().

// Require Shift+Click to start selection
renderer.enableSelectionWithOptions(
  SelectionMode.Linear,
  true,
  ModifierKeys.SHIFT,
);

// Require Ctrl+Shift+Click
renderer.enableSelectionWithOptions(
  SelectionMode.Block,
  true,
  ModifierKeys.CONTROL.or(ModifierKeys.SHIFT),
);

| Static property | Description | | ---------------------- | ------------------------- | | ModifierKeys.NONE | No modifier keys required | | ModifierKeys.SHIFT | Shift key | | ModifierKeys.CONTROL | Ctrl key | | ModifierKeys.ALT | Alt / Option key | | ModifierKeys.META | Cmd / Windows key |

Combine with .or(): ModifierKeys.SHIFT.or(ModifierKeys.ALT)

Enums

SelectionModeLinear (text flow, like a terminal) or Block (rectangular, like a text editor).

MouseEventTypeMouseDown, MouseUp, MouseMove, Click, MouseEnter, MouseLeave.

Helper Functions

| Function | Description | | --------------------- | --------------------------------------------------------- | | main() | Initialize the WASM module (typically imported as init) | | style() | Create a new CellStyle | | cell(symbol, style) | Create a Cell |

Color Format

Colors are 24-bit RGB values: 0xRRGGBB.

const white = 0xffffff;
const red = 0xff0000;
const tokyoBg = 0x1a1b26;

Common Patterns

Animation Loop

function animate() {
  const batch = renderer.batch();
  batch.clear(0x1a1b26);
  batch.text(0, 0, `Frame: ${Date.now()}`, style().fg(0xc0caf5));
  renderer.render();
  requestAnimationFrame(animate);
}

Responsive Terminal

window.addEventListener("resize", () => {
  renderer.resize(window.innerWidth, window.innerHeight);
  redrawTerminal();
});

URL Detection

renderer.setMouseHandler((event) => {
  if (event.event_type === MouseEventType.Click) {
    const match = renderer.findUrlAt(event.col, event.row);
    if (match) window.open(match.url, "_blank");
  }
});

Examples

See the examples/ directory:

Performance Tips

  • Prefer batch.text() over multiple batch.cell() calls for strings with uniform styling
  • Use batch.fill() for large rectangular regions
  • Batch all updates within a single renderer.batch() / renderer.render() cycle
  • Reuse CellStyle objects when possible

License

MIT — see LICENSE for details.

Links