JSPM

  • Created
  • Published
  • Downloads 207
  • Score
    100M100P100Q63839F
  • License MIT

Real-time LOD time series rendering engine for the browser.

Package Exports

  • blazeplot
  • blazeplot/package.json
  • blazeplot/plugins/interactions
  • blazeplot/plugins/legend
  • blazeplot/plugins/tooltip

Readme

BlazePlot

npm version npm downloads license

Fast WebGL2 plotting engine for the browser 🔥

Built for people who have hit the performance ceiling of Chart.js, Plotly, and similar browser charting libraries. BlazePlot keeps the hot path GPU-native and the DOM minimal, so large streaming datasets stay interactive instead of turning into a slideshow.

Built on WebGL2 + regl.

Installation

bun install blazeplot

Quick start

<div id="chart" style="width:100%;height:400px"></div>

<script type="module">
  import { Chart } from "blazeplot";

  const chart = new Chart(document.getElementById("chart"));
  const wave = chart.addLine({ capacity: 10_000, downsample: "minmax" });

  wave.append(
    Float64Array.from({ length: 1000 }, (_, i) => i),
    Float32Array.from({ length: 1000 }, (_, i) => Math.sin(i * 0.02)),
  );

  chart.setViewport({ xMin: 0, xMax: 1000, yMin: -1.5, yMax: 1.5 });
  chart.start();
</script>

Features

WebGL2 rendering GPU-accelerated plot rendering from the ground up. No Canvas2D fallback. Axis labels use lightweight DOM layers.
Flexible data model Streaming ring buffer or static arrays. Bring your own data shape.
LOD downsampling Min/max pyramid for efficient line rendering at any zoom level — sparse views show raw points, dense views show vertical segments.
Pan & zoom Pointer/touch pan and wheel zoom via Camera2D. Customizable viewport policies.
Grid lines Data-anchored grid rendered as WebGL line lists.
Axis labels Smart tick generation with DOM labels. Per-axis inside/outside positioning; outside axes reserve real layout gutters.
Multi-series Independent buffers, styles, and visibility per series. Line, area, scatter, and bar modes are supported.
Plugin-ready UI Optional built-in legendPlugin() and tooltipPlugin() use the same public state and hover APIs available to custom plugins.
Benchmark overlay Built-in fps, frame time, vertex count, draw calls.
ResizeObserver Automatic DPR-aware canvas sizing.

API

Chart

