Skip to content

Benchmarks

These numbers are produced by a reproducible harness (packages/bench) that drives system Chrome through Playwright + CDP — real Chromium timing, not a synthetic micro-benchmark. The charts below are rendered with UrCharts itself: the bars you see are a bar mark fed the committed results JSON.

Comparing against other libraries? The Comparison page runs the same scenarios across uPlot, ECharts, Chart.js, Lightweight Charts, Observable Plot, D3.js, Recharts, shadcn/ui Charts and Unovis, with a full capability matrix.

What's measured

Each scenario renders an identical, deterministic dataset (same seed → same geometry) across the three production engines, then records:

  • Render time — wall-clock from chart().mount() to the painted frame (two requestAnimationFrames), median of repeated runs.
  • Bytes / point — JS heap delta across the mount (HeapProfiler.collectGarbage then Runtime.getHeapUsage before/after), divided by point count. The headline number for the geometry-minimisation work.
  • DOM nodes — element count under the host (a proxy for browser-side cost; meaningful for the svg engine).

Current scenario matrix: cold-init (1k line, fresh page → time-to-first-frame), static-line, static-scatter, static-bars at 1k–100k points, plus driven scenarios — streaming (ring-buffer append per frame → frame p50/p95/max and dropped-frame %) and animate-entry (staggered bar entry via @urcharts/core/motion). Pick Frame p95 or Dropped frames in the metric selector to see those.

Reading the numbers honestly

  • svg emits a fixed-size DOM path — it culls off-plot geometry and LTTB-downsamples every frame, so its node count stays flat as the dataset grows (the line you see is visually faithful but not one node per point). Its heap still holds the full source array, so bytes/point reflects real memory.
  • canvas2d is the throughput leader for large static renders — no per-point DOM.
  • webgpu (the WebGPU engine, @urcharts/core/render/gpu) is measured headless via ANGLE/Metal; on a GPU-less CI box it will not initialise. Treat its headless figures as indicative, not absolute.

Methodology / fairness

Published numbers come from one fixed reference machine; the environment stamp (CPU, core count, Chrome version, commit SHA, run date) travels with every result and is shown under the chart. CI runs are regression-only — relative, never published as absolute.

When competitor comparisons land (uPlot, ECharts, Chart.js, lightweight-charts, Observable Plot), the rules are: identical data arrays, identical canvas size and device-pixel-ratio; two configurations per library (defaults and best-tuned); adapter code lives in the repo; and losses get published too — a column where a competitor wins, with a note on why, builds more trust than a clean sweep.

Status: harness + engine matrix + memory + streaming/animation scenarios (LAUNCH-PLAN B.7 items 1–3). Zoom-pan/crosshair scenarios and competitor adapters (uPlot, ECharts, …) are next.

Released under the MIT License.