Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,6 @@ jobs:
- name: Public API coherence
run: .venv/bin/python scripts/check_public_api.py

- name: Claim guardrails
run: .venv/bin/python scripts/check_claim_guardrails.py

- name: Lint (ruff)
run: |
.venv/bin/ruff check .
Expand Down
28 changes: 26 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,32 @@ release change. A change is incomplete while its affected specification is
missing, stale, or inconsistent with the implementation; resolve discrepancies
instead of treating the implementation alone as authoritative.

## Product North Star

XY is being built to outperform every competing charting library and become
the best overall charting system for Python. That goal spans every chart type
and every data scale, from a handful of values to billions of rows, across the
two dimensions users should not have to trade off: performance and
customization.

Treat every competitor lead as a concrete product gap. Work that affects a
user-visible capability should:

- compare XY with the relevant leaders, including Matplotlib, Seaborn, Plotly,
Bokeh, Altair, Datashader, HoloViews/hvPlot, and emerging alternatives;
- add or extend reproducible evidence across small, medium, large, and massive
data, covering startup, build and render time, interaction, memory, payload
and export size, and multi-chart applications where applicable;
- update the capability matrix and visual examples when the improvement is
about chart breadth or customization rather than timing; and
- commit the environment, raw results, output contracts, and reproduction
commands needed to inspect the win and catch regressions.

The goal is not to win one large-scatter benchmark. XY should become the
library users choose for ordinary charts, massive data, every chart family,
notebooks, applications, static output, performance, and complete design
control.

## Layout

