Visual

Line chart

Sand scatters within a ribbon along each series, then resolves into a solid line — straight or a smooth Catmull-Rom spline — with an optional area fill. The config surface mirrors the bar chart; a line block replaces the bar's border.

The line chart reuses the exact same axes, legend (including click-to-isolate), currentValue, fps, interaction.hover and interaction.dim interfaces as the bar chart. This page documents only what differs; see the bar chart for those shared blocks.

Constructor

new LineChart(host: HTMLElement, config: LineChartConfig)

Mounts a canvas into host and boots the best backend (WebGPU → Canvas2D). Only data is required.

Config — top level

Identical to the bar chart except for lineThickness (new) and line (replaces bars).

PropertyTypeDefaultDescription
data requiredDataSetPoints to render. See Data model.
lineThicknessnumber0.03New for line charts. Ribbon thickness the sand scatters within, in layout units.
grainDensitynumber0.6Grains per unit area of the line ribbon.
maxGrainsnumber100000Hard grain ceiling. Grains scale with the line's ribbon area (path length × thickness), not the point count, and never exceed this — so grain cost stays bounded even at thousands of points.
colorsstring[]DEFAULT_PALETTESeries colors (CSS), cycled per series.
backgroundstringtransparentCanvas clear color.
grainobjectGrain appearance. See below.
animationobjectTiming. See below.
interactionobjectHover + legend-dim tuning. Same as bar chart.
axes{ x?, y? }offSame as bar chart.
legendLegendConfigoffSame as bar chart, including click-to-isolate (interactive).
currentValueobjectoffSame as bar chart.
lineLineStyleConfigoffSolid line + area fill. See below.
fpsFpsConfigoffSame as bar chart.
panZoomPanZoomConfigoffDrag to pan, wheel/UI to zoom. Shared pan & zoom.
backend'auto' | 'webgpu' | 'webgl2' | 'canvas2d''auto'Force a rendering backend.

grain

Identical to the bar chart: sizePx (3), shape ('disc'), jitter (0.6), settleJitter (0.004). See bar chart → grain.

animation

Units: duration, stagger and morphDuration are in milliseconds.

Same as the bar chart, with two line-specific twists in the enum values:

