Package Exports
- blazeplot
- blazeplot/package.json
Readme
BlazePlot
Real-time LOD time series rendering engine for the browser.
BlazePlot is GPU-native plotting engineered for high-frequency streaming data where standard charting libraries (Chart.js, ECharts, uPlot) fall over. Instead of drawing every sample, it keeps a resident ring buffer of millions of points, builds a min/max LOD pyramid, and renders only what each pixel needs — so frame cost is O(pixels), not O(samples).
Built on WebGL2 + regl. No Canvas2D, no SVG, no DOM layout.
Target
- 10M+ resident points per series
- 60 Hz smooth append + render
- Zero allocations in the frame loop
- Multi-series with independent buffers and LOD
Installation
bun install blazeplotQuick start
<canvas id="chart" style="width:100%;height:400px"></canvas>import { Chart } from "blazeplot";
const canvas = document.getElementById("chart");
const chart = new Chart(canvas);
const series = chart.addSeries(
{ mode: "line", capacity: 1_000_000, downsample: "minmax" },
{ color: [0.3, 0.6, 1.0, 1.0] },
);
chart.setViewport({ xMin: 0, xMax: 1000, yMin: -2, yMax: 2 });
chart.start();
// Append data periodically
function push() {
const n = 256;
const xs = new Float64Array(n);
const ys = new Float32Array(n);
for (let i = 0; i < n; i++) {
xs[i] = t++;
ys[i] = Math.sin(t * 0.01) * 0.5 + Math.random() * 0.01;
}
series.append(xs, ys);
requestAnimationFrame(push);
}
let t = 0;
push();Features
| LOD downsampling | Min/max pyramid automatically selects the right detail level for the visible range — sparse viewports show raw points, dense viewports show vertical min/max segments. |
| Streaming append | Fixed-capacity ring buffer wraps silently. No re-allocation. No memory growth. |
| Pan & zoom | Pointer/touch pan and wheel zoom via Camera2D. Customizable viewport policies (e.g. live-follow X while Y is free). |
| Grid lines | Data-anchored grid rendered as WebGL line lists. |
| Multi-series | Independent buffers, styles, and visibility per series. |
| No DOM | No axis DOM elements, no SVG overlay. The canvas owns everything. |
| ResizeObserver | Automatic DPR-aware canvas sizing. |
API
Chart
| Signature | Description |
|---|---|
new Chart(canvas, options?) |
Create a chart from an HTML canvas element. |
chart.addSeries(config, style?) |
Add a data series. Returns SeriesStore. |
chart.removeSeries(series) |
Remove a previously added series. |
chart.setViewport({ xMin, xMax, yMin, yMax }) |
Set the visible data range. |
chart.resize(dpr?) |
Resize the canvas to match its CSS size × DPR. |
chart.start() |
Start the render loop (rAF). |
chart.stop() |
Stop the render loop. |
chart.getFrameStats(target?) |
Copy per-frame benchmark counters into a reusable object. |
chart.dispose() |
Dispose GPU resources, observers, and input handlers. |
ChartOptions
| Property | Default | Description |
|---|---|---|
viewportPolicy? |
— | Custom pan/zoom/viewport behavior hooks. |
grid? |
true |
Show grid lines. |
gridStyle? |
{ color: [0.22,0.30,0.44,0.45] } |
Grid line color and width. |
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" / "mixed". |
SeriesStore
| Signature | Description |
|---|---|
series.append(xs, ys) |
Append typed arrays of X (Float64) and Y (Float32) values. |
series.clear() |
Clear all data and reset LOD state. |
series.setVisible(v) |
Toggle visibility. |
series.visible |
Current visibility state. |
series.length |
Number of samples buffered. |
SeriesConfig
| Property | Description |
|---|---|
mode |
"line" / "envelope" / "scatter" (envelope and scatter roadmap-only). |
capacity |
Ring buffer capacity (samples). |
downsample |
"minmax" (the only LOD strategy). |
SeriesStyle
| Property | Default | Description |
|---|---|---|
color |
[0.3, 0.6, 1.0, 1.0] |
RGBA float color. |
lineWidth |
1 |
Line width in pixels. |
ViewportPolicy
interface ViewportPolicy {
beforePan?(camera: Camera2D, intent: PanIntent): PanIntent | null;
beforeZoom?(camera: Camera2D, intent: ZoomIntent): ZoomIntent | null;
beforeRender?(camera: Camera2D): void;
}Built-in data types: Viewport, PanIntent, ZoomIntent, ZoomAxis.
Lower-level primitives
Camera2D, RingBuffer, MinMaxPyramid, AxisController are exported for advanced use cases (custom pipelines, worker threads, offscreen rendering).
How it works
Data stream ──► RingBuffer (resident, wraps at capacity)
│
▼
MinMaxPyramid (full rebuild today, incremental roadmap)
│
▼
SeriesStore.query() ──► LODView (buckets for visible range)
│
▼
Renderer (regl / WebGL2)The render loop decides per-frame:
- Few visible samples → raw line strip from ring buffer
- Many visible samples → min/max vertical segments from the pyramid
Architecture
src/
core/ # Data engine — no UI, no GPU
render/ # GPU abstraction + regl backend
interaction/ # Camera, input, axis ticks
ui/ # Orchestrator (Chart)Development
bun install
bun run dev # Vite dev server → preview/
bun run build # Package build (JS + declarations)
bun run build:js # JS-only build
bun test # Core data-structure tests
bun run typecheck # TypeScript strict checkPackage build
bun run buildOutput:
dist/index.js ES module
dist/index.d.ts TypeScript declarations
dist/index.js.map Source map
dist/*.d.ts.map Declaration maps