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

ntk

v8.8.0

Published

Desktop UI toolkit for X11 with canvas-like 2d and OpenGL rendering

Downloads

9,746

Readme

ntk

node.js desktop UI toolkit for X11

A set of wrappers around the low level node-x11 module to simplify X Window UI programming — window creation, DOM-style event handling, 2d/3d graphics — using API concepts you already know from the web.

Everything, including font rasterization, is pure JavaScript: npm install never compiles anything.

Docs & live playground: https://sidorares.github.io/ntk/ — the playground runs ordinary ntk code in your browser against node-x11's in-browser pure-JS X server (XRender included) with bundled fonts. The same server also works headless in node — see docs/xserver.md — so ntk apps and tests can run with no real X server at all.

Installation

npm install ntk

Requires Node.js >= 20.19 and an X server. Full documentation lives in docs/.

Basic usage

import { createClient } from 'ntk';

const app = await createClient();
const wnd = app.createWindow({ width: 500, height: 300, title: 'Hello' });
wnd.on('mousedown', (ev) => wnd.setTitle(`click: ${ev.x},${ev.y}`));
wnd.map();

2d graphics

Each window (or pixmap) can create a 2d canvas implementing the HTML context2d api via the XRender extension — paths (arcs, beziers, Path2D with SVG path data, non-zero/even-odd fill rules), transforms with save()/restore(), clipping, globalAlpha and Porter-Duff composite ops (docs/context-2d.md). Most operations are performed on the X server side (image composition, scaling, blur, text composition, gradients etc). Text is fully shaped in pure JS — OpenType kerning/ligatures and complex scripts (fontkit), bidi (bidi-js), automatic font fallback — rasterized by a built-in scanline rasterizer and cached server-side as XRender glyphs, so drawing a line of text costs about a byte per glyph on the wire. Font names resolve through fontconfig (fc-match). Very large and continuously animated sizes render as server-side trapezoids instead of cached bitmaps. A TextLayout engine wraps styled text to a target width — see docs/text.md.

PNG/JPEG images decode client-side (loadImage) and composite server-side via ctx.drawImage (docs/images.md). An SvgView widget renders static SVG (shapes, gradients, transforms, use) through the same 2d pipeline (docs/svg.md).

Rendering documents — markdown, formulas, rich text — is not ntk's job: it draws, and a document is a tree of layout decisions on top of that. @react-x11/components is where those live, over the react-x11 renderer.

import { createClient } from 'ntk';

const app = await createClient();
const wnd = app.createWindow({ width: 800, height: 600 });
const ctx = wnd.getContext('2d');

wnd.on('mousemove', (ev) => {
  const gradient = ctx.createRadialGradient(0, 0, 10, ev.x, ev.y, 500);
  gradient.addColorStop(0, 'red');
  gradient.addColorStop(0.5, 'green');
  gradient.addColorStop(1, 'rgba(255, 255, 255, 0)');
  ctx.fillStyle = gradient;
  ctx.fillRect(0, 0, ctx.width, ctx.height);
});

wnd.map();

ctx.drawImage() also accepts a node-canvas canvas as a source — for images with lots of drawing calls it might be more efficient to draw locally and transfer pixels to the server when ready.

Frame pacing & networked displays

Noisy events (resize, mousemove, expose) are coalesced into paced frames — the latest state wins, nothing queues up — and each frame is fenced with a server round-trip, so rendering automatically slows to the connection's real throughput instead of drawing a trail of stale updates over ssh-forwarded displays. Animation uses the DOM-style requestAnimationFrame:

function frame(now) {
  // ... draw ...
  wnd.requestAnimationFrame(frame); // ~60fps locally, RTT-paced remotely
}
wnd.requestAnimationFrame(frame);

See docs/window.md for the knobs (frameInterval, frameSync, coalesceEvents) and the raw uncoalesced event stream.

Resource management

Server-side resources support using / await using (Node 24+):

{
  await using app = await createClient();
  using pixmap = app.createPixmap({ width: 256, height: 256, depth: 24 });
  // ... draw ...
} // pixmap freed, connection closed

3d graphics

Two backends, chosen by glPolicy (docs/context-gles.md):

  • direct (opt-in) — shader GL on the real GPU with no pixels on the socket: OpenGL ES 2 over DRI3 + Present on Linux, CGL over the Apple-DRI extension on macOS/XQuartz. Needs the optional x11-dri addon.
  • indirect GLX (default) — most of the OpenGL 1.4 api, serialized into the X connection. Note that on many systems indirect GLX is disabled by default — you'll need to enable it for gl to work.
import { createClient } from 'ntk';

const app = await createClient();
// GLX drawables need a GLX-capable visual, chosen before the window exists
const glx = await app.chooseGLXConfig({ DEPTH_SIZE: 24 });
const wnd = app.createWindow({ width: 300, height: 300, visual: glx.visual, depth: glx.depth });
wnd.map();

const gl = wnd.getContext('opengl', glx);
gl.ClearColor(0.3, 0.3, 0.3, 0.0);
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.Begin(gl.TRIANGLES);
gl.Color3f(1, 0, 0);
gl.Vertex3f(-1, -1, 0);
gl.Vertex3f(1, -1, 0);
gl.Vertex3f(0, 1, 0);
gl.End();
gl.SwapBuffers();

See examples/ for more (teapot, GL clock, textures, text rendering).

High level widgets / layout management etc

Likely to be implemented outside as part of a react renderer (react-x11).