Plot overview
If you’ve reached for Observable Plot or ggplot2 before, insomni-plot will feel
like home — you describe a chart by what the data means, not by what to draw.
Map columns to channels, pick your marks, and let the library work out scales,
axes, and layout. The twist: it compiles that spec into insomni
layers and renders on the GPU, so the same declarative grammar you’d use for
a 200-point scatter also drives a 200,000-point one without breaking a sweat.
Two things to know up front, because they shape everything else:
- The
Chartbuilder is immutable. Every method —.layer(),.scale(),.axes(), and friends — returns a newChart. Your base spec is never mutated, so you can fork it freely (more on why that’s useful below). - Nothing renders until you ask. A
Chartis just a description. It stays inert until you call.mount(canvas),.render(target), or.toSVG().
Quick start
Section titled “Quick start”plot(spec) takes a PlotSpec<T> — at minimum { data } — and hands you back a
Chart<T>. From there you add marks with .layer(...) and tune the rest:
import { plot, point } from "insomni-plot";
type Row = { weight: number; mpg: number; cylinders: number };
const data: Row[] = [ { weight: 3.2, mpg: 21, cylinders: 6 }, { weight: 1.6, mpg: 39, cylinders: 4 }, { weight: 4.1, mpg: 15, cylinders: 8 },];
// WebGPU needs a device — grab one before you mount.const device = await (await navigator.gpu.requestAdapter())!.requestDevice();const canvas = document.querySelector("canvas")!;
const chart = plot<Row>({ data }) .layer(point({ x: "weight", y: "mpg", color: "cylinders" })) .axes({ x: { title: "Weight" }, y: { title: "MPG" } }) .title("Fuel economy");
const mounted = chart.mount(canvas, { device });
// Later — swap the data live, then tear it all down.mounted.setData(data);mounted.destroy();Mapping data to channels
Section titled “Mapping data to channels”An aesthetic is how a data column becomes something you can see — a position,
a color, a size. Every channel (x, y, color, size, …) accepts three
shapes, all unified under the Aes<T, V> type:
- a column name —
x: "weight" - an accessor —
x: (d) => d.weight * 2 - a constant —
color: "#888"orsize: 4
Here’s a nicety you’ll appreciate: column-name autocomplete is type-narrowed. If
a key’s value type doesn’t match what the channel expects, TypeScript rejects it
at compile time — so you find out about x: "name" on a numeric axis before you
ever hit the GPU. The full channel list lives in
Scales & aesthetics.
Why immutability earns its keep
Section titled “Why immutability earns its keep”Because every builder method returns a fresh Chart, a base spec is something
you can branch from without fear:
const base = plot<Row>({ data }).axes({ x: { title: "t" } });
const dots = base.layer(point({ x: "t", y: "v" }));const both = dots.layer(line({ x: "t", y: "v" }));// `base` and `dots` are still exactly what they were.Cheap to fork, cheap to share, cheap to re-derive. Faceting and small-multiples lean on this directly.
Two entry points
Section titled “Two entry points”insomni-plot— the grammar.plot(spec)returns an immutableChartyou compose with geoms, coordinate systems (coordCartesian/coordPolar/coordRadial), themes, faceting, annotations, color palettes, and stats helpers (bin,kde,linearFit, …). This is the one you want 90% of the time.insomni-plot/core— the imperative primitives the grammar is built on: scales (linearScale,bandScale,timeScale,logScale, …), axis builders (bottomAxis,leftAxis, …), mark builders (pointMark,lineMark,barMark,areaMark, stacking), legends, color scales, formatters, and the dataviewport/navigator. Drop down here when you want to hand-wire a render without theChartbuilder, or pull a single primitive out on its own.
Where to go next
Section titled “Where to go next”- Grammar of graphics — the declarative model and the frame pipeline (aes → scales → coord → geoms → layers).
- Geoms —
point,line,bar,area, and the rest of the mark vocabulary. - Scales & aesthetics — channels, scale types, domains, ranges, palettes, and legends.
- Axes & coordinates — ticks, gridlines, coordinate systems, and faceting.
- Mount, render & export —
.mount()/.render()/.toSVG()and interaction wiring. - Core (imperative) API — the
insomni-plot/coreprimitives.