Signature Description
new Chart(container, options?) Create a chart inside an HTML container element. The chart owns the plot canvas and axis layout.
chart.addSeries(config, style?) Add a data series. Returns SeriesStore.
chart.addLine(config, style?) / addArea / addScatter / addBar / addOhlc / addCandlestick Typed helpers that set the series mode for you. config.yAxis: "right" maps a series to the secondary Y axis.
chart.removeSeries(series) Remove a previously added series.
chart.setViewport({ xMin, xMax, yMin, yMax }) Set the visible data range on both Y axes and the shared X range.
`chart.setYViewport("left" "right", { yMin, yMax })`
chart.getViewport(yAxis?) Return the current visible data range for "left" or "right" Y axis.
chart.pan(intent) / chart.zoom(intent) Plugin-facing camera interaction helpers.
chart.clientToData(clientX, clientY, yAxis?) / chart.dataToPlot(x, y, yAxis?) Convert between client/plot coordinates and data coordinates for the selected Y axis.
chart.resize(dpr?) Resize the internal plot canvas to match its CSS size × DPR.
chart.start() Start the render loop (rAF).
chart.stop() Stop the render loop.
chart.canvas Read-only access to the internal plot canvas.
chart.xAxisElement / chart.yAxisElement Plugin-facing access to outside axis gutter elements.
chart.theme Resolved theme values used by the chart and built-in plugins.
chart.getFrameStats(target?) Copy per-frame benchmark counters into a reusable object.
chart.getSeriesState() Return public series metadata/state for plugins or custom UI.
chart.setSeriesVisible(series, visible) Toggle visibility and notify series-state subscribers.
chart.pick(clientX, clientY, options?) Raw-data hit test. Supports `mode: "nearest-x"
chart.subscribe("hover", cb) / chart.subscribe("serieschange", cb) Subscribe to hover or series state changes. Returns an unsubscribe function.
await chart.screenshot(options?) Export the full chart as an image Blob, including the WebGL plot and built-in DOM text overlays.
chart.dispose() Dispose GPU resources, observers, input handlers, and owned DOM layout.

ChartOptions

Property Default Description
viewportPolicy? Optional beforeRender viewport hook. Pass the same policy to interactionsPlugin({ viewportPolicy }) for pan/zoom hooks.
hover? { mode: "nearest-x", group: "x" } Default hover picking behavior. mode can be "nearest-x" or "nearest-point"; group: "x" reports one item per visible series at the selected X, while group: "none" reports only the selected point.
plugins? [] Optional ChartPlugin instances, e.g. legendPlugin() and tooltipPlugin().
theme? built-in dark theme Override chart, axis, palette, legend, and tooltip colors/fonts.
grid? true Show grid lines.
gridStyle? { color: theme.gridColor } Grid line color and width; overrides the theme grid color.
axes? true Show axis tick labels. true/false, or per-axis { x?: boolean | AxisConfig, y?: boolean | AxisConfig, y2?: boolean | AxisConfig }. y2 is the right-side secondary Y axis and is hidden by default.

ChartTheme

:root {
  --plot-bg: #050816;
  --plot-grid: rgb(148 163 184 / 0.22);
  --plot-axis: #cbd5e1;
  --series-a: #38bdf8;
  --series-b: oklch(70% 0.19 22);
}
new Chart(container, {
  theme: {
    backgroundColor: "var(--plot-bg)",
    gridColor: "var(--plot-grid)",
    axisColor: "var(--plot-axis)",
    seriesColors: ["var(--series-a)", "var(--series-b)"],
    tooltipBackgroundColor: "rgb(4 8 16 / 0.85)",
    legendBackgroundColor: "rgb(4 8 16 / 0.85)",
  },
});

WebGL-facing colors (backgroundColor, gridColor, seriesColors) accept either normalized RGBA tuples ([r,g,b,a]) or CSS colors, including CSS variables inherited by the chart container. BlazePlot resolves CSS colors internally to WebGL-compatible RGBA floats while preserving CSS strings for DOM styling where appropriate. Per-series styles and plugin options still override theme defaults.

AxisConfig

Property Default Description
visible? true Show this axis.
position? "inside" "inside" draws labels over the plot; "outside" reserves a real DOM gutter and shrinks the plot canvas.
// X labels outside (bottom gutter), left Y inside, right Y outside.
const chart = new Chart(canvas, {
  axes: { x: { position: "outside" }, y: true, y2: { position: "outside" } }
});

chart.addLine({ capacity: 10_000, yAxis: "left" });
chart.addLine({ capacity: 10_000, yAxis: "right" });
chart.setYViewport("right", { yMin: 0, yMax: 100 });

ChartFrameStats

Field Description
fps Instantaneous render-loop FPS.
frameMs Milliseconds spent in render().
pointsRendered Number of vertices drawn this frame.
drawCalls Number of GPU draw calls this frame.
uploadBytes Bytes uploaded to GPU this frame.
renderMode "none" / "raw" / "minmax" / "points" / "bars" / "area" / "mixed".

Plugins

import { Chart } from "blazeplot";
import { interactionsPlugin } from "blazeplot/plugins/interactions";
import { legendPlugin } from "blazeplot/plugins/legend";
import { tooltipPlugin } from "blazeplot/plugins/tooltip";

const chart = new Chart(container, {
  hover: { mode: "nearest-x", group: "x" },
  plugins: [
    interactionsPlugin({ axis: "xy" }),
    legendPlugin(),
    tooltipPlugin({ mode: "nearest-point", group: "none", maxDistancePx: 24 }),
  ],
});

Built-in plugins are optional. interactionsPlugin() provides plain-drag box zoom, Shift+drag plot pan, wheel zoom, double-click reset, and axis: "x" | "y" | "xy". When outside axes are visible, scrolling an axis zooms that axis and dragging an axis pans that axis; the plugin also applies a subtle axis hover color/filter configurable with axisHover, axisHoverColor, and axisHoverFilter. Legend/tooltip consume public APIs (getSeriesState, setSeriesVisible, pick, and subscribe) so custom UI can use the same contract. The default tooltip updates while the cursor is still on live charts and highlights the raw sample(s) it is reporting. For shared time-series tooltips, use group: "x"; for scatter/true point hover, use group: "none".

SeriesStore

Signature Description
series.append(xs, ys) Append typed arrays of X and Y values (streaming).
series.clear() Clear all data and reset.
series.setVisible(v) Toggle visibility.
series.visible Current visibility state.
series.length Number of samples buffered.

SeriesConfig

Property Description
mode "line" / "area" / "scatter" / "bar" / "ohlc" / "candlestick" / "envelope" (envelope roadmap-only).
capacity Ring buffer capacity (samples).
id? / name? Optional metadata exposed to plugins, legend, and tooltip rows.
yAxis? "left" or "right"; selects the primary or secondary Y viewport/axis for this series.
downsample "minmax" or "none". Min/max LOD applies to line and bar rendering; area/scatter skip LOD.

SeriesStyle

Property Default Description
color [0.3, 0.6, 1.0, 1.0] RGBA float color.
lineWidth 1 Line width in pixels.
pointSize 4 Scatter point size in CSS pixels.
barWidth 0.8 Bar width in data-space X units.
baseline 0 Area/bar baseline in data-space Y units.
fillColor line color with 25% alpha Area fill RGBA color.
tickWidth barWidth or 0.8 OHLC open/close tick width in data-space X units.
upColor / downColor series color / translucent fill Candlestick body colors.
wickColor series color Candlestick wick color.

High-performance dataset capabilities

Dataset only requires getX/getY and X binary search. For maximum performance with huge, procedural, remote, or memory-mapped data, implement AcceleratedDataset.

AcceleratedDataset is the convenience contract for all fast paths: range min/max, exact range copying, stable viewport sampling, and renderer-ready min/max buckets. BlazePlot still exports the smaller capability interfaces (RangeMinMaxDataset, RangeSampleCopyDataset, VisibleSampleCopyDataset, MinMaxSegmentCopyDataset) for advanced partial acceleration, but most high-performance custom datasets should target AcceleratedDataset.

ViewportPolicy

beforeRender is consumed by Chart; beforePan and beforeZoom are consumed by interactionsPlugin({ viewportPolicy }).

interface ViewportPolicy {
  beforePan?(camera: Camera2D, intent: PanIntent): PanIntent | null;
  beforeZoom?(camera: Camera2D, intent: ZoomIntent): ZoomIntent | null;
  beforeRender?(camera: Camera2D): void;
}

Lower-level primitives

Camera2D, RingBuffer, MinMaxPyramid, AxisController are exported for advanced use cases.

Architecture

src/
  core/          # Data model — series, datasets, LOD
  render/        # GPU abstraction + regl backend
  interaction/   # Camera, axis ticks, interaction intent types
  ui/            # Orchestrator (Chart)

Development

bun install
bun run dev          # Vite dev server → preview/
bun run build        # Package build (JS + declarations)
bun test             # Tests
bun run typecheck    # TypeScript strict check