v0.4.0

Guides

Multi-chart dashboards

Multi-chart boards on a shared GPUDevice: appendData with maxPoints, thresholds and incident annotations, stream sampling, and a production checklist.

Shared GPU across charts

Create adapter, device, and pipeline cache once. Pass the same context into each ChartGPU.create. Charts do not destroy a shared device on dispose. Prefer this for three or more charts on one page.

TypeScript
import { ChartGPU, createPipelineCache, connectCharts } from '@chartgpu/chartgpu';

const adapter = await navigator.gpu.requestAdapter({ powerPreference: 'high-performance' });
if (!adapter) throw new Error('No GPU adapter');
const device = await adapter.requestDevice();
const pipelineCache = createPipelineCache(device);
const shared = { adapter, device, pipelineCache };

const a = await ChartGPU.create(elA, optsA, shared);
const b = await ChartGPU.create(elB, optsB, shared);
const c = await ChartGPU.create(elC, optsC, shared);

connectCharts([a, b, c], { syncZoom: true });

appendData for the hot path

Prefer chart.appendData(seriesIndex, points, { maxPoints }) on every streaming series. Bound history with maxPoints so memory and GPU uploads stay capped. Do not rebuild the full options object every tick.

TypeScript
// One window per tick (column or tuple data both work)
chart.appendData(0, [[t, y0]], { maxPoints: 2_400 });
chart.appendData(1, [[t, y1]], { maxPoints: 2_400 });

Sampling on streams

For high-rate line series, set sampling: 'none' so appends stay on the incremental path. Product-level sampling (how often you score or poll upstream) is separate from chart sampling: sample upstream if needed; stream every retained window to the chart.

Thresholds as lineY

Static reference lines (SLO floors/ceilings) belong in annotations once at create time, not every tick.

TypeScript
annotations: [{
  id: 'slo-max',
  type: 'lineY',
  y: 2, // domain units (e.g. percent)
  layer: 'belowSeries',
  style: { color: '#F0A202', lineDash: [6, 5], lineWidth: 2 },
  label: { text: 'target <2%' },
}]

Incident annotations

Annotate discrete events only. Spread chart.options when calling setOption so series identity and append buffers stay intact (setOption replaces the full user option object).

TypeScript
if (incident) {
  annotations = [...annotations, {
    id: `${incident}-${t}`,
    type: 'lineX',
    x: t,
    style: { color: '#E05A8C', lineWidth: 2 },
    label: { text: incident, offset: [6, -10], background: { color: '#0a0b0e', opacity: 0.75 } },
  }].slice(-12);

  chart.setOption({ ...chart.options, annotations });
}

Example: multi-chart streaming board

Four charts on one shared device and pipeline cache. This demo uses online-eval-style metrics (quality, error rate, tool health, latency/cost). Controls stress the series and incident markers; follow-live pins the edge of each stream.

What this example’s series represent
  • Generation quality — dual line (e.g. faithfulness / relevance) with a floor threshold
  • Error / hallucination rate — percent series with a ceiling lineY
  • Tool health — selection accuracy + execution success
  • SLOs — p99 latency and cost/query with sparse lineX incidents

The domain is optional; the patterns above apply to any multi-chart board.

Full hosted demo

A five-chart dashboard lives at the streaming dashboard demo.

Production checklist

  • Dispose every chart on unmount; destroy the shared device when the board goes away
  • Resize via chart.resize() (coalesced ResizeObserver)
  • Disable decorative animation on high-frequency streams
  • Annotate only discrete incidents; keep SLO thresholds as stable lineY
  • Always spread chart.options when calling setOption so series identity is preserved
  • Feature-detect WebGPU and degrade gracefully

Related API