For AI agents: the complete XY documentation index is at llms.txt. Markdown versions are available by appending .md or sending Accept: text/markdown.
Overview

/

Benchmarks

Benchmarks

This benchmark measures what a user waits for on a live interactive scatter chart. Every library receives every source row and runs through its own normal input path in a real browser. The sweep covers 10,000 to 100 million points on one Apple M5 Pro.

The clock stops only when the canvas is both correct and stable. Correct means sentinel points planted at known coordinates are lit in the expected places. Stable means the full drawing buffer is byte-identical for 10 consecutive frames. Progressive renderers are therefore charged until their last chunk lands, not just until their first paint.

Live interactive render time

Correct and stable canvas · Apple M5 Pro · lower is better

XY
XY · density off
Matplotlib
Plotly

XY holds 0.071 seconds at 10k and 0.081 seconds at 100M, nearly flat across four orders of magnitude. Above 200k rows, the default path draws a screen-bounded density surface and retains a sample for interaction instead of drawing one marker per row. Deep zooms request exact source rows.

Every exact-marker path scales with the row count. Matplotlib crosses one second at about 3M and reaches 13.4 seconds at 50M. Plotly crosses one second at about 2.5M and reaches 9.8 seconds at 25M.

Time until every point is on screen

Times are seconds. marks a size that did not satisfy the full benchmark contract: Plotly never finished constructing the 50M figure, while Matplotlib drew 100M points but did not resolve the zoom that followed.

Points10k100k500k1M2.5M5M10M25M50M100M
XY speedup16×34×89×177×
XY0.0710.0720.0750.0840.0830.0890.0830.0770.0760.081
XY (density=False)0.0850.0740.0870.0980.1110.1440.2060.4240.6451.343
Matplotlib (WebAgg)0.0860.1150.2240.3570.7581.4242.8046.83813.385
Plotly (scattergl)0.3410.3730.4770.6141.0331.7853.3679.794

The speedup row compares default XY with the next-fastest other library at each size. One run was recorded per cell. At the small end, timings carry roughly ±10 ms of run-to-run spread.

Peak Python-side memory

Peak resident memory is reported in GiB. Browser memory is measured separately and excluded here because headless Chrome occupies about 1 GiB before it draws a chart.

Points10k100k500k1M2.5M5M10M25M50M100M
XY advantage1.8×1.7×1.9×2.1×2.1×2.4×2.6×2.9×2.8×
XY0.050.050.060.070.130.190.320.701.362.58
XY (density=False)0.050.050.070.100.180.310.571.352.665.26
Matplotlib (WebAgg)0.090.090.120.150.280.460.842.063.85
Plotly (scattergl)0.210.180.280.360.601.051.864.70

The advantage row compares default XY with the next-lowest Python-side peak from another library. The exact-marker XY path reaches 100M in 1.343 seconds and 5.26 GiB, showing the engine's scaling without giving it aggregation credit.

Why the density path is a fair product comparison

No benchmark arm receives pre-thinned input. Each library gets all rows, then uses its normal rendering strategy. XY's density arm proves that all rows were included with a count oracle, while Matplotlib, Plotly, and density=False draw one marker per row.

That makes the default comparison an end-to-end product question: what does a user get from the ordinary API at this data size? The pale exact-marker XY series answers the separate like-for-like question of how the same engine scales when every row stays an individual marker.

What this benchmark does and does not show

The recorded sweep showsIt does not establish
Navigation to a correct, stable live scatter chartPerformance for every chart family or dashboard layout
Default XY and exact-marker XY across the same row ladderEquivalent rendered geometry after default XY enters density mode
Whether the scripted zoom returns a final correct frameEvery interaction pattern or server deployment
Python-side peak RSS on one Apple M5 ProBrowser memory, GPU memory, or performance on every platform

The benchmark also records first paint, during-gesture frame timing, settle time, browser memory, screenshots, raw JSON, and a synchronized video for each size. Those measurements remain separate so a fast first frame cannot hide unfinished rendering or deferred work.

Inspect and reproduce the evidence

The methodology and artifact contracts are documented in the repository:

After completing the runbook setup, run the same size ladder:

Keep results separated by environment. Hardware WebGL and SwiftShader rows are not interchangeable. For the rendering model behind XY's flat default curve, read Large data and performance.

Built with Reflex