BlazePlot: Why I Built a WebGL Plotting Engine for Scientific Apps

BlazePlot is my WebGL2 plotting engine for real-time scientific and engineering data, built after hitting the limits of general-purpose browser chart libraries.

May 18, 20267 min read
BlazePlot: Why I Built a WebGL Plotting Engine for Scientific Apps

I built BlazePlot because scientific web apps keep running into the same boring, expensive problem: at some point, the chart becomes the bottleneck.

Not the database. Not the API. Not React. The chart.

You can get surprisingly far with the usual tools. Chart.js is great when you need something simple and clean. Plotly.js is powerful and flexible. For dashboards, reports, admin panels, and most product analytics, those libraries are more than enough.

But scientific interfaces are not dashboards. A sensor monitor, a lab tool, a telemetry viewer, a radiation detector UI, a simulation frontend — these are much less forgiving. Data arrives continuously. The user zooms and pans while the data is still moving. They are not looking for a pretty average; they are looking for the spike that explains why the system behaved strangely for half a second.

When the plot freezes, the app stops feeling like an instrument and starts feeling like a screenshot.

That is the use case BlazePlot is built for: high-performance, browser-native plotting for large streaming datasets.

The gap between dashboard charts and scientific plotting

Most open-source charting libraries are broad by design. They support many chart types, layout options, plugins, legends, themes, exports, interactions, and configuration styles. That breadth is useful, but it comes with a cost. The rendering path has to serve a lot of masters.

Scientific plotting usually wants the opposite. The requirements are narrower, but much harsher:

  • append data continuously,
  • keep a rolling buffer without reallocating everything,
  • pan and zoom without stalling,
  • show dense time series without lying about peaks,
  • avoid pushing every frame through a UI framework,
  • keep the browser responsive even when the dataset is much larger than the screen.

This is where the open-source ecosystem often feels awkward. You either use a general-purpose charting library and start fighting performance, or you move to commercial tools like LightningChart JS and SciChart.js, which are impressive but not always a fit for smaller projects, internal tools, research prototypes, or open-source work.

Those commercial libraries exist for a reason. They prove that the browser can handle serious visualization if the renderer is designed for it. BlazePlot is my attempt to bring that kind of thinking into a focused open-source package: not a dashboard framework, not a chart zoo, just a fast plotting engine for the cases where performance actually matters.

WebGL2 is not an implementation detail

BlazePlot uses WebGL2 from the start, built on regl. There is no Canvas2D fallback in the core renderer.

That sounds strict, but it keeps the design honest. Large real-time plots are mostly a data movement problem. You want typed arrays, GPU buffers, predictable draw calls, and as little DOM involvement as possible. WebGL gives the browser access to hardware-accelerated graphics, which is exactly the layer I wanted to build on.

The DOM still has a place. Axis labels are DOM. Lightweight overlays can be DOM. But the actual plot — lines, scatter points, bars, areas, grids — belongs on the GPU. BlazePlot keeps that boundary clear.

That also makes debugging performance less mysterious. The chart exposes frame stats: FPS, frame time, vertex count, draw calls, upload bytes, and render mode. If something is slow, I want to know whether it is data extraction, GPU upload, picking, ticks, or actual drawing. Guessing is not a strategy.

The part most chart demos avoid

A lot of charting demos show that they can load a million points. That is useful, but it is not the whole story.

The harder question is: what happens after the data is loaded?

Can you zoom into it? Can you pan across it? Can new samples keep arriving? Can the chart still show the peak that only lasted one pixel column wide? Can the user interact with it without the browser feeling like it is underwater?

This is why BlazePlot has level-of-detail rendering built into the data path. When a view is sparse, it draws raw samples. When the view is dense, it switches to min/max segments. Instead of pretending a bucket of samples is one average value, it preserves the vertical range. Peaks stay visible. Outliers do not disappear just because the screen ran out of pixels.

This is not a new academic problem. Sveinn Steinarsson's thesis, Downsampling Time Series for Visual Representation, helped popularize LTTB-style visual downsampling. The VLDB paper M4: A Visualization-Oriented Time Series Data Aggregation makes the case for pixel-column-oriented aggregation with values like min and max. BlazePlot is not trying to be a research project, but it does take that lesson seriously: if you reduce data for visualization, preserve the information people are actually looking for.

Internally, BlazePlot uses ring buffers for streaming data and a min/max pyramid for efficient range queries. That lets it avoid rebuilding everything every frame. Wrapped buffers still expose logical-order access, and dense views can extract the right representation without scanning the entire raw dataset.

That is the kind of boring engineering that makes the UI feel fast.

The API is intentionally plain

I did not want a declarative chart object that gets rebuilt every render. I wanted something closer to an instrument: create a chart, add a signal, append samples, set a viewport.

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();

That is basically the mental model. Data comes in as typed arrays. Series own their buffers. The camera owns the viewport. The renderer draws what is visible.

BlazePlot currently supports line, area, scatter, and bar series. It has pan and zoom, smart ticks, data-anchored grid lines, outside axis gutters, hover picking, screenshots, and plugin hooks for legends and tooltips. I want those things, but I do not want them to leak into the hot path. Optional UI should sit around the renderer, not inside it.

The project structure follows that rule:

src/
  core/          # Data model, series, datasets, LOD
  render/        # GPU abstraction + regl backend
  interaction/   # Camera, axis ticks, interaction intent types
  ui/            # Chart orchestration

The core does not know about the DOM. The renderer does not own interaction policy. The chart wires things together. That separation is not architectural decoration; it is what makes the system easier to reason about when performance gets weird.

I wrote about this kind of restraint in The Philosophy of Minimalist Software. Minimalism is not about making smaller things for the sake of it. It is about reducing the number of places where behavior can hide.

Where BlazePlot fits

I am not trying to replace every charting library. If you need polished business dashboards, use the tools built for that. If you need a full commercial visualization stack with support contracts, SciChart and LightningChart are already there.

BlazePlot is for the middle space I kept needing myself: open-source, TypeScript-first, browser-native scientific plotting that can handle live data without turning the page into a slideshow.

It is also deliberately not finished in the way a product landing page pretends things are finished. The foundation is there: WebGL2 rendering, streaming buffers, min/max LOD, multiple series types, camera interaction, plugin hooks, frame stats, and a simple package API. The next work is less glamorous: better examples, more benchmarks, more edge-case tests, and more interaction patterns that come up in real scientific tools.

You can install it with:

bun install blazeplot

The goal is simple: make high-performance scientific plotting on the web feel boringly reliable. The browser is capable of serious visualization. Open-source scientific tools deserve plotting infrastructure that does not fall apart exactly when the data gets interesting.

Federico Cervelli

Federico Cervelli

Computer Science graduate and Software Developer at CAEN S.p.A. This blog is my digital lab for architectural deep-dives, technical experiments, and personal reflections.