Package Exports
- blazeplot
- blazeplot/linked
- blazeplot/package.json
- blazeplot/plugins/annotations
- blazeplot/plugins/crosshair
- blazeplot/plugins/interactions
- blazeplot/plugins/legend
- blazeplot/plugins/navigator
- blazeplot/plugins/selection
- blazeplot/plugins/tooltip
- blazeplot/react
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
A chart only needs a host element. For regular arrays, wrap your data in a StaticDataset; capacity is only needed when you want a streaming ring buffer.
<div id="chart" style="width:100%;height:400px"></div>
<script type="module">
import { Chart, StaticDataset } from "blazeplot";
const el = document.getElementById("chart");
if (!el) throw new Error("Missing #chart element");
const x = Array.from({ length: 1000 }, (_, i) => i);
const y = x.map((value) => Math.sin(value * 0.02));
const chart = new Chart(el);
chart.addLine({ dataset: new StaticDataset(x, y), name: "sine" });
chart.setViewport({ xMin: x[0], xMax: x[x.length - 1], 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 reference
This section is generated by bun run docs:readme from TypeScript declaration files and JSDoc comments. Do not edit it by hand.
Package entry points
| Import | Contents |
|---|---|
blazeplot |
Core chart, data, interaction, rendering types, and low-level primitives. |
blazeplot/react |
React wrapper component and hooks. |
blazeplot/linked |
Linked chart layout helpers. |
blazeplot/plugins/legend |
Built-in legend plugin. |
blazeplot/plugins/tooltip |
Built-in tooltip plugin. |
blazeplot/plugins/interactions |
Built-in pan, zoom, axis interaction, and reset plugin. |
blazeplot/plugins/annotations |
Built-in annotation overlay plugin. |
blazeplot/plugins/selection |
Built-in brush/range selection plugin. |
blazeplot/plugins/crosshair |
Built-in crosshair and ruler plugin. |
blazeplot/plugins/navigator |
Built-in overview/navigator plugin. |
Selected generated declarations
These member tables are generated from TypeScript declarations.
class Chart
| Member |
|---|
constructor(target: HTMLElement, options?: ChartOptions) |
get canvas(): HTMLCanvasElement |
get rootElement(): HTMLElement |
get plotElement(): HTMLElement |
get xAxisElement(): HTMLElement |
get yAxisElement(): HTMLElement |
get y2AxisElement(): HTMLElement |
get theme(): ResolvedChartTheme |
| `getWebGLContext(): WebGL2RenderingContext \ |
getCamera(yAxis?: SeriesYAxis): Camera2D |
dataToPlot(x: number, y: number, yAxis?: SeriesYAxis): [ number, number ] |
| `clientToData(clientX: number, clientY: number, yAxis?: SeriesYAxis): [ number, number ] \ |
getViewport(yAxis?: SeriesYAxis): Viewport |
pan(intent: PanIntent): void |
zoom(intent: ZoomIntent): void |
addSeries(config: SeriesConfig, style?: Partial<SeriesStyle>): SeriesStore |
addLine(config: TypedSeriesConfig, style?: Partial<SeriesStyle>): SeriesStore |
addArea(config: TypedSeriesConfig, style?: Partial<SeriesStyle>): SeriesStore |
addScatter(config: TypedSeriesConfig, style?: Partial<SeriesStyle>): SeriesStore |
addBar(config: TypedSeriesConfig, style?: Partial<SeriesStyle>): SeriesStore |
addOhlc(config: TypedSeriesConfig, style?: Partial<SeriesStyle>): SeriesStore |
addCandlestick(config: TypedSeriesConfig, style?: Partial<SeriesStyle>): SeriesStore |
removeSeries(series: SeriesStore): boolean |
setSeriesVisible(series: SeriesStore, visible: boolean): boolean |
getSeriesState(): ChartSeriesState[] |
setViewport(v: { xMin?: number; xMax?: number; yMin?: number; yMax?: number; }): void |
setYViewport(yAxis: SeriesYAxis, v: { yMin?: number; yMax?: number; }): void |
resize(dpr?: number): boolean |
getFrameStats(target?: ChartFrameStats): ChartFrameStats |
| `getHoverState(): ChartHoverState \ |
| `setLayoutReservation(id: string, reservation: ChartLayoutReservation \ |
| `subscribe(event: "hover", callback: (state: ChartHoverState \ |
subscribe(event: "serieschange", callback: () => void): () => void |
subscribe(event: "themechange", callback: () => void): () => void |
subscribe(event: "render", callback: (chart: Chart) => void): () => void |
subscribe(event: "viewportchange", callback: (event: ChartViewportChangeEvent) => void): () => void |
subscribe(event: "select", callback: (event: ChartSelectEvent) => void): () => void |
subscribe(event: "seriesclick", callback: (event: ChartSeriesClickEvent) => void): () => void |
subscribe(event: ChartPointerEventType, callback: (event: ChartPointerEventState) => void): () => void |
emitSelect(selection: unknown): void |
setTheme(theme?: ChartTheme): void |
setGridVisible(visible: boolean): void |
getGridVisible(): boolean |
setAxes(axes: ChartOptions["axes"]): void |
| `pick(clientX: number, clientY: number, options?: ChartPickOptions): ChartHoverState \ |
screenshot(options?: ChartScreenshotOptions): Promise<Blob> |
start(): void |
stop(): void |
dispose(): void |
interface ChartOptions
| Member |
|---|
viewportPolicy?: ViewportPolicy |
grid?: boolean |
gridStyle?: Partial<SeriesStyle> |
| `axes?: boolean \ |
| `title?: string \ |
| `subtitle?: string \ |
hover?: ChartPickOptions |
plugins?: readonly ChartPlugin[] |
theme?: ChartTheme |
interface ChartTheme
| Member |
|---|
backgroundColor?: ThemeColor |
gridColor?: ThemeColor |
axisColor?: string |
axisFont?: string |
seriesColors?: readonly ThemeColor[] |
tooltipBackgroundColor?: string |
tooltipTextColor?: string |
tooltipFont?: string |
legendBackgroundColor?: string |
legendBorderColor?: string |
legendTextColor?: string |
legendMutedTextColor?: string |
legendFont?: string |
titleColor?: string |
titleFont?: string |
subtitleColor?: string |
subtitleFont?: string |
axisTitleColor?: string |
axisTitleFont?: string |
interface AxisConfig
| Member |
|---|
visible?: boolean |
position?: AxisPosition |
scale?: AxisScale |
tickFormat?: AxisTickFormat |
timezone?: AxisTimeZone |
| `title?: string \ |
class SeriesStore
| Member |
|---|
config: SeriesConfig |
style: SeriesStyle |
constructor(dataset: Dataset, config: SeriesConfig, style: SeriesStyle) |
get hasLOD(): boolean |
get dirty(): boolean |
get length(): number |
get visible(): boolean |
setVisible(visible: boolean): void |
append(x: ArrayLike<number>, y: ArrayLike<number>): void |
appendY(y: ArrayLike<number>): void |
markDirty(): void |
clear(): void |
rebuildPyramid(): void |
query(viewport: Viewport, pixelWidth: number): LODView |
visibleSampleCount(viewport: Viewport): number |
| `sampleAt(index: number): SeriesSample \ |
| `nearestSampleByX(x: number, viewport?: Viewport): SeriesSample \ |
| `nearestSampleByPoint(x: number, y: number, viewport: Viewport, plotWidth: number, plotHeight: number, maxDistancePx?: number): SeriesSample \ |
copyRawVisible(viewport: Viewport, target: Float32Array, maxPoints: number, xOrigin?: number): number |
copyScatterVisible(viewport: Viewport, target: Float32Array, maxPoints: number, pixelWidth: number, pixelHeight: number, pointSize: number, xOrigin?: number): number |
copyScatterRange(start: number, end: number, viewport: Viewport, target: Float32Array, maxPoints: number, xOrigin?: number, pixelHeight?: number, pointSize?: number): number |
copyRawVisibleClipped(viewport: Viewport, target: Float32Array, maxPoints: number, xOrigin?: number): number |
copyRawVisibleClipSpace(viewport: Viewport, target: Float32Array, maxPoints: number): number |
copyRawRange(start: number, end: number, target: Float32Array, maxPoints: number, xOrigin?: number): number |
copyAreaVisible(viewport: Viewport, target: Float32Array, maxPoints: number, baseline?: number, xOrigin?: number): number |
copyAreaRange(start: number, end: number, target: Float32Array, maxPoints: number, baseline?: number, xOrigin?: number): number |
copyMinMaxVisible(viewport: Viewport, target: Float32Array, maxSegments: number, xOrigin?: number): number |
copyMinMaxInstanced(viewport: Viewport, target: Float32Array, maxSegments: number, xOrigin?: number): number |
copyOhlcRange(start: number, end: number, target: Float32Array, maxCandles: number, tickWidth: number, xOrigin?: number): number |
copyOhlcTuplesRange(start: number, end: number, target: Float32Array, maxCandles: number, xOrigin?: number): number |
| `visibleIndexRange(viewport: Viewport \ |
interface SeriesConfig
| Member |
|---|
mode: SeriesMode |
capacity?: number |
downsample?: LODStrategy |
overflow?: BufferOverflowStrategy |
dataset?: Dataset |
yAxis?: SeriesYAxis |
id?: string |
name?: string |
interface SeriesStyle
| Member |
|---|
color: readonly [ number, number, number, number ] |
lineWidth: number |
pointSize?: number |
barWidth?: number |
baseline?: number |
fillColor?: readonly [ number, number, number, number ] |
tickWidth?: number |
upColor?: readonly [ number, number, number, number ] |
downColor?: readonly [ number, number, number, number ] |
wickColor?: readonly [ number, number, number, number ] |
class UniformRingBuffer
| Member |
|---|
capacity: number |
xStep: number |
constructor(capacity: number, options?: UniformRingBufferOptions) |
get length(): number |
| `get range(): TimeRange \ |
push(x: number, y: number): void |
append(x: ArrayLike<number>, y: ArrayLike<number>): void |
appendY(y: ArrayLike<number>): void |
clear(): void |
getX(index: number): number |
getY(index: number): number |
lowerBoundX(x: number): number |
upperBoundX(x: number): number |
| `rangeMinMaxY(start: number, end: number): { minY: number; maxY: number; } \ |
copyVisibleSamples(viewport: Viewport, target: Float32Array, maxPoints: number, layout: SampleLayout, baseline: number, xOrigin: number): number |
copySamplesRange(start: number, end: number, target: Float32Array, maxPoints: number, layout: SampleLayout, baseline: number, xOrigin: number): number |
copyMinMaxSegments(viewport: Viewport, target: Float32Array, maxSegments: number, layout: MinMaxLayout, xOrigin: number): number |
class RingBuffer
| Member |
|---|
capacity: number |
constructor(capacity: number, options?: RingBufferOptions) |
get length(): number |
| `get range(): TimeRange \ |
push(x: number, y: number): void |
append(x: ArrayLike<number>, y: ArrayLike<number>): void |
| `get(index: number): { x: number; y: number; } \ |
getX(index: number): number |
getY(index: number): number |
lowerBoundX(x: number): number |
upperBoundX(x: number): number |
| `rangeMinMaxY(start: number, end: number): { minY: number; maxY: number; } \ |
clear(): void |
class StaticDataset
| Member |
|---|
constructor(xData: ArrayLike<number>, yData: ArrayLike<number>) |
get length(): number |
| `get range(): TimeRange \ |
getX(index: number): number |
getY(index: number): number |
lowerBoundX(x: number): number |
upperBoundX(x: number): number |
class OhlcRingBuffer
| Member |
|---|
capacity: number |
constructor(capacity: number, options?: OhlcRingBufferOptions) |
get length(): number |
| `get range(): TimeRange \ |
push(x: number, open: number, high: number, low: number, close: number): void |
append(x: ArrayLike<number>, open: ArrayLike<number>, high: ArrayLike<number>, low: ArrayLike<number>, close: ArrayLike<number>): void |
clear(): void |
getX(index: number): number |
getY(index: number): number |
getOpen(index: number): number |
getHigh(index: number): number |
getLow(index: number): number |
getClose(index: number): number |
lowerBoundX(x: number): number |
upperBoundX(x: number): number |
interface Dataset
| Member |
|---|
length: number |
| `range: TimeRange \ |
getX(index: number): number |
getY(index: number): number |
lowerBoundX(x: number): number |
upperBoundX(x: number): number |
interface ViewportPolicy
| Member |
|---|
| `beforePan(camera: Camera2D, intent: PanIntent): PanIntent \ |
| `beforeZoom(camera: Camera2D, intent: ZoomIntent): ZoomIntent \ |
beforeRender(camera: Camera2D): void |
All public exports
Generated from dist/index.d.ts after the package build
| Export | Kind | Source | JSDoc summary |
|---|---|---|---|
AcceleratedDataset |
interface | ./core/types |
Convenience contract for maximum-performance custom datasets. Implement this when a dataset can provide fast exact sample copies, stable viewport sampling, range min/max queries, and renderer-ready min/max buckets. |
AppendableDataset |
interface | ./core/types |
— |
AxisConfig |
interface | ./ui/Chart |
— |
AxisController |
class | ./interaction/AxisController |
— |
AxisControllerAxisOptions |
interface | ./interaction/AxisController |
— |
AxisControllerOptions |
interface | ./interaction/AxisController |
— |
AxisPosition |
type | ./ui/ChartLayout |
— |
AxisRenderTarget |
type | ./interaction/AxisController |
— |
AxisScale |
type | ./interaction/AxisController |
— |
AxisTickFormat |
type | ./interaction/AxisController |
— |
AxisTickFormatter |
type | ./interaction/AxisController |
— |
AxisTimeZone |
type | ./interaction/AxisController |
— |
AxisTitleConfig |
interface | ./ui/Chart |
— |
BufferOverflowStrategy |
type | ./core/types |
— |
BuiltInAxisScale |
type | ./interaction/AxisController |
— |
Camera2D |
class | ./interaction/Camera2D |
— |
Chart |
class | ./ui/Chart |
— |
ChartFrameStats |
interface | ./ui/Chart |
— |
ChartHoverState |
interface | ./ui/Chart |
— |
ChartLayoutReservation |
interface | ./ui/Chart |
— |
ChartOptions |
interface | ./ui/Chart |
— |
ChartPickGroup |
type | ./ui/Chart |
— |
ChartPickItem |
interface | ./ui/Chart |
— |
ChartPickMode |
type | ./ui/Chart |
— |
ChartPickOptions |
interface | ./ui/Chart |
— |
ChartPlugin |
interface | ./ui/Chart |
— |
ChartPluginHandle |
interface | ./ui/Chart |
— |
ChartPointerEventState |
interface | ./ui/Chart |
— |
ChartPointerEventType |
type | ./ui/Chart |
— |
ChartScreenshotOptions |
interface | ./ui/Chart |
— |
ChartSelectEvent |
interface | ./ui/Chart |
— |
ChartSeriesClickEvent |
interface | ./ui/Chart |
— |
ChartSeriesState |
interface | ./ui/Chart |
— |
ChartTheme |
interface | ./ui/theme |
— |
ChartTitleConfig |
interface | ./ui/Chart |
— |
ChartViewportChangeEvent |
interface | ./ui/Chart |
— |
CssColor |
type | ./ui/theme |
— |
CustomAxisScale |
interface | ./interaction/AxisController |
— |
Dataset |
interface | ./core/types |
— |
DEFAULT_CHART_THEME |
const | ./ui/theme |
— |
LODBucket |
interface | ./core/types |
— |
LODStrategy |
type | ./core/types |
— |
LODView |
interface | ./core/types |
— |
MinMaxPyramid |
class | ./core/MinMaxPyramid |
— |
MinMaxSegmentCopyDataset |
interface | ./core/types |
Optional high-performance min/max extraction capability for dense rendering. Implementations can use pyramids, segment trees, database aggregates, or analytic/procedural envelopes to emit renderer-ready min/max buckets. |
MinMaxSegmentLayout |
type | ./core/types |
— |
OhlcDataset |
interface | ./core/types |
— |
OhlcRingBuffer |
class | ./core/OhlcDataset |
— |
OhlcRingBufferOptions |
interface | ./core/OhlcDataset |
— |
PanIntent |
interface | ./interaction/types |
— |
RangeMinMaxDataset |
interface | ./core/types |
— |
RangeSampleCopyDataset |
interface | ./core/types |
Optional high-performance extraction capability for datasets that can copy raw samples without going through repeated getX/getY calls. Implement this for very large datasets, implicit-X datasets, or remote/memory-mapped sources. |
ResolvedChartTheme |
interface | ./ui/theme |
— |
RgbaColor |
type | ./ui/theme |
— |
RingBuffer |
class | ./core/RingBuffer |
— |
RingBufferOptions |
interface | ./core/RingBuffer |
— |
RingBufferOverflow |
type | ./core/RingBuffer |
— |
SampleCopyLayout |
type | ./core/types |
— |
SeriesConfig |
interface | ./core/types |
— |
SeriesMode |
type | ./core/types |
— |
SeriesSample |
interface | ./core/types |
— |
SeriesStore |
class | ./core/SeriesStore |
— |
SeriesStyle |
interface | ./core/types |
— |
SeriesYAxis |
type | ./core/types |
— |
StaticDataset |
class | ./core/StaticDataset |
— |
StaticOhlcDataset |
class | ./core/OhlcDataset |
— |
TextOverlayConfig |
interface | ./ui/Chart |
— |
ThemeColor |
type | ./ui/theme |
— |
TimeRange |
interface | ./core/types |
— |
TypedSeriesConfig |
type | ./ui/Chart |
— |
UniformRingBuffer |
class | ./core/UniformRingBuffer |
High-throughput ring buffer for uniformly spaced X values. Store only Y samples and derive X as xStart + index * xStep. This is the fastest built-in dataset for live telemetry, signals, and other fixed-rate streams because appends copy a single typed array and min/max extraction uses a block segment tree over the physical ring. |
UniformRingBufferOptions |
interface | ./core/UniformRingBuffer |
— |
Viewport |
interface | ./core/types |
— |
ViewportPolicy |
interface | ./interaction/types |
— |
VisiblePointCopyDataset |
interface | ./core/types |
Optional high-performance extraction capability for point/scatter datasets. Implementations should cull against the full 2D viewport and may sample in screen space so dense point clouds respond to both X and Y zoom. |
VisibleSampleCopyDataset |
interface | ./core/types |
Optional high-performance stable visible sampling capability. Unlike copySamplesRange, this method may stride/downsample, but should choose samples anchored to data coordinates so streamed appends do not make existing sampled points jitter. |
YAppendableDataset |
interface | ./core/types |
— |
ZoomAxis |
type | ./interaction/types |
— |
ZoomIntent |
interface | ./interaction/types |
— |
Architecture
src/
core/ # Data model — series, datasets, LOD
render/ # GPU abstraction + regl backend
interaction/ # Camera, axis ticks, interaction intent types
ui/ # Orchestrator (Chart)Contributing
Keep pull requests small and focused. One feature or fix per PR.
Branch flow:
developmentis the integration branch for regular work.- Open feature/fix PRs into
development. - Open release PRs from
developmentintomainwith the version and changelog already updated. - Merging an unpublished version to
mainruns CI, publishes npm, creates thevX.Y.Ztag, and creates the GitHub Release.
Development
bun install
bun run dev # Vite dev server → preview/
bun run ci # Typecheck + tests + package build
bun run build # Package build (JS + declarations)
bun test # Tests
bun run typecheck # TypeScript strict check
bun run version:patch # Prepare package.json + changelog for a patch release PR