GPU-Powered WebGL Canvas Chart Library for JavaScript – WebGL Chart

Category: Chart & Graph , Javascript | May 7, 2026
Authortomsoftware
Last UpdateMay 7, 2026
LicenseMIT
Tags
Views0 views
GPU-Powered WebGL Canvas Chart Library for JavaScript – WebGL Chart

WebGL Chart is a low-level JavaScript WebGL charting library that renders interactive charts on an HTML canvas using GPU-backed buffers.

It works in Vanilla JavaScript through UMD bundles, and also includes Vue 3 and React wrappers for framework projects.

Features:

  • Renders all chart drawing through WebGL on a canvas for GPU-accelerated output.
  • Supports line, point, bar, area, bubble, range rectangle, and candlestick chart types.
  • Hover tooltips with crosshair lines and nearest-point snap markers.
  • Mouse wheel zoom and drag-to-pan interactions on both axes.
  • Multiple y-axes on a single chart, with optional right-side axis placement.
  • Stacked chart panels sharing one canvas and one event dispatcher.
  • Rolling chart mode using circular buffers that overwrite old data at capacity.
  • GPU-rendered axis labels, tick labels, and custom text overlays.
  • Annotation support, including vertical lines, horizontal lines, shaded bands, boxes, and text labels.

Use Cases:

  • Real-time sensor dashboards that stream telemetry data at high frame rates.
  • Financial applications that display candlestick charts with a volume bar series on a secondary axis.
  • Scientific instruments that need custom tick formatting for units like Hz, mV, or °C.
  • Engineering tools that overlay multiple signal types across stacked panels on one canvas.

How to Use It:

1. Use the core chart package and the WebGL utility package in module-based projects.

# NPM
npm i @tomsoftware/webgl-chart @tomsoftware/webgl-lib --save
# Vue 3
npm i @tomsoftware/webgl-chart @tomsoftware/webgl-lib @tomsoftware/webgl-chart-vue --save
# React
npm i @tomsoftware/webgl-chart @tomsoftware/webgl-lib @tomsoftware/webgl-chart-react --save

2. Or load the UMD version via CDN (Vanilla JS, no build step).

After loading via CDN, window.WebGLLib and window.WebGLChart become available as globals. The webgl-lib script must load before webgl-chart.

<script src="https://chart.hmilch.net/dist/1.1/webgl-lib.umd.js"></script>
<script src="https://chart.hmilch.net/dist/1.1/webgl-chart.umd.js">

3. Create a basic line chart. The canvas element needs a real computed CSS width and height before the chart can render. Set these in your stylesheet or with an inline style.

<canvas id="chartCanvas" style="width: 100%; height: 400px;"></canvas>
const { GpuChart, BasicChartLayout, Scale, SeriesLine } = window.WebGLChart;
const { GpuGrowingBuffer, Color, LayoutCell, EventDispatcher } = window.WebGLLib;
// Bind the renderer to the canvas.
const canvas = document.getElementById('chartCanvas');
const chart = new GpuChart().bind(canvas);
// Bind pointer events to the same canvas.
const dispatcher = new EventDispatcher();
dispatcher.bind(canvas);
// Create x values for the chart.
const time = new GpuGrowingBuffer('float32', 1000)
  .generate((i) => i * 0.01);
// Create y values from the x buffer.
const values = GpuGrowingBuffer.generateFrom('float32', time, (t) => {
  return Math.sin(t) * Math.cos(t * 0.25);
});
// Create the line series.
const line = new SeriesLine(time, values)
  .setColor(Color.darkGreen)
  .setThickness(2);
// Define the visible data ranges.
const scaleX = new Scale(0, 10);
const scaleY = new Scale(-1.2, 1.2);
// Create the layout and axes.
const container = new LayoutCell();
const layout = new BasicChartLayout(dispatcher, container, scaleX);
layout.addYScale(scaleY, 'Signal');
layout.xAxis.label?.setText('Seconds');
// Draw every frame.
chart.setRenderCallback((context) => {
  // Calculate chart areas before drawing.
  context.calculateLayout(container);
  // Process wheel, pan, and pointer events.
  dispatcher.dispatch(context);
  // Draw axes and grid lines.
  layout.draw(context);
  // Draw the data series inside the chart area.
  line.draw(context, scaleX, scaleY, layout.chartCell);
});
// Limit redraw work.
chart.setMaxFrameRate(12);
// Start the render loop.
chart.render();