PropertyTypeDefaultDescription
durationnumber (ms)900Per-grain travel time.
staggernumber (ms)500Spread of pour start times.
ease'linear' | 'easeOutCubic' | 'easeOutQuint''easeOutCubic'Grain travel easing.
morphDurationnumber (ms)duration + staggerTransition window for live changes (line tween + grain fade).
reflow'translate' | 'reshuffle' | 'withLine''translate' How grains of an unchanged part of the line move. withLine (vs. the bar's withBar) locks them rigidly to the line — no delay, no fade flash.
morphGrainsbooleantrue Animate the sand during an update/add/remove morph. false snaps every grain straight to its new position (added grains just appear, removed just vanish) so only the solid line tweens on a data change. Ignored for the initial pour-in.
enter'pour' | 'rise' | 'continue''pour' How an added point's grains appear. continue (line-only) skips the emergence entirely — the vertex appears settled and the line just extends to it. Best for continuous streaming.
exit'fall' | 'vanish''fall'How a removed point's grains leave.

line — solid line + area fill LineStyleConfig

The line-chart analogue of the bar chart's fill + border. Off by default; a config without line renders as pure sand. Line and fill always use the series color.

PropertyTypeDefaultDescription
stackbooleanfalse Stack the series into a stacked area chart: each series sits on the cumulative total of the series below it (at every x), and the value axis spans the stack total. Best paired with a visible fill. Stacked bands use straight edges so they tile gap-free (spline is applied only to un-stacked areas).

line.line — the connecting line

PropertyTypeDefaultDescription
style'none' | 'straight' | 'spline''straight'* none = sand only, no line; straight = segments between points; spline = smooth Catmull-Rom curve. *Default when a line block is present.
widthnumber1Stroke width in CSS px.
opacitynumber (0..1)1Stroke opacity.

line.fill — area under the line

PropertyTypeDefaultDescription
opacitynumber (0..1)1Area fill opacity. Omit the fill block for no area.

line.reveal — particle→line crossfade

PropertyTypeDefaultDescription
start'afterPour' | number'afterPour'When the fade begins. A number = absolute seconds from start.
durationnumber (ms)500Fade window length.
easeEasing'easeOutCubic'Fade easing.
grainsTonumber (0..1)0Grain end-opacity after the fade.

Shared blocks

axes, legend, currentValue, fps, interaction.hover and interaction.dim are the same interfaces the bar chart uses — including currentValue's cursor-line (guide: horizontal / vertical / crosshair), axis value markers, the mode: 'axis' (On axes) readout, and legend.interactive click-to-isolate. Full property tables: axes · legend · currentValue · fps · interaction.hover / interaction.dim.

Isolating a series dims every other series' whole line + fill (and its grains) — same "whole series, not just one vertex" scope hover already uses here.

Methods

Same surface as the bar chart: whenReady(), get backend, on('hover', fn), on('seriesFocus', fn), focusSeries(index), getFocusedSeries(), update(data | patches), add(points), remove(refs), repour(), getData(), dispose(). See bar chart → Methods.

It also implements the shared PanZoomable interface: getView(), setView(v), panBy(), panTo(), zoomBy(), zoomTo(), resetView(), isPanZoomEnabled().

Events

The hover event carries { point: LineMeta | null } — note it's point (a line vertex), where the bar chart uses bar.

// LineMeta — metadata for one line vertex (data point)
{
  pointId: number;
  seriesIndex: number;
  seriesKey: Scalar | undefined;
  xValue: Scalar;
  yValue: number;
  pos: number;        // layout-space x center [0,1]
  height: number;     // layout-space y (top of the band when stacked)
  baseHeight: number; // layout-space y of the stack floor (0 when un-stacked)
  color: RGBA;
}

seriesFocus carries { index: number | null } — same shape as the bar chart's. See bar chart → Events.

Examples

Spline line with area fill

new LineChart(el, {
  data,
  lineThickness: 0.04,
  line: {
    line: { style: 'spline', width: 2 },
    fill: { opacity: 0.18 },
    reveal: { duration: 700, grainsTo: 0.1 },
  },
  axes: { x: { show: true }, y: { show: true, gridLines: true } },
});

Stacked area chart

new LineChart(el, {
  data,
  line: {
    stack: true,                       // each series stacks on the ones below
    fill: { opacity: 0.6 },
    line: { style: 'straight', width: 1.5 },
  },
  axes: { x: { show: true }, y: { show: true } },
});

Line-only transition (grains don't move on a data change)

new LineChart(el, {
  data,
  line: { line: { style: 'spline' }, fill: { opacity: 0.15 } },
  animation: { morphGrains: false },   // update/add/remove: only the line tweens
});

Crosshair cursor with values on the axes

new LineChart(el, {
  data,
  axes: { x: { show: true }, y: { show: true } },
  currentValue: { show: true, mode: 'axis', guide: 'both' },
});

Streaming: append points that just extend the line

const chart = new LineChart(el, {
  data,
  line: { line: { style: 'straight' } },
  animation: { enter: 'continue' },   // no pour — vertex appears settled
});

setInterval(() => {
  chart.add({ x: Date.now(), y: nextSample() });
}, 1000);

Sand only (no resolved line)

new LineChart(el, {
  data,
  line: { line: { style: 'none' } },   // grains never resolve into a stroke
});

Hover

chart.on('hover', ({ point }) => {
  if (point) label.textContent = `${point.xValue} → ${point.yValue}`;
});

Series dimming — click a legend entry to isolate it

new LineChart(el, {
  data,
  legend: { show: true, interactive: true },
  interaction: { dim: { opacity: 0.15 } },
});

© 2026 · License · Data model →