Skip to content

Scales & aesthetics

An aesthetic maps a data column to a visual channel. A scale turns the mapped values into pixels or colors. Both are inferred automatically; you only reach for .scale(channel, options) when you want to override the default.

Every channel value is an Aes<T, V> — a column-name string, an accessor (row, index) => value, or a constant. The available channels (the Channel union) are:

ChannelRoleTypical value type
x, ypositionnumber, Date, or string (band)
colorfill / stroke colorany (categorical) or number (continuous)
sizemark radiusnumber
alphamark opacitynumber
shapepoint glyphcategorical → shape palette
borderStylepoint border treatmentcategorical → solid | open | dashed | dotted
overlayGlyphsecondary point glyphcategorical → glyph palette

Which channels a geom honors is geom-specific — see Geoms.

.scale("x", options) / .scale("y", options). The type (and the whole options shape) follows the data:

typeFordomain
"linear" (default for numbers)numeric[number, number] or "nice"
"log"positive numeric[number, number]
"sqrt"numeric[number, number]
"time" (default for dates)Date[Date, Date]
"band" (default for strings)categoricalreadonly string[]

Common options: range ([number, number] pixel range), nice (pad numeric domains to round values — domain: "nice" is sugar for the same), and padding (band inner padding).

plot<Row>({ data })
.layer(point({ x: "income", y: "lifeExp" }))
.scale("x", { type: "log" })
.scale("y", { domain: "nice" });

When domain is omitted it’s inferred from the data. With pan/zoom enabled the x/y position-scale domain is overridden each frame from the viewport — don’t re-thread it through .scale() yourself.

.scale("color", options). Two families:

  • Categorical{ type: "categorical", domain?, palette? }. Each distinct value gets a palette color.
  • Continuous / diverging{ type: "continuous" | "diverging", domain?, palette?, nice?, blendSpace? }. Numeric values interpolate the palette; domain: "quantile" (with quantiles?: number, default 5) buckets the data into ordinal quantile bins.
import { plot, point } from "insomni-plot";
import { viridis } from "insomni-plot/core";
plot<Row>({ data })
.layer(point({ x: "lon", y: "lat", color: "elevation" }))
.scale("color", { type: "continuous", palette: viridis });

The continuous color bar legend renders automatically; categorical scales render swatches.

  • .scale("size", { type?: "linear" | "sqrt", domain?, range? }) — maps a numeric column to mark radius. range is [minRadius, maxRadius].
  • .scale("alpha", { domain?, range? }) — maps a numeric column to opacity.

These map categorical values to non-color visual properties, each with a default palette you can override:

  • .scale("shape", { domain?, palette? })PointShapeKind[] (default POINT_SHAPE_PALETTE).
  • .scale("borderStyle", { domain?, palette? })PointBorderStyle[] (default DEFAULT_BORDER_STYLE_PALETTE).
  • .scale("overlayGlyph", { domain?, palette? }) — secondary glyphs (default DEFAULT_OVERLAY_GLYPH_PALETTE); null palette entries suppress an overlay.

Individual named palettes live in insomni-plot/core, not the grammar barrel. Categorical: category10, tableau10, set1, set2, set3, dark2, paired, pastel, accent. Continuous / diverging: viridis, magma, inferno, plasma, cividis, blues, greens, reds, oranges, purples, greys, coolwarm, spectral, rdbu, brbg, piyg, prgn, puor. The grammar barrel (insomni-plot) exposes only the palettes combinator object and distinguishable.

The palettes object and distinguishable(...) combinator provide categorical encodings tuned for visual separation. For a hand-wired color scale, see colorScale in the core API.

A legend is auto-generated from the active categorical / continuous scales. Configure it with .legend(spec) or disable it with .legend(false):

plot<Row>({ data })
.layer(point({ x: "x", y: "y", color: "group", shape: "group" }))
.legend({ position: "right", title: "Group", merge: ["color", "shape"] });
OptionTypeNotes
showbooleanDisable the auto-legend.
position"top" | "right" | "bottom" | "left" | { inside: { x, y } }Outer slot or over-plot placement (x/y in 0..1).
titlestring
merge("color" | "shape" | "size" | "borderStyle" | "overlayGlyph")[]Combine channels that encode the same field into one legend.

For continuous (color-bar) legends, length, thickness, ticks, tickValues, minorTicks, labelStep, format, and blendSpace tune the gradient bar.