4. All available configuration options.

  • canvas (HTMLCanvasElement): Sets the canvas target for GpuChart.bind().
  • callback (function): Sets the drawing routine for setRenderCallback().
  • fps (number): Sets the maximum frame rate for setMaxFrameRate().
  • min (number): Sets the visible minimum value for Scale.
  • max (number): Sets the visible maximum value for Scale.
  • eventDispatcher (EventDispatcher): Routes pointer, wheel, and pan events to chart layouts.
  • baseCell (LayoutCell): Defines the root layout area for a chart.
  • xScale (Scale): Sets the horizontal scale for BasicChartLayout.
  • scale (Scale): Adds a y-axis scale through addYScale().
  • title (string): Sets the y-axis title in addYScale().
  • position (VerticalAxisOrientation): Places a y-axis on the left or right side.
  • timeBuffer (Gpu buffer): Stores x positions for line, point, and area series.
  • dataBuffer (Gpu buffer): Stores y values for line and point series.
  • upperBuffer (Gpu buffer): Stores upper y values for area charts.
  • lowerBuffer (Gpu buffer): Stores lower y values for area charts.
  • xBuffer (Gpu buffer): Stores x positions for bars, bubbles, and range series.
  • yBuffer (Gpu buffer): Stores y values for bars and bubbles.
  • radiusBuffer (Gpu buffer): Stores bubble radius values.
  • y1Buffer (Gpu buffer): Stores the first y value for range series.
  • y2Buffer (Gpu buffer): Stores the second y value for range series.
  • color (Color): Sets a series, axis, annotation, or tooltip color.
  • upperColor (Color): Sets the top area color for SeriesArea.
  • lowerColor (Color): Sets the bottom area color for SeriesArea.
  • color1 (Color): Sets the first conditional range rectangle color.
  • color2 (Color): Sets the second conditional range rectangle color.
  • thickness (number): Sets line thickness in pixels.
  • size (number): Sets point size in pixels.
  • value (number): Sets bar width, offsets, zoom factors, or scale movement values.
  • stripeWidth (number): Sets annotation band width.
  • lineThickness (number): Sets annotation line thickness.
  • radius (number): Sets annotation box corner radius.
  • text (GpuText): Sets annotation or axis label text.
  • padding (number): Sets annotation label padding.
  • boxRadius (number): Sets annotation label background radius.
  • margin (number): Sets annotation label offset.
  • type (string): Sets buffer data type such as float32, vec2, vec3, or vec4.
  • sizeOrValues (number or number[]): Sets buffer capacity or initial data.
  • sourceBuffer (Gpu buffer): Sets the input buffer for derived data.
  • pointSize (number): Sets tooltip marker size.

5. API methods:

