Package Exports
- blazeplot
- blazeplot/package.json
- blazeplot/plugins/interactions
- blazeplot/plugins/legend
- blazeplot/plugins/tooltip
Readme
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 blazeplotQuick 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