insomni-node
v0.1.0-alpha.2
Published
Headless Node adapter for the insomni renderer (Dawn/WebGPU).
Readme
insomni-node
Headless Node.js adapter for the insomni renderer. Runs the full
GPU pipeline on Dawn/WebGPU (the webgpu
npm package) without a browser, canvas, or display server. Designed for
server-side chart rendering, CI screenshot tests, and PNG export pipelines.
Install
pnpm add insomni-node insomniwebgpu (native Dawn addon) is a peer dependency — install it alongside:
pnpm add webgpuPrebuilt Dawn binaries are bundled inside the webgpu package. Verified on
arm64 macOS; the webgpu package also ships Linux x64 and Windows x64
binaries (untested by this package).
API
createNodeRenderer(options): Promise<NodeRenderer>
Options
| Option | Type | Default | Description |
| ------------- | ------------------ | ------------------------------------------ | ---------------------------------------------------------------------------------- |
| width | number | required | Offscreen texture width in pixels. |
| height | number | required | Offscreen texture height in pixels. |
| sampleCount | number | renderer default (1 or 4) | MSAA sample count. |
| format | GPUTextureFormat | navigator.gpu.getPreferredCanvasFormat() | Color attachment format. |
| gpu | GPUHandle | undefined | Pre-built GPU handle to reuse; when omitted, one is acquired and owned internally. |
| persistent | boolean | false | Accepted for API symmetry; has no effect (no swap chain). |
Return shape — NodeRenderer
| Member | Type | Description |
| --------------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| renderer | Renderer2D | Core insomni renderer. Call renderer.setBackground(rgba(...)) before rendering. |
| target | OffscreenRenderTarget | The offscreen color+depth textures. |
| device | GPUDevice | Underlying WebGPU device. |
| frame(layers, maxFrames?) | Promise<void> | Render layers (a Layer[]) and drain the GPU queue. maxFrames extra ticks help async resources settle (default: 1). |
| readPixels() | Promise<PixelData> | Copy rendered pixels to CPU as a tight RGBA Uint8Array. |
| toPNG(opts?) | Promise<Uint8Array> | Render → readback → PNG encode. Returns a Uint8Array PNG buffer (unpremultiplied by default). |
| resize(w, h) | void | Resize the renderer and offscreen target. |
| dispose() | void | Destroy the renderer, offscreen target, and (if owned) the GPU handle. |
Offscreen-only caveat
insomni-node has no swap chain or canvas. present() is a no-op. All
readback goes through copyTextureToBuffer (via readPixels()). The rendered
result is only accessible via readPixels() or toPNG() — there is no
on-screen display.
Example
import { createNodeRenderer } from "insomni-node";
import { createLayer, rgba } from "insomni";
import { writeFileSync, mkdirSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const W = 256,
H = 256;
const api = await createNodeRenderer({ width: W, height: H });
api.renderer.setBackground(rgba(0.05, 0.06, 0.09, 1));
const scene = createLayer({ space: "ui" });
scene.pushRect({
x: 32,
y: 32,
width: 192,
height: 192,
fill: rgba(0.36, 0.7, 1, 1),
cornerRadius: 24,
});
scene.pushRect({
x: 72,
y: 72,
width: 112,
height: 112,
fill: rgba(0.96, 0.45, 0.62, 1),
cornerRadius: 16,
});
await api.frame([scene]);
const png = await api.toPNG();
const outDir = join(dirname(fileURLToPath(import.meta.url)), "..", ".dev-shots");
mkdirSync(outDir, { recursive: true });
const outPath = join(outDir, "smoke.png");
writeFileSync(outPath, png);
console.log(`wrote ${outPath} (${png.byteLength} bytes)`);
api.dispose();Run it after building the package:
node packages/insomni-node/examples/smoke.mjsFurther reading
The downstream integration plan (migrating the demo harness to Dawn):
plans/dawn-node-webgpu-migration.md