// Create a chart renderer.
const chart = new GpuChart();
// Bind the renderer to a canvas.
chart.bind(document.getElementById('chartCanvas'));
// Set the drawing callback.
chart.setRenderCallback(function(context) {
  context.calculateLayout(container);
});
// Cap the redraw rate.
chart.setMaxFrameRate(24);
// Read the configured frame rate.
chart.getMaxFrameRate();
// Start the render loop.
chart.render();
// Draw one frame manually.
chart.drawScene(performance.now());
// Dispose WebGL resources and listeners.
chart.dispose();
// Create a scale.
const scaleX = new Scale(0, 100);
// Move a scale by a fraction of its range.
scaleX.pan(0.1);
// Zoom a scale by a fraction of its range.
scaleX.zoom(0.05);
// Set scale bounds.
scaleX.setRange(10, 80);
// Convert a position back to a scale value.
scaleX.valueAt(0, 0.5, 1);
// Generate axis ticks.
scaleX.calculateTicks(10, 600, false);
// Create a standard chart layout.
const layout = new BasicChartLayout(dispatcher, container, scaleX);
// Add a y-axis.
layout.addYScale(scaleY, 'Temperature');
// Add a right-side y-axis.
layout.addYScale(scaleRight, 'Pressure', VerticalAxisOrientation.Right);
// Set the x-axis label.
layout.setXAxisLabel('Seconds');
// Draw axes and grid lines.
layout.draw(context);
// Create a line series.
const line = new SeriesLine(timeBuffer, valueBuffer);
// Set line color.
line.setColor(Color.darkGreen);
// Set line thickness.
line.setThickness(2);
// Connect the last point back to the first point.
line.setLineLoop(true);
// Draw the line series.
line.draw(context, scaleX, scaleY, layout.chartCell);
// Create a point series.
const points = new SeriesPoint(timeBuffer, valueBuffer);
// Set point color.
points.setColor(Color.red);
// Set point size.
points.setPointSize(4);
// Draw points.
points.draw(context, scaleX, scaleY, layout.chartCell);
// Create an area series.
const area = new SeriesArea(timeBuffer, upperBuffer, lowerBuffer);
// Set upper and lower fill colors.
area.setColor(Color.lightBlue, Color.lightGray);
// Draw the area.
area.draw(context, scaleX, scaleY, layout.chartCell);
// Create a bar series.
const bars = new SeriesBar(xBuffer, yBuffer);
// Set bar color.
bars.setColor(Color.orange);
// Set bar width in data units.
bars.setBarWidth(0.08);
// Offset bars for grouped columns.
bars.setOffsetX(0.04);
// Draw bars.
bars.draw(context, scaleX, scaleY, layout.chartCell);
// Create a bubble series.
const bubbles = new SeriesBubble(xBuffer, yBuffer, radiusBuffer);
// Set bubble color.
bubbles.setColor(Color.green.withAlpha(0.6));
// Scale bubble radius values.
bubbles.setBubbleScaling(1.2);
// Draw bubbles.
bubbles.draw(context, scaleX, scaleY, layout.chartCell);
// Create a range rectangle series.
const rangeRects = new SeriesRangeRect(xBuffer, lowBuffer, highBuffer);
// Set conditional range colors.
rangeRects.setColor(Color.darkGreen, Color.red);
// Set range rectangle width.
rangeRects.setBarWidth(0.5);
// Draw range rectangles.
rangeRects.draw(context, scaleX, scaleY, layout.chartCell);
// Create a range line series.
const rangeLines = new SeriesRangeLine(xBuffer, lowBuffer, highBuffer);
// Set range line color.
rangeLines.setColor(Color.black);
// Set range line width.
rangeLines.setLineWidth(2);
// Draw range lines.
rangeLines.draw(context, scaleX, scaleY, layout.chartCell);
// Create annotation storage.
const annotations = new Annotations();
// Add a shaded box.
annotations.addBox(2, 10, 5, -10, Color.orange.withAlpha(0.25), 4);
// Add a vertical marker line.
annotations.addVerticalLine(4.2, Color.red, 8, 2);
// Add a horizontal threshold line.
annotations.addHorizontalLine(12, Color.green, 5, 2);
// Draw annotations.
annotations.draw(context, scaleX, scaleY, layout.chartCell);
// Create a tooltip guide line.
const tooltipLine = new TooltipLine();
// Set tooltip guide color.
tooltipLine.setMouseColor(Color.gray);
// Show vertical and horizontal guide lines.
tooltipLine.showMouseLines(true, true);
// Draw tooltip guide lines.
tooltipLine.draw(context, dispatcher.getMousePosition(), layout.chartCell);
// Create tooltip markers.
const markers = new TooltipMarkers();
// Clear registered series.
markers.clearSeries();
// Register a series for hover lookup.
markers.addSeries(timeBuffer, valueBuffer, scaleX, scaleY, Color.darkGreen, 8);
// Draw nearest markers and labels.
markers.draw(context, dispatcher.getMousePosition(), layout.chartCell);
// Create a growing buffer.
const growing = new GpuGrowingBuffer('float32', 1000);
// Fill a buffer from an index callback.
growing.generate(function(i) {
  return i * 0.01;
});
// Create a derived buffer.
const derived = GpuGrowingBuffer.generateFrom('float32', growing, function(value) {
  return Math.sin(value);
});
// Add one or more values.
growing.push(1.25, 1.5, 1.75);
// Add a value array.
growing.pushRange([2, 2.25, 2.5]);
// Clear buffer data.
growing.clear();
// Read one component.
growing.getComponentAt(0, 0);
// Read one logical item.
growing.getAttributeAt(0);
// Write one component.
growing.setComponentAt(0, 0, 3.14);
// Write one logical item.
growing.setAttributeAt(0, [3.14]);
// Find the nearest index for a value.
growing.findIndex(2.5);
// Create a ring buffer for live charts.
const ring = new GpuRingBuffer('float32', 300);
// Break a false line segment after wrapping.
ring.setBreakAfterWritePosition(true);
// Copy the last value to the beginning during wrap.
ring.setCopyLastValueToBeginning(true);

6. Events:

// Create the event dispatcher.
const dispatcher = new EventDispatcher();
// Bind mouse and touch input to the canvas.
dispatcher.bind(document.getElementById('chartCanvas'));
// Process queued events inside the render callback.
dispatcher.dispatch(context);
// Handle mouse wheel events over the chart area.
dispatcher.on(EventTypes.Wheel, layout.chartCell, function(event) {
  scaleX.zoom(event.wheelDelta / 600);
});
// Handle drag pan events over the chart area.
dispatcher.on(EventTypes.Pan, layout.chartCell, function(event, node, area) {
  scaleX.pan(event.panDeltaX / area.width);
});
// Handle pointer movement over the chart area.
dispatcher.on(EventTypes.MouseMove, layout.chartCell, function(event) {
  console.log(event.position);
});
// Read the current pointer position for tooltips.
const pointer = dispatcher.getMousePosition();
// Read mouse button state.
const buttons = dispatcher.getMouseButtons();
// Remove event listeners when the chart leaves the page.
dispatcher.dispose();

FAQs:

Q: How do I add zoom and pan?
A: Bind EventDispatcher to the canvas and call dispatcher.dispatch(context) inside the render callback. BasicChartLayout registers chart-area zoom and pan handlers.

Q: Can I build candlestick charts?
A: Yes. Use SeriesRangeRect for open and close bodies, then use SeriesRangeLine for high and low wicks.

Q: How do I handle live data?
A: Use GpuRingBuffer for fixed-history streams. Use setBreakAfterWritePosition(true) if line wrapping creates a false segment.

Q: How do I format axis tick labels as dates or currency?
A: Call setTickFormat() on the axis instance. The callback receives the raw numeric tick value and returns a display string. Store relative values in the GPU buffers (for example, milliseconds since a fixed epoch) and reconstruct absolute labels inside the formatter to preserve 32-bit float precision.

You Might Be Interested In:


Leave a Reply