Most libraries claim to be "optimized for large datasets." Here are real, reproducible numbers instead — including the cases where a competitor is faster. We would rather you trust the comparison than win it.
Every number below is best-of-repeated-runs render time (one
fresh page per measurement) in headless Chromium at 900×600, comparing
CanvasXpress 69.0 against Plotly 2.35.2 (SVG
scatter and WebGL scattergl), ECharts 5.5
(large canvas mode), Chart.js 4.4, and deck.gl 8.9
(WebGL). All timed the same way — wall-clock from construction to render complete. JS-heap
deltas are Chrome-only and approximate.
Don't take our word for it — run the benchmark live in your own browser (CanvasXpress vs Plotly SVG vs Plotly WebGL, 10k–1M points). For a feature-by-feature comparison, see the library comparisons.
The same random scatter rendered by six libraries across three technologies — SVG/DOM, 2D canvas, and WebGL/GPU. All timed identically: wall-clock from construction to render complete. We show the numbers as they fall, including where CanvasXpress is not the fastest.
| Points | CanvasXpress canvas |
ECharts canvas |
Chart.js canvas |
Plotly SVG | Plotly WebGL | deck.gl WebGL |
|---|---|---|---|---|---|---|
| 10,000 | 56 ms | 25 ms | 28 ms | 85 ms | 87 ms | 28 ms |
| 100,000 | 112 ms | 54 ms | 153 ms | 579 ms | 121 ms | 36 ms |
| 500,000 | 381 ms | 165 ms | — | 2,700 ms | 245 ms | 53 ms |
| 1,000,000 | 736 ms | 323 ms | — | 5,353 ms | 377 ms | 79 ms |
Read honestly: for raw scatter throughput,
CanvasXpress is not the fastest. A GPU library (deck.gl, ~79 ms
at 1M) leads, and an optimized canvas path (ECharts large mode,
~323 ms) and Plotly's WebGL (~377 ms) also beat it; CanvasXpress (~0.74 s) clears Plotly's SVG
and, at 100k, Plotly's WebGL. Notably it is slower even though it decimates to a handful
of representative marks while the others draw every point — so the cost is not drawing, it is
CanvasXpress's data pipeline. (Chart.js is not built for high volume and is omitted above 100k.)
This column is roughly half of what 68.1 measured (1,744 ms at 1M): since 68.9 the per-sample
metadata the pipeline builds is computed in one pass and, for large sample sets, on demand.
Why spend that time? Because CanvasXpress is a visualization and analysis engine, not a pure renderer — most of its time builds an interactive data model (see below). If your need is purely drawing a million points fast, deck.gl or ECharts is the better tool, and we say so. We evaluated a WebGL data layer and declined it: it would only touch the draw, which is the smaller part of this time.
Note on D3.js: it is deliberately absent — D3 is a low-level toolkit for building visualizations from primitives, not a charting library with a scatter to call, so timing it would mean benchmarking a hand-written implementation, not the library. The D3 comparison covers that distinction.
The table above uses CanvasXpress's automatic density decimation (one representative per pixel cell), which is the right default. When a figure genuinely needs every point drawn — a spread embedding coloured by a continuous value, where each point matters — the gap narrows but Plotly's WebGL still leads:
Read this table on its own — its times are not comparable to the six-library table above. That table is decimated: CanvasXpress draws only a handful of representative marks, so those numbers are dominated by the data pipeline, not drawing. This one is draw-bound — CanvasXpress actually rasterises 107,809 marks here (we verified the count), which is why 150k full-fidelity (853 ms) costs more than 500k decimated (381 ms) in the table above. The two measure different work; compare down each column, not across the tables.
| Points (all drawn) | CanvasXpress (2D canvas) | Plotly (scattergl / WebGL) |
|---|---|---|
| 150,000 (107,809 marks) | 853 ms · 215 MB | 359 ms · 93 MB |
This row is exactly like-for-like — both render all points. 69.0 trimmed it (950 → 853 ms) by dropping dead per-shape context resets; the larger 69.0 gain is on redraws of the same chart (about 650 → 475 ms), which a cold-render benchmark does not show. A WebGL layer was measured against this row and declined: it would remove at most ~250 ms of draw and none of the data work below.
A first render is not just "where does each point go." Most of CanvasXpress's cold time is spent building an analytical model of the data — and that is the difference that matters, not the pixels.
Of the ~0.9 seconds to first-render the 150k full-fidelity scatter above, only about a quarter is the actual draw (~220 ms — the part a GPU would accelerate). The rest (~650 ms) is data work: detecting column types, indexing samples and variables, computing ranges, building the density structure, and freezing the data model. That work makes filtering, grouping, clustering, recolouring, hover, cross-plot broadcast, and reproducible export immediate once the chart is up — because the model is already there.
A thin GPU renderer is fast at first paint precisely because it does not do this — it uploads coordinates and draws. Ask it to filter a category, recolour by a new field, cluster, or hand back a figure that round-trips, and that work either happens later, on you, or isn't available. So the honest reading of the tables above: on first paint CanvasXpress pays up front for a model competitors don't build; on every interaction after, that model is already paid for. WebGL is on our roadmap for the draw third — it will not (and is not meant to) remove the analysis, which is the point of the library.
There is a second cost the tables don't show, and it is the one that most sets CanvasXpress apart: it ingests the whole dataset, not only the columns the current chart happens to draw. Extra variables, sample and variable annotations, factor levels, and metadata all come in and stay live — available for analysis even though they were never plotted.
A pure renderer takes the two or three arrays it needs for the marks on screen and discards the rest; ask it about a column you didn't map and it never had it. CanvasXpress keeps the full table indexed behind the figure, so you can colour or group by a variable that isn't on either axis, filter on an annotation the chart never showed, compute a new field, cluster on the unplotted dimensions, pivot the same data into a different chart, or export the analysed result — all without reloading or re-sending the data. The chart is a view onto a data model, not the data itself.
That is why the honest comparison isn't only "how fast are the pixels." Part of the cold-render time above buys a queryable, analysable copy of everything you handed the library, plotted or not — which is a different product from a fast draw of the subset you chose to show.
large mode, renders
hundreds of thousands to millions of points several times faster than CanvasXpress.The honest one-glance version of the two sections above. CanvasXpress does not win every row — and saying so is the point.
| If you need… | Consider |
|---|---|
| Raw GPU rendering of millions of points | deck.gl / ECharts |
| General-purpose web dashboards | Plotly / ECharts |
| Maximum low-level visualization freedom | D3 |
| Interactive scientific exploration + reproducibility | CanvasXpress |
| R / Python / JS scientific visualization with one grammar | CanvasXpress |
| Visualization generated and modified by AI agents | CanvasXpress |
CanvasXpress timing is the engine's own reported render
total (excluding harness overhead); Plotly timing is the Plotly.newPlot
wall-clock. Each measurement uses a fresh page so an accumulated JS heap can't inflate the
result. The exact fixtures, scales, and method are fixed and deterministic, and the raw
results are versioned alongside the figures they describe. These numbers were measured on
the date shown on this page against the versions named above; they are rerun as the engine
changes rather than quoted once and forgotten.