BlazePlot
Fast WebGL2 plotting engine for the browser 🔥
GPU-native, zero DOM. Built on WebGL2 + regl . No Canvas2D, no SVG, no layout thrashing.
Installation Quick 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 ( ) ;
const xs = new Float64Array ( 256 ) ;
const ys = new Float32Array ( 256 ) ;
let t = 0 ;
function push ( ) {
for ( let i = 0 ; i < 256 ; i++ ) {
xs[ i] = t++ ;
ys[ i] = Math. sin ( t * 0.01 ) * 0.5 + Math. random ( ) * 0.01 ;
}
series. append ( xs, ys) ;
requestAnimationFrame ( push) ;
}
push ( ) ; Features
WebGL2 rendering
GPU-accelerated from the ground up. No Canvas2D fallback, no DOM text nodes.
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.
Multi-series
Independent buffers, styles, and visibility per series.
Benchmark overlay
Built-in fps, frame time, vertex count, draw calls.
ResizeObserver
Automatic DPR-aware canvas sizing.
APIChart
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 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" / "envelope" / "scatter" (envelope and scatter roadmap-only).
capacity
Ring buffer capacity (samples).
downsample
"minmax" (LOD for line rendering).
SeriesStyle
Property
Default
Description
color
[0.3, 0.6, 1.0, 1.0]
RGBA float color.
lineWidth
1
Line width in pixels.
ViewportPolicyinterface ViewportPolicy {
beforePan? ( camera: Camera2D, intent: PanIntent) : PanIntent | null ;
beforeZoom? ( camera: Camera2D, intent: ZoomIntent) : ZoomIntent | null ;
beforeRender? ( camera: Camera2D) : void ;
} Lower-level primitivesCamera2D, RingBuffer, MinMaxPyramid, AxisController are exported for advanced use cases.
Architecturesrc/
core/ # Data model — series, datasets, LOD
render/ # GPU abstraction + regl backend
interaction/ # Camera, input, axis ticks
ui/ # Orchestrator (Chart) Developmentbun install
bun run dev
bun run build
bun run build:js
bun test
bun run typecheck Package buildOutput:
dist/index.js ES module
dist/index.d.ts TypeScript declarations Why WebGL2?Canvas2D and SVG are CPU-bound — every point becomes a draw call or a DOM node. BlazePlot keeps data on the GPU, streams only visible vertices, and avoids DOM entirely. For dense line plots (millions of points), interactive scatter, or real-time streaming, the difference is orders of magnitude.