Geoms
A geom is the mark you actually see on the page — a dot, a line, a bar. In
code it’s a factory: geom(channels, options?) returns a Geom<T>, and you add
it to a chart with .layer(...). Stack as many as you like; layers draw in the
order you declare them, so the last one sits on top. Every channel you pass is an
Aes<T, V> — a column name, an accessor, or a
constant — and the channels object is where mappings go while options is for
fixed styling.
Here’s the full vocabulary, all exported from insomni-plot:
aggregate,area,band,bar,boxplot,bullet,connectedScatter,dumbbell,ganttTrack,histogram,interval,line,lollipop,point,radar,ribbon,ridgeline,rug,rule,smooth,statRolling,text,tile,violin.
The rest of this page walks each one. They share a rhythm: a line on what it’s for, the channels it reads, and the options that style it.
Your bread-and-butter scatter and bubble marks. x and y are the only things
it can’t do without; map size and you’ve got a bubble chart, map color and
you’ve split it by category.
| Channel | Type | Required |
|---|---|---|
x, y | number | Date | yes |
color | any (categorical or continuous) | no |
size | number | no |
shape | categorical → shape palette | no |
alpha | number | no |
borderStyle | categorical → solid | open | dashed | dotted | no |
overlayGlyph | categorical → secondary glyph | no |
overlayScale | number | no |
Options: fill, radius, stroke, strokeWidth, shape
(PointShapeKind), borderStyle, overlayGlyph, overlayScale, label, and
glow ({ color?, radius?, alpha? } — a soft translucent disc behind each
marker, handy for making a highlighted point pop).
import { plot, point } from "insomni-plot";
plot<Row>({ data }).layer( point({ x: "weight", y: "mpg", color: "origin", size: "hp" }, { radius: 3 }),);A connected polyline. Hand it a color channel and it splits the data into one
stroke per category — exactly what you want for a multi-series time series. Use
order if your rows aren’t already in the sequence you want drawn.
| Channel | Type | Required |
|---|---|---|
x, y | number | Date | yes |
color | categorical | no |
order | number | Date | no |
Options: stroke, strokeWidth, curve (LineCurve), curveSamples,
dashPattern, dashStyle (dashed \| dotted), nearestX, label.
import { line } from "insomni-plot";
plot<Row>({ data }).layer( line({ x: "date", y: "price", color: "ticker" }, { curve: "monotone-x" }),);connectedScatter
Section titled “connectedScatter”A line whose vertex order is driven by a third variable, with optional point heads at each datum. Great for trajectories through a two-variable space — think country-level GDP vs. life-expectancy walks over time.
| Channel | Type | Required |
|---|---|---|
x, y | number | Date | yes |
order | number | Date | yes |
color | categorical | no |
size, alpha | number | no |
shape | categorical | no |
Options: line (LineOptions), point (PointOptions or false for a bare
path).
Filled area from a baseline. A single y column draws 0 → y; an array of
column keys draws a stacked area keyed by column name. Reach for position: "fill" when you care about proportions more than absolute values.
| Channel | Type | Required |
|---|---|---|
x | number | Date | yes |
y | number | Date or (keyof T)[] | yes |
color | categorical | no |
Options: fill, stroke, strokeWidth, position ("identity" /
"stack" / "fill"), order (StackOrder), nearestX, curve
(LineCurve), curveSamples, label.
import { area } from "insomni-plot";
plot<Row>({ data }).layer(area({ x: "date", y: ["us", "eu", "asia"] }, { position: "stack" }));Categorical bars — the workhorse of comparison charts. A single y column draws
one bar per category; an array of column keys draws multi-series bars.
Orientation is auto-detected from scale types and can be forced with orientation.
| Channel | Type | Required |
|---|---|---|
x | string | number | Date | yes |
y | string | number | Date or (keyof T)[] | yes |
color | categorical | no |
Options: orientation, fill, stroke, strokeWidth, cornerRadius,
borderStyle, position (BarPosition), order, groupPadding,
showTotals ((total, datum, index) => string),
showValues ((value, datum, index, key?) => string),
labelColor, labelFontSize, label.
import { bar } from "insomni-plot";
plot<Row>({ data }).layer( bar({ x: "quarter", y: ["q1", "q2", "q3", "q4"] }, { position: "dodge" }),);histogram
Section titled “histogram”Bins one numeric variable and counts the results. Provide exactly one of x
(vertical bars) or y (horizontal). A color channel splits the sample into
per-group bins for side-by-side or stacked comparisons.
| Channel | Type | Required |
|---|---|---|
x or y | number | one of |
color | categorical | no |
Bin selection (in priority order): breaks → binwidth → bins → rule
(sturges / rice / scott / fd). Other options: domain, nice,
closed, y measure (count / frequency / density / proportion), position,
mirror, fillAlpha, fill, stroke, strokeWidth, cornerRadius, gap,
groupPadding, showCounts.
import { histogram } from "insomni-plot";
plot<Row>({ data }).layer(histogram({ x: "weight" }, { rule: "fd", y: "density" }));smooth
Section titled “smooth”A regression fit with an optional confidence ribbon. One curve per color group.
Pair it with point() on the same data for the classic scatter + trend combo.
| Channel | Type | Required |
|---|---|---|
x | number | Date | yes |
y | number | yes |
color | categorical | no |
Options: method (SmoothMethod — "lm" default, "poly", "loess"),
degree, span, ci (true default = 95% ribbon, false disables the
ribbon, a number sets an explicit confidence level), samples, stroke,
strokeWidth, ribbonFill, ribbonOpacity, label, nearestX.
import { point, smooth } from "insomni-plot";
plot<Row>({ data }) .layer(point({ x: "weight", y: "mpg" })) .layer(smooth({ x: "weight", y: "mpg" }, { method: "loess", span: 0.6 }));statRolling
Section titled “statRolling”A rolling-window statistic — moving average, sum, min, max — drawn as a line. Useful for taming noisy time series without switching to a separate smoothing model.
| Channel | Type | Required |
|---|---|---|
x | number | Date | yes |
y | number | yes |
color | categorical | no |
Options: window (RollingWindow — required), statistic (default "mean"),
axis, filter, curve, stroke, strokeWidth, dashStyle,
nearestX (default true), label.
ribbon
Section titled “ribbon”A filled band between two y-bounds at each x. Reach for this when you have a pre-computed range — confidence intervals, forecast envelopes, sensor min/max — and you want the fill without the stat layer.
| Channel | Type | Required |
|---|---|---|
x | number | Date | yes |
y0, y1 | number | Date | yes |
Options: fill, stroke (both accept a Color or a theme accent key),
strokeWidth, fillAlpha, curve (LineCurve), curveSamples, label.
interval
Section titled “interval”Error bars and range marks. Bind either (yMin, yMax) for vertical intervals
(then x is required) or (xMin, xMax) for horizontal (then y is
required) — not both. Pair with point() for the classic mean ± CI look.
| Channel | Type | Required |
|---|---|---|
x | number | Date | when yMin/yMax set |
y | number | Date | when xMin/xMax set |
yMin, yMax | number | Date | vertical pair |
xMin, xMax | number | Date | horizontal pair |
color | categorical | no |
Options: stroke (Color or accent key), strokeWidth, caps, capWidth,
label.
A static reference band spanning an x- or y-range. Channels take a
[start, end] tuple rather than a data column, so it’s a decoration you add
once — think target zones, normal ranges, or weekend shading.
| Channel | Type | Required |
|---|---|---|
x | [number | Date, number | Date] | one of |
y | [number | Date, number | Date] | one of |
Options: fill / stroke (Color or accent key), strokeWidth, alpha,
label, labelColor.
A reference line at a constant value. x draws a vertical line; y draws a
horizontal one. Reach for it when you need a zero-line, a threshold, or a goal
marker without touching the data.
| Channel | Type | Required |
|---|---|---|
x or y | number | Date | one of |
Options: stroke (Color or accent key — positive / negative / warn /
info), strokeWidth, dashPattern, label, labelColor, labelInset,
endCap ("none" (default) / "arrow" / "triangle"), endCapSize.
Marginal tick marks along the axes — the minimal marginal distribution. Stack
one on top of your scatter and you get a quick read on data density without
committing to a full histogram. side defaults to whichever channels are wired.
| Channel | Type | Required |
|---|---|---|
x | number | Date | no |
y | number | Date | no |
color | categorical | no |
Options: side ("x" / "y" / "both"), length, strokeWidth, stroke,
opacity, label.
Per-row text labels anchored to data positions. Use collisionMode to keep a
dense dataset readable — "hide" culls overlapping labels, "stagger" nudges
them into rows.
| Channel | Type | Required |
|---|---|---|
x, y | number | Date | string | yes |
text | string | yes |
Options: fontSize, color, align, offsetX, offsetY, box (rounded-rect
background), collisionMode ("none" / "hide" / "stagger"),
collisionPadding, label.
Heatmap cells. Map a numeric column to fill and the color scale does the work;
omit it and pass options.fill for a constant color. Works best on a band × band
grid where every cell is the same size.
| Channel | Type | Required |
|---|---|---|
x, y | string | number | Date | yes |
fill | any (continuous) | no |
Options: fill, padding / paddingX / paddingY, stroke, strokeWidth,
cornerRadius, showValues, labelColor, labelFontSize, minLabelCellPx,
cellWidth, cellHeight, na, label.
boxplot
Section titled “boxplot”Box-and-whisker summaries — the five-number summary at a glance. The band axis
is the category; the other is the distribution. Map color to dodge side-by-side
groups within each band.
| Channel | Type | Required |
|---|---|---|
x | string | number | Date | yes |
y | string | number | Date | yes |
color | categorical | no |
Options include orientation, width, varwidth, whisker (WhiskerRule),
quantile (QuantileMethod), notched, notchWidth, points (PointsMode
— "auto" / "always" / "none"), pointsThreshold, pointJitter,
outliers, fill, fillAlpha, stroke, strokeWidth,
mean (true for a default red dot, or MeanMarkerOptions to customize),
medianStroke, medianStrokeWidth, whiskerStrokeWidth, capWidth,
groupPadding, showCounts, countsOffset, countsFontSize, countsColor.
violin
Section titled “violin”KDE density per category, mirrored into a symmetrical shape. Where a boxplot compresses the distribution into five numbers, a violin shows the full shape — worth the extra ink when multimodality matters. Optionally annotate the interior with a box, quartile lines, or raw sticks.
| Channel | Type | Required |
|---|---|---|
x | string | number | Date | yes |
y | string | number | Date | yes |
color | categorical | no |
Options include orientation, width, bandwidth (KdeBandwidth),
gridSize, kernel (KdeKernel), trim, scale ("width" / "area" /
"count"), inner ("none" / "box" / "quartile" / "stick"), points,
pointsThreshold, pointJitter, pointRadius, jitterSeed, whisker,
quantile, fill, stroke, strokeWidth, innerStroke, innerStrokeWidth,
groupPadding, showCounts, countsOffset, countsFontSize, countsColor.
ridgeline
Section titled “ridgeline”Stacked density ridges — the “joyplot.” Great for showing how a distribution
shifts across a categorical dimension (months, cohorts, sensors) without burning
a panel on each one. Each row gets its own KDE or histogram, and the ridges
overlap by scale.
| Channel | Type | Required |
|---|---|---|
x, y | string | number | yes (one numeric, one category) |
color | categorical | no |
Options include orientation, geom ("kde" / "histogram"), overlap,
scale ("width" / "area" / "count"), KDE pass-throughs (bandwidth,
gridSize, kernel, trim), histogram pass-throughs (bins, binwidth,
breaks, rule, measure, closed), fillMode ("solid" / "gradient"),
gradient, inner ("none" / "median" / "quartile" / "mean"),
innerStroke, innerStrokeWidth, innerDotRadius, baseline, baselineStroke,
baselineWidth, fill, fillAlpha, stroke, strokeWidth, groupPadding,
whisker, quantile, showCounts, countsAnchor, countsOffset,
countsFontSize, countsColor.
import { ridgeline } from "insomni-plot";
plot<Row>({ data }).layer(ridgeline({ x: "temp", y: "month" }, { scale: 2.4, inner: "median" }));aggregate
Section titled “aggregate”Bins along an axis and reduces each bin to a summary statistic, rendering an
inner geom for the result. Falls back to the raw geom when bins are too sparse
(dissolveAt) — so it gracefully handles zoomed-out views without extra wiring.
| Channel | Type | Required |
|---|---|---|
x, y | number | Date | yes |
Options: binBy, binSize ("auto" default), autoTargetPx (target px per
bin when binSize: "auto", default 7), summary (scalar like "mean" /
"median", or a bundle like "mean+ci" / "median+iqr"), filter,
dissolveAt, geom ("point" / "line" / "bar" / "interval" /
"ribbon"), fill, stroke, radius, shape (PointShapeKind), curve,
ciLevel, caps, capWidth, fillAlpha, label.
import { aggregate } from "insomni-plot";
plot<Row>({ data }).layer( aggregate( { x: "timestamp", y: "latency" }, { summary: "mean+ci", geom: "ribbon", }, ),);bullet
Section titled “bullet”A horizontal bullet gauge: a measure bar with an optional vertical target notch. The compact format — one row per metric — makes it easy to compare actuals against goals across many categories at once. Use with a band y scale and a continuous x scale.
| Channel | Type | Required |
|---|---|---|
y | string | number | Date | yes |
x | number | yes |
target | number | no |
Options: fill (Color or Gradient for the measure bar), target
(BulletTargetOptions — stroke, strokeWidth), bar (forwarded
cornerRadius, stroke, strokeWidth, label from BarOptions).
import { bullet } from "insomni-plot";
plot<Row>({ data }).layer(bullet({ y: "category", x: "value", target: "goal" }));dumbbell
Section titled “dumbbell”A connector line between a low and high x value, with endpoint circles — the
go-to mark for before/after or range comparisons by category. Each datum emits
two hit targets (seriesKey: "lo" / "hi") so tooltips anchor to the nearest
endpoint.
| Channel | Type | Required |
|---|---|---|
y | string | number | Date | yes |
xLo | number | yes |
xHi | number | yes |
color | categorical | no |
Options: loColor (default cool blue), hiColor (default warm red), radius
(endpoint circle radius, default 6), connector ({ stroke?, strokeWidth? }).
import { dumbbell } from "insomni-plot";
plot<Row>({ data }).layer(dumbbell({ y: "label", xLo: "before", xHi: "after" }));ganttTrack
Section titled “ganttTrack”Gantt rows: one filled rounded rectangle per datum spanning [xMin, xMax] on
the continuous x axis, at the datum’s band row on y. Perfect for timelines,
schedules, and project plans. Optionally backed by a faint full-row track
rectangle to ground each row visually.
| Channel | Type | Required |
|---|---|---|
y | string | number | Date | yes |
xMin | number | yes |
xMax | number | yes |
color | categorical | no |
Options: cornerRadius (default 4), barPadding (vertical inset fraction
0..0.5, default 0.18), fill (fallback color), track (false to hide
background track, or { fill?, cornerRadius? } to customize).
import { ganttTrack } from "insomni-plot";
plot<Row>({ data }).layer(ganttTrack({ y: "task", xMin: "start", xMax: "end", color: "phase" }));lollipop
Section titled “lollipop”Stick-and-dot chart: a vertical (or horizontal) interval from a baseline to each value, optionally topped with a point head. A lighter-weight alternative to bars when the dots do more communicative work than the filled rectangles.
| Channel | Type | Required |
|---|---|---|
x | number | Date | yes |
y | number | yes |
color | categorical | no |
Options: baseline (constant or accessor, default 0), stick
(IntervalOptions), point (PointOptions or false for a bare stick),
orientation ("y" = vertical sticks (default), "x" = horizontal).
import { lollipop } from "insomni-plot";
plot<Row>({ data }).layer(lollipop({ x: "category", y: "value", color: "group" }));Polar spider chart: a closed filled polygon (area) ordered around the angular
axis, optionally topped with vertex points. Use with
coordPolar({ gridShape: "polygon" }) and a band angle scale. Map color to
overlay multiple series on the same web for direct comparison.
| Channel | Type | Required |
|---|---|---|
x | categorical | yes |
y | number | yes |
color | categorical | no |
alpha | number | no |
Options: area ({ fill?, stroke?, strokeWidth? }), point (PointOptions
or false to omit vertex dots).
import { radar } from "insomni-plot";
plot<Row>({ data }) .coordPolar({ gridShape: "polygon" }) .layer(...radar({ x: "axis", y: "score", color: "series" }));See also
Section titled “See also”- Scales & aesthetics — how channels map to scales,
and the categorical channels (
shape,borderStyle,overlayGlyph). - Axes & coordinates — axis config and coordinate systems.