Skip to main content

Headless rendering

@oh-just-another/headless renders scene documents to SVG/PNG in plain Node.js โ€” no DOM, no browser, no canvas shim. (Server-side by definition, so no live demo on this page.)

pnpm add @oh-just-another/headless
pnpm add @resvg/resvg-js # optional peer dep โ€” only needed for renderToPng

Scene JSON โ†’ SVG + PNGโ€‹

Both functions accept an in-memory Scene or the JSON string that @oh-just-another/serialization emits (parsed and validated for you):

// render.mjs โ€” run with: node render.mjs
import { readFile, writeFile } from "node:fs/promises";
import { renderToSvg, renderToPng } from "@oh-just-another/headless";

const sceneJson = await readFile("scene.json", "utf8");

// SVG โ€” synchronous, pure JS, no optional deps.
await writeFile("out.svg", renderToSvg(sceneJson));

// PNG โ€” async, rasterized via @resvg/resvg-js (Rust โ†’ WASM).
await writeFile("out.png", await renderToPng(sceneJson, { scale: 2 }));

Optionsโ€‹

OptionApplies toEffect
width / heightbothOutput size in CSS px; defaults to the scene viewport's size
measureTextbothCustom text measurer (defaults to a built-in approximation)
skipInstallbothSkip the implicit installBuiltinRenderers() when you registered your own shape renderers
scalePNGDevice-pixel factor; 2 = retina. Default 1
backgroundPNGColour behind the scene. Default white
fitToWidth / fitToHeightPNGFit output to N device px (aspect preserved); overrides scale

Notesโ€‹

  • renderToSvg(scene, options?) returns a string; renderToPng(scene, options?) returns a Promise<Uint8Array> and throws with an install hint if @resvg/resvg-js is missing. SVG-only consumers never pay for the ~3 MB WASM payload.
  • Typical uses: rendering diagrams in CI, OG images, thumbnails for a document list, server-side export endpoints. Combine with data conversion to turn Mermaid/dot/drawio input into images.