Your interface has a ceiling. It is about five thousand nodes.
gpu-components is a WebGPU runtime and a component registry for the surfaces that hit it — timelines, grids, heatmaps, scatter plots, trace views. Copy the component source into your repo. The runtime stays a versioned dependency.
npx gpu-components add timelineNothing is on npm yet. The install surface is published early so it can be argued with while changing it is cheap.
Every dense UI dies the same way.
You ship it in DOM. It is fine at a thousand rows. At five thousand the frame budget is gone — that is roughly the ceiling on primitives one JS frame can issue. So you rewrite it in Canvas2D and buy an order of magnitude: about fifty thousand fillRects at 60fps. Then the dataset grows again.
The third rewrite is the one nobody budgets for. That is the one this library is.
Zoom is a uniform write, not a re-render.
A CPU pipeline re-walks the whole dataset on every pan, zoom, filter and brush. A GPU pipeline uploads it once. After that, interaction changes a few dozen bytes of uniform and the frame redraws from data that never moved — the dataset stopped being in the interaction path.
Per-element work runs on the hardware built for it
Colour mapping, normalisation, thresholding, LOD binning, min/max reduction — data-parallel work, run data-parallel.
Selection is a bitset the shader branches on
Brushing a hundred thousand spans does not mean touching a hundred thousand objects.
Compute lives in the data path, not beside it
Indirect dispatch and storage-buffer-driven vertex work are the durable WebGPU advantages. Fill rate is not.
Six GPU panels on a page means six GPU devices. That is the bug we started from.
Chart libraries already exist, and WebGPU charting already shipped. What does not exist is one shared runtime underneath heterogeneous components — a timeline, a heatmap, a grid and a scatter plot on the same page, through one device, one frame loop, one submit.
Starting the runtime…
import { GPUProvider } from '@gpu-components/react'
import { GPUTimeline } from '@/components/gpu/timeline'
// One init() → one Gpu → one GPUDevice, shared by every child.
<GPUProvider>
<GPUTimeline spans={spans} tracks={tracks} />
<GPUHeatmap data={matrix} />
</GPUProvider>- One command buffer per tick. Every mounted component’s passes land in a single
frame(), compute before render. - Shared caches. Pipelines, samplers, colormaps and transient uniforms exist once, not once per component.
- React never owns GPU state. Render state lives in refs and GPU buffers; React state is for semantics — selected ids, labels, the accessibility tree.
You own the component. We own the plumbing.
The runtime is infrastructure nobody wants to fork and everybody wants patched — device management, scheduling, leak fixes, device-loss handling. The component is policy: colours, LOD thresholds, label rules, interaction feel, shaders. That is exactly what teams need to change and what a props API can never anticipate.
npm i @gpu-components/core @gpu-components/reactVersioned, upgradeable, not yours to fork.
npx gpu-components add timelineCopied into your repo, yours to edit.
No shader prop. No renderer prop. No uniforms prop. Extensibility comes from owning the source, not from an escape hatch.
Below the crossover, this library is slower than what you already have.
Upload cost and pipeline overhead dominate at small N. Every component ships a section titled “When NOT to use this”, with the measured crossover number and a recommendation for what to use instead. Canvas2D is better than most people assume: a canvas grid already scrolls millions of rows at 60fps today, and anyone selling you a GPU grid on scroll performance is selling you something you already have.
We publish the comparison whether or not it flatters us. Anything not yet measured on the harness carries a target label, on every page.
Seventeen components. Every one of them runs live in your browser.
- GPUTimelineSpans, flame graphs, Gantt and waterfalls. Up to 500,000 spans, pan, zoom, brush-select.
- GPUHeatmapA dense matrix with a GPU colormap and GPU auto-ranging, zoomable on both axes.
- GPUDataGridRead-only grid with per-cell conditional formatting evaluated in the fragment shader.
- GPUScatter250,000 points, one draw call. Zoom, filter and brush are all uniform writes.
- GPUGraphForce-directed layout running entirely on the GPU, settling in front of you.
- GPUImageDiffSplit, onion-skin, difference and heat comparison of two images, with a GPU pixel count.
- GPULogViewerHalf a million lines in a GPU ring, streaming appends, and match density over all of them.
- GPUCandlestick200,000 OHLC bars, revised tick by tick, with a whole-history envelope along the bottom.
- GPUDensityMap200,000 lon/lat points hexbinned on the GPU over Web Mercator — graticule chrome, no tiles.
- GPUHistogram250,000 values, Freedman–Diaconis bins at ingest, GPU atomic binning into instanced bars.
- GPUDepGraphSugiyama layered package DAG with orthogonal edges — layout once on the CPU, GPU draw only.
- GPUNetworkTopologyForce-laid service mesh with status, link health, and traffic pulse.
- GPUAnnotationCanvasA Float32 field with a GPU colormap and a host-owned rect/ellipse/point/ruler/polygon overlay.
- GPUSpreadsheetEditable cells, a dependency-graph formula engine, selection, clipboard, and pivot tables.
- GPUNodeEditorDrag nodes, drag-to-connect ports, multi-select and delete — all CPU-owned, GPU-drawn.
- GPUWhiteboardA procedural dot-grid canvas with instanced shapes and ink, forked from GPUAnnotationCanvas.
- GPUPdfViewerA small resident-texture pool composites host-rasterized pages, virtualized and zoomable.
Do not take the claim. Run it.
The playground runs every component live, on your GPU, on your machine. Architecture, the scoring matrix, and the full “why not” live on their own pages when you want the detail.