- `src/` — Rust core, **minimal external crates** (C ABI; one cdylib per
Expand Down Expand Up @@ -107,5 +133,3 @@ PRs, or code. Set `git config user.name/user.email` to the human author
cache (§27). NaN never reaches vertex buffers (§19).
- f32 uploads are offset-encoded; tick/hover math stays f64 (§4/§16).
- Every decimation/tier decision is recorded in the spec, never silent (§28).
- Claims are mode-scoped and benchmarked (§2); update README numbers from
`scripts/bench.py`, don't invent them.
10 changes: 3 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ WHEEL ?=
BENCHMARK_JSON ?= benchmark.json
BENCHMARK_KIND ?= auto

.PHONY: help setup setup-browser check check-full check-browser check-conformance check-docs check-examples check-security check-errors check-api check-import check-ci check-claims check-benchmark-harness check-pyplot check-pyplot-speed check-sdist check-wheel check-artifacts check-benchmark-report list-checks test lint format typecheck public-api python-floor js-check rust-check abi-smoke
.PHONY: help setup setup-browser check check-full check-browser check-conformance check-docs check-examples check-security check-errors check-api check-import check-ci check-benchmark-harness check-pyplot check-pyplot-speed check-sdist check-wheel check-artifacts check-benchmark-report list-checks test lint format typecheck public-api python-floor js-check rust-check abi-smoke

help:
@printf '%s\n' \
Expand All @@ -19,14 +19,13 @@ help:
' make check-full run JS, Rust, and ABI gates too' \
' make check-browser run browser smokes (set CHROMIUM=/path/to/chrome)' \
' make check-conformance run accessibility + Chromium/Firefox/WebKit conformance' \
' make check-docs run docs examples and public claim guardrails' \
' make check-docs run documentation examples' \
' make check-examples run canonical API examples and Reflex asset registry checks' \
' make check-security run standalone HTML safety and client text-sink checks' \
' make check-errors run public error, LOD, and mutation-safety tests' \
' make check-api run lazy public API and type-surface checks' \
' make check-import run import-time and dependency-boundary checks' \
' make check-ci run CI/release workflow invariant checks' \
' make check-claims run public performance-claim guardrails' \
' make check-benchmark-harness run benchmark metadata/report/regression tests' \
' make check-pyplot run the matplotlib-shim suite and compatibility corpus' \
' make check-pyplot-speed enforce the per-family 10x static-PNG target (requires .[bench])' \
Expand Down Expand Up @@ -76,7 +75,7 @@ check-conformance:
node scripts/browser_conformance.mjs

check-docs:
$(PYTHON) scripts/verify_local.py --only examples,claim_guardrails
$(PYTHON) scripts/verify_local.py --only examples

check-examples:
$(PYTHON) scripts/verify_local.py --only examples
Expand All @@ -99,9 +98,6 @@ check-import:
check-ci:
$(PYTHON) scripts/verify_local.py --only ci_workflow

check-claims:
$(PYTHON) scripts/verify_local.py --only claim_guardrails

check-benchmark-harness:
$(PYTHON) scripts/verify_local.py --only benchmark_harness

Expand Down
98 changes: 64 additions & 34 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,43 @@
<img src="spec/assets/xy-sdf-binned-scatter.png" alt="XY-shaped probability field shown as a binned scatter chart." width="521">
</p>

<p align="center">
<b><a href="https://reflex.dev/docs/xy/" target="_blank" rel="noopener noreferrer">Try it live: a million points in your browser &rarr;</a></b>
</p>

<p align="center">
<a href="https://github.com/reflex-dev/xy/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/reflex-dev/xy/actions/workflows/ci.yml/badge.svg"></a>
<a href="https://app.codspeed.io/reflex-dev/xy?utm_source=badge"><img alt="CodSpeed" src="https://img.shields.io/endpoint?url=https://codspeed.io/badge.json"></a>
<a href="pyproject.toml"><img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-3776ab?logo=python&logoColor=white"></a>
<a href="https://reflex.dev/docs/xy/" target="_blank" rel="noopener noreferrer"><img src="https://img.shields.io/badge/docs-reflex.dev-blue" alt="Docs" /></a>
</p>

XY is an actively evolving, early-alpha Python charting library for large,
interactive datasets. Its Rust core and WebGL2 renderer keep work bounded by
what the screen can show; find guides, API reference, and examples in the
[documentation](https://reflex.dev/docs/xy/).
<p align="center">
<strong>Small data should be effortless. Large data should stay interactive.<br>
Neither should limit the design.</strong>
</p>

XY is an actively evolving, early-alpha project with one ambition: become the
fastest and most customizable charting library for Python — across every chart
type and every scale, from a handful of values to billions of rows.

Small datasets render directly and exactly. As data grows, XY's Rust core,
binary transport, and WebGL2 renderer reduce work to what the screen can show,
then bring detail back as you zoom. The same composable API controls marks,
axes, annotations, legends, tooltips, interactions, themes, responsive layout,
and HTML, raster, and vector exports.

## Highlights

- **Built for large data.** Reduces long lines and dense scatters to what the screen can show, and brings detail back as you zoom.
- **Designed for every scale.** Ordinary datasets take the direct path; large datasets automatically use decimated or density representations.
- **Customize every layer.** Control marks and composition in Python, then style chart chrome through stable CSS and Tailwind hooks.
- **Declarative interface.** Compose marks and guides, or use the familiar `xy.pyplot`.
- **Interactive by default.** Pan, zoom, hover, select, and inspect exact source rows.
- **One chart, many outputs.** Use notebooks or export HTML, raster, and vector formats.
- **Built for apps.** Embed responsive charts and style them with CSS or Tailwind.
- **Built for apps.** Embed responsive charts and style them with Python, CSS, or Tailwind.

## Is XY for me?

XY is a great fit for teams that want to explore large 2D datasets in Python,
share interactive notebook results, or ship self-contained charts on the web.
Build charts once, then display them in notebooks and apps or export them as
HTML, images, and vector graphics.
XY is for Python users who want one charting system for everyday plots, deeply
custom application visuals, and data too large for conventional browser chart
stacks. Build charts once, then display them in notebooks and apps or export
them as self-contained HTML, images, and vector graphics.

## Installation

Expand All @@ -42,7 +49,7 @@ pip install xy
uv add xy
```

## Getting started
## Getting started: one API, every scale

A chart is a container plus the marks inside it. Any sequence works — plain
Python lists need no NumPy:
Expand All @@ -57,7 +64,8 @@ chart = xy.line_chart(xy.line([1, 2, 3, 4, 5], [120, 180, 165, 240, 310]))
chart # notebooks render it
```

The same API scales. Chart a hundred million points as a density surface:
The data size changes; the chart model does not. The same API scales to a
hundred million points as a density surface:

<p align="center">
<picture>
Expand Down Expand Up @@ -117,25 +125,20 @@ plt.show()
The shim intentionally covers common plotting workflows rather than every
matplotlib feature. See the [compatibility guide](spec/matplotlib/compat.md).

## Benchmarks

<p align="center">
<img src="spec/assets/launch-benchmark-comparison.svg" alt="Cold-render time for a 10-million-point chart in XY, Matplotlib, and Plotly. Lower is better." width="1200">
</p>

In the recorded 10-million-point baseline, XY produced a static PNG in 0.023 s
versus 2.8 s for Matplotlib and 9.6 s for Plotly, and reached first interactive
render 16–20× sooner.
## Customize every layer

The committed launch baseline uses identical seeded data, a 900×420 output,
and three isolated cold runs. See the
[launch report](benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/report.md)
and [benchmark runbook](benchmarks/README.md) for the environment,
methodology, and raw results.
Performance is not a preset. Use Python to control data marks and chart
composition, then style browser chrome through stable CSS and Tailwind hooks.

## Styling
- **Marks:** data-driven color and size, custom colormaps, opacity, symbols,
gradients, strokes, and curves.
- **Guides:** axes, ticks, grids, annotations, legends, colorbars, and tooltips.
- **Interaction:** pan, zoom, hover, selection, crosshairs, callbacks, and
linked charts.
- **Layout and output:** layering, facets, responsive dimensions, themes, and
HTML, raster, or vector export.

Customize marks and chart chrome with Python, CSS, or Tailwind. See the [styling guide](docs/styling/index.md).
See the [styling guide](docs/styling/index.md) for patterns and examples.

What each mechanism reaches — per property, per chrome slot, per renderer, and
where it stops — is the [capability matrix](spec/api/capability-matrix.md),
Expand All @@ -150,6 +153,30 @@ chart = xy.line_chart(
)
```

## Benchmarks

<p align="center">
<img src="spec/assets/launch-benchmark-comparison.svg" alt="Cold-render time for a 10-million-point chart in XY, Matplotlib, and Plotly. Lower is better." width="1200">
</p>

In the recorded 10-million-point baseline, XY produced a static PNG in 0.023 s
versus 2.8 s for Matplotlib and 9.6 s for Plotly, and reached first interactive
render 16–20× sooner.

That baseline is one proof point, not the finish line. XY is being built to
outperform every competing charting library across small-data startup,
massive-data rendering, interaction, memory, payload and export size,
multi-chart applications, chart breadth, and customization. The committed
benchmark program, raw results, capability matrix, and visual examples are how
the project measures and proves that goal.

The committed launch baseline uses identical seeded data, a 900×420 output,
and three isolated cold runs. See the
[launch report](benchmarks/launch_baselines/xy-0.1.0/macos-arm64-m5-pro/report.md),
[benchmark runbook](benchmarks/README.md), and
[competitive benchmark specification](spec/benchmarks/results.md) for the
environment, methodology, raw results, and full evidence program.

## Embed XY in a Reflex app

With the `reflex-xy` adapter, any XY chart becomes a regular Reflex component.
Expand Down Expand Up @@ -266,8 +293,10 @@ the chart gallery, guides, and API reference. The repository also includes

## Roadmap

XY is 2D-first: broad chart coverage on top of the binary transport and
screen-bounded rendering, before any 3D work. Queued next, no dates implied:
XY is headed toward every chart family. The current implementation sequence
builds broad 2D coverage on top of the binary transport and screen-bounded
renderer, then extends the same performance and customization goals to
geographic, 3D, and volume visualization. Queued next, no dates implied:

- **Categorical distributions** &mdash; strip, swarm, beeswarm, boxen, rug
- **Regression diagnostics** &mdash; trendline, residual, QQ, PP
Expand All @@ -278,6 +307,7 @@ screen-bounded rendering, before any 3D work. Queued next, no dates implied:
- **Treemap, sunburst, and icicle**
- **Radar / polar and gauge** &mdash; needs polar axes first
- **Slope, bump, and dumbbell**
- **3D and volume** &mdash; scatter, surfaces, meshes, isosurfaces, and volumetric views

The full ranked backlog is in the [chart roadmap](spec/api/chart-roadmap.md).
Want a chart or feature that isn't listed?
Expand Down
3 changes: 1 addition & 2 deletions docs/advanced/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,7 @@ does not automatically become a browser object:
- Retained GPU buffers and local interaction avoid rebuilding the chart on
every pointer movement.

This is a focused design, not a claim that every chart is faster. Compare the
same data, output, and rendering mode. The
This focused design is the foundation for making every chart faster. The
[benchmark snapshot](/docs/xy/overview/benchmarks/) publishes those contracts
and records when XY uses a reduced representation.

Expand Down
6 changes: 1 addition & 5 deletions docs/api-reference/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,13 @@ import error rather than silently selecting another backend.
| --- | --- |
| Fast local verification | `make check` |
| Production-facing change | `make check-full` |
| Public docs, examples, or claims | `make check-docs` |
| Public docs and examples | `make check-docs` |
| Public exports or annotations | `make check-api` |
| Lazy imports and dependency boundaries | `make check-import` |
| Validation and mutation behavior | `make check-errors` |
| Standalone HTML and text safety | `make check-security` |
| Browser lifecycle and interaction | `make check-browser CHROMIUM=/path/to/chrome` |

Run `make check-claims` before publishing performance prose. Every comparison
must name the chart type, data size and shape, representation mode, backend,
render target, and whether browser time-to-first-render is included.

## Adding a Chart Type

A complete chart-family contribution normally spans:
Expand Down
5 changes: 2 additions & 3 deletions docs/core-concepts/large-data-and-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,8 @@ There are two different scaling regimes:
2. The resulting wire payload, WebGL geometry, and static SVG/native-PNG scene
are bounded by the chosen viewport representation.

This is why a “cost scales with pixels, not points” slogan needs qualification:
it describes the output side after reduction, not the complete data-to-pixels
pipeline.
The output side after reduction scales with pixels; the complete data-to-pixels
pipeline still includes row-dependent ingest and reduction work.

## Inspect memory and tier decisions

Expand Down
29 changes: 14 additions & 15 deletions docs/overview/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ description: Inspect XY's recorded launch benchmark with its exact output contra

# Benchmarks

XY's large-data claim is about the representation sent to a fixed-size output,
not about drawing every source row as an individual marker. The committed 0.1.0
launch baseline measures identical seeded scatter data at 900×420 pixels on an
Apple M5 Pro with 64 GiB RAM. Each successful cell below is the mean of three
isolated cold runs. The machine name and memory are copied verbatim from the
committed environment record.
XY's large-data architecture reduces source rows to the representation useful
for a fixed-size output instead of drawing every row as an individual marker.
The committed 0.1.0 launch baseline measures identical seeded scatter data at
900×420 pixels on an Apple M5 Pro with 64 GiB RAM. Each successful cell below
is the mean of three isolated cold runs. The machine name and memory are copied
verbatim from the committed environment record.

> **How to read this comparison.** XY switches dense scatter output to a
> screen-bounded density representation, while the default Plotly and
> Matplotlib paths retain every marker. These results compare each library's
> default user-visible outcome at the same output size; they do not claim that
> the libraries send identical geometry to the renderer.
> default user-visible outcome at the same output size; the libraries send
> different geometry to the renderer.

## Snapshot at 10 million points

Expand Down Expand Up @@ -121,8 +121,9 @@ frozen:
- **Release and hardware tracking:** immutable release directories plus
clearly separated macOS hardware-WebGL and CI SwiftShader results.

Until those artifacts are published, this page intentionally keeps its
headline claims scoped to the committed launch scatter.
The launch scatter is the first committed proof point. The competitive
benchmark program expands it across these workloads, chart families,
competitors, and environments.

## Inspect and reproduce the evidence

Expand All @@ -148,9 +149,7 @@ uv run --project "$BASELINE" --frozen python benchmarks/bench_launch_scatter.py
--chrome "$CHROME" --out launch-scatter-default.json
```

One machine and three runs describe that recorded environment, not every
machine or workload. New comparisons should retain chart type, data size,
representation, backend, output target, and browser-TTFR status rather than
shortening these results to a universal “faster than” claim. For the rendering
model behind the numbers, read
Each new comparison records its chart type, data size, representation, backend,
output target, and browser-TTFR status so the result can be reproduced and
improved. For the rendering model behind the numbers, read
[Large data and performance](/docs/xy/core-concepts/large-data-and-performance/).
Loading
Loading