SVG export
SVGRenderer is a vector / static-export backend. It consumes the same
readonly Layer[] that Renderer2D.render() takes and emits an SVG document as
a string — never touching the GPU. Because it shares the layer model, a chart
exports without a separate spec: build your layers once, draw them to the canvas
and to SVG.
createSVGRenderer. Use it for static export, print, server-side rendering (it runs in pure Node), and snapshot tests.
Quick start
Section titled “Quick start”import { createSVGRenderer } from "insomni";
const svgRenderer = createSVGRenderer({ width: 800, height: 600 });svgRenderer.setCamera({ x: 0, y: 0, zoom: 1 });
const svgString = svgRenderer.render(layers); // same Layer[] you pass to render()// In a browser/jsdom you can also get a DOM node:const node = svgRenderer.element(); // SVGSVGElement (throws in pure Node)createSVGRenderer(options?) is a thin factory over
new SVGRenderer(options?).
API reference
Section titled “API reference”SVGRendererOptions
Section titled “SVGRendererOptions”| Option | Default | Description |
| -------- | -------- | -------------------------------------------------------------------------- |
| width | 600 | SVG document width (also the viewBox width). |
| height | 400 | SVG document height. |
| camera | identity | Initial camera ({ x, y, zoom, rotation }) used for world-space layers. |
| dpr | 1 | Device-pixel ratio used to scale ui layers into the viewBox. |
SVGRenderer
Section titled “SVGRenderer”| Member | Description |
| ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| render(layers) | Render layers (composited in array order) to an SVG string, also caching it. |
| svg() | The string from the most recent render() (empty before the first). |
| element() | Parse the last SVG into an SVGSVGElement. Requires a DOM (DOMParser); throws in pure Node — use svg() there. |
| setCamera(state) / getCamera() | Get/set the camera. |
| fitCameraToBounds(bounds, options?) | Fit the camera to a Bounds2D. |
| setBackground(color) | Background rect drawn when alpha > 0. |
| resize(width, height) / setDpr(dpr) | Update document size / DPR. |
| getViewMatrix() / worldToScreen(x, y) / screenToWorld(x, y) | Camera math helpers (mirror the GPU renderer). |
| width / height / dpr | Readonly getters. |
| compute() | No-op (SVG has no GPU compute phase; warns once). |
| destroy() | Clear the cached SVG. |
Supported primitives
Section titled “Supported primitives”The backend walks each layer’s UberPack (and GlyphPack) and emits semantic
SVG elements:
| insomni kind | SVG element |
| ------------------ | -------------------------------------------------------------------- |
| rect | <rect> (with rx/ry for corner radius, rotate(...) transform) |
| circle | <circle> |
| ellipse | <ellipse> (with rotate(...) transform) |
| segment | <line> |
| curve (cubic) | <path> (cubic C) |
| arc | <path> (A; full rings split into two semicircles) |
| triangle / polygon | <polygon> |
| text (glyphs) | <text> (reconstructed from the glyph atlas) |
Per-layer space (world / ui), the camera, per-shape group transforms
(folded into the layer matrix), and per-layer clip rects (<clipPath>) are all
honored.
Not supported (warn-and-skip)
Section titled “Not supported (warn-and-skip)”- Sprites / textures (
TYPE_SPRITE) — skipped with a one-time warning. BufferLayer/ custom drawables — they expose no walkableUberPack, so a layer that lacks one is skipped.compute()— no-op.
Fidelity caveats vs the GPU path
Section titled “Fidelity caveats vs the GPU path”The UberPack does not retain the original high-level shape records — it stores
the packed instance fields, so the SVG backend recovers from those:
- Positions, endpoints, control points, triangle vertices, arc center/radius/angles are stored as full f32 → recovered losslessly. These are the values a vector consumer cares most about.
- rect/circle/ellipse size, rotation, stroke width, corner radius, curve widths are stored as f16 → recovered to ~3 significant digits.
- All colors are unorm8 → recovered to 8 bits per channel.
This f16/unorm8 loss is the same loss the live GPU render shows and is imperceptible at export resolution. Additional approximations specific to SVG:
- Tapered curves — SVG has no per-endpoint stroke taper; tapered curves are emitted as a uniform-width path (the two widths are averaged), with a one-time warning.
- Text — glyph strings are not retained, so runs are reconstructed by grouping
consecutive glyphs that share a baseline/color into one
<text>. The baseline is an export-time approximation (glyph top + quad height), so exact positions may drift a fraction of a glyph.
Example: export a chart to a file
Section titled “Example: export a chart to a file”import { createSVGRenderer } from "insomni";
function exportSvg(layers, camera) { const r = createSVGRenderer({ width: 1024, height: 768, camera }); return r.render(layers); // write this string to a .svg file}See also
Section titled “See also”- Renderer & frame loop — the GPU
render(layers)this mirrors. - Layers & groups — the
Layermodel both backends share. - Spaces & cameras —
world/uicoordinate spaces. - Packing — the f16 / unorm8 instance layout the export reads back.