Fast WebGL2 plotting engine for the browser 🔥
GPU-native, minimal DOM. Built on WebGL2 + regl . No Canvas2D, no SVG, no layout thrashing.
Installation Quick start< div id = " chart" style = " width:100%;height:400px" > </ div> import { Chart } from "blazeplot" ;
const container = document. getElementById ( "chart" ) ;
const chart = new Chart ( container) ;
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 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.
APIChart
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
Typed helpers that set the series mode for you.
chart.removeSeries(series)
Remove a previously added series.
chart.setViewport({ xMin, xMax, yMin, yMax })
Set the visible data range.
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.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 "nearest-x" and "nearest-point"; returned items include actual sample X/Y and plot/client coordinates for highlights.
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?
—
Custom pan/zoom/viewport behavior hooks.
hover?
{ mode: "nearest-x" }
Default hover picking behavior. mode can be "nearest-x" or "nearest-point".
plugins?
[]
Optional ChartPlugin instances, e.g. legendPlugin() and tooltipPlugin().
grid?
true
Show grid lines.
gridStyle?
{ color: [0.22,0.30,0.44,0.45] }
Grid line color and width.
axes?
true
Show axis tick labels. true/false, or per-axis { x?: boolean | AxisConfig, y?: boolean | AxisConfig }.
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.
new Chart ( canvas, {
axes: { x: { position: "outside" } , y: true }
} ) ; 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".
Pluginsimport { Chart } from "blazeplot" ;
import { legendPlugin } from "blazeplot/plugins/legend" ;
import { tooltipPlugin } from "blazeplot/plugins/tooltip" ;
const chart = new Chart ( container, {
hover : { mode : "nearest-x" } ,
plugins : [
legendPlugin ( ) ,
tooltipPlugin ( { mode : "nearest-point" } ) ,
] ,
} ) ; Built-in plugins are optional. They 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.
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" / "envelope" (envelope roadmap-only).
capacity
Ring buffer capacity (samples).
id? / name?
Optional metadata exposed to plugins, legend, and tooltip rows.
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.
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 plot data on the GPU and streams only visible vertices; DOM is limited to chart layout and labels. For dense line plots (millions of points), interactive scatter, or real-time streaming, the difference is orders of magnitude.