Library / SDK
reflex-dev/xy avatar
reflex-dev/xy

XY: A Python charting library that trades exact markers for screen-bounded density

Project brief: Ultra-fast and customizable Python charts. Customize every layer Use Python to control the chart, from marks and axes to interactions and layout.

1,841 stars74 forksPythonApache-2.0

At a glance

What is it?
XY is an alpha-stage Python charting library from the Reflex team that renders interactive charts in the browser with a Rust core. Its core trick is switching to a density surface above 200k rows, which keeps render time flat but changes what you are actually looking at.
Who is it for?
Adopt XY if you need interactive web charts or notebooks with datasets in the millions to billions of points, and you accept that above 200k rows you are viewing a density surface, not exact markers. Do not adopt it if you require exact-marker rendering at scale, full matplotlib API compatibility, or production stability, since the project is in alpha and the compatibility guide lists unsupported charts.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 13 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What XY actually is and who it targets

XY is a Python charting library that renders interactive charts for the web, notebooks, and static exports. It is built by the Reflex team, the same group behind the Reflex web framework, and its homepage sits inside the Reflex docs. The intended user is a Python developer who wants one library for everyday plots and for large datasets, and who wants those charts to appear in a browser or notebook without switching tools. The README states that a chart is a container plus marks inside it, and that any sequence works, with NumPy optional. That is a deliberate design choice: you can pass plain Python lists. The library is in alpha, which the README flags prominently with an important note. That alpha status matters because it means the API can shift between releases, and the project is receiving frequent enhancements, which is a polite way of saying breaking changes are likely.

The density mechanism: how 100M points stay fast

The performance story rests on a specific mechanism, not just a fast renderer. For small charts, every point is sent to the browser. For large charts, the Rust core computes only what the screen needs to display, based on its resolution. Above 200k rows, XY switches from one marker per row to a screen-bounded density surface. Pan, zoom, hover, and selection run the same process for the new range, and a selection returns the original rows. That last part is the key: the density surface is a visual proxy, but the underlying data is still there for interaction. The README gives a concrete example with 100 million points drawn as a density surface, using parameters like density=True, opacity=0.85, and zoom_size_factor=2.6. Those parameters control how the density surface behaves when the user drills in. The zoom_size_factor and zoom_opacity values grow and solidify markers once a view goes deep enough to reach real rows. This is a trade-off: you get flat render times, but you are not looking at exact points until you zoom in.

Benchmarks: what the numbers do and do not show

The README includes a benchmark table comparing XY, Matplotlib WebAgg, and Plotly scattergl. The methodology is explicit: every library gets every row, is driven through its own input path in a real browser, and the clock stops only when the canvas is both correct and stable, meaning 10 byte-identical frames. That is a rigorous setup, and it charges progressive renderers until their last chunk lands. The headline numbers are XY at 0.071 seconds for 10k points and 0.081 seconds for 100M points. That flatness across four orders of magnitude is the density effect. The pale line in the chart, labeled XY with density=False, shows the same engine drawing one marker per row. It renders 100M exact markers in 1.34 seconds on 5.26 GiB. That is still fast, but it scales with N. Matplotlib crosses a second at about 3M points and reaches 13.4 seconds at 50M. Plotly crosses at about 2.5M and reaches 9.8 seconds at 25M, and it never finishes constructing the figure at 50M. The memory table shows XY using 2.58 GiB at 100M points, versus 5.26 GiB for the density=False path. These are the project's own numbers, published in its README, and they are plausible. But they come from the project's own benchmark, not from an independent test, and the README does not specify the hardware or browser version. Treat them as indicative, not as a guarantee for your machine.

Getting started: installation and the matplotlib compatibility layer

Installation is straightforward. The README gives two commands: pip install xy, or uv add xy if you use uv. The getting-started example is minimal. You create a line chart by passing a line mark with two lists, one for x and one for y. The chart object renders in a notebook, and you can call to_html, to_png, or to_svg to export it. That is the declarative API. There is also a matplotlib-compatible path. The README shows a pyplot workflow where you change the import to import xy.pyplot as plt and keep the rest of the plotting code. The example uses numpy, subplots, ax.plot with a format string like r--, and a legend, and it works. That compatibility layer is a major selling point for existing matplotlib users, but the README explicitly warns that not all charts and functionality are supported yet, and it points to a compatibility guide. That guide is the place to check before you assume your existing matplotlib code will run unchanged.

Customization and styling: Python, CSS, and Tailwind

The README emphasizes that you can customize every layer, from marks and axes to interactions and layout. Marks control color, size, opacity, symbols, gradients, strokes, curves, and colormaps. Guides cover axes, ticks, grids, annotations, legends, colorbars, and tooltips. Interaction includes pan, zoom, hover, selections, crosshairs, callbacks, and linked charts. Layout covers layers, facets, responsive dimensions, and themes. The styling example shows a line chart with a hex color, a width, and class_name and class_names parameters that accept Tailwind classes. That is unusual for a Python charting library. It means you can style charts with the same utility classes you use in a web app. The README points to a styling guide and a capability matrix for the full breakdown. The capability matrix is a practical resource, but it also reveals the project's maturity: some features are listed as not yet implemented. The customization surface is broad, but it is not complete, and the alpha label means the API for these customizations can change.

Limitations and failure modes: alpha status, exact markers, and compatibility gaps

The most obvious limitation is the alpha status. The README says XY is in alpha and is receiving frequent enhancements. That means the API can break between releases, and the project may not be stable enough for production use. The second limitation is the density trade-off. When you have more than 200k rows, you are not seeing every point. You are seeing a density surface. That is fine for overview, but if you need to inspect individual outliers at full dataset scale without zooming, this is the wrong tool. The third limitation is the matplotlib compatibility layer. The README says not all charts and functionality are supported yet. If your workflow depends on a niche matplotlib feature, you will need to check the compatibility guide and likely find a gap. The fourth limitation is memory. The README shows XY using 2.58 GiB at 100M points, which is lower than the exact-marker path at 5.26 GiB, but it is still a lot of memory. If you are working on a laptop with 8 GiB, a 100M-point dataset will be tight. The benchmarks also show that Plotly never finishes at 50M, but that does not mean XY will always succeed on every machine.

Alternatives: how XY differs from Matplotlib, Plotly, and Datashader

The obvious alternatives are Matplotlib and Plotly, which the benchmarks compare against. Matplotlib is a mature, static-first library with a huge ecosystem. Its WebAgg backend is interactive but slow at scale. Plotly is interactive-first and has a well-known API, but it also struggles beyond a few million points with scattergl. XY's difference is the Rust core and the density mechanism, which neither Matplotlib nor Plotly has built in. For large datasets, the common alternative is Datashader, which also renders data as images rather than individual markers. Datashader is a separate library that you typically combine with HoloViews or Bokeh. XY integrates density rendering directly into the chart API, so you do not need a separate pipeline. That is a real architectural difference. Datashader works by rasterizing the entire dataset into an image, and it does not provide built-in zoom-to-exact-rows interaction. XY does, because it keeps the original rows available for selection. If you need that interaction, XY is closer to what you want. If you already have a Datashader pipeline, switching to XY means learning a new API and giving up some control over the rasterization process.

Maintenance, license, and upgrade cost

The project is under the Apache-2.0 license, which is permissive and allows commercial use, modification, and redistribution, with the usual conditions about retaining copyright notices. The repository is actively developed, with the latest release v0.0.7 pushed on 2026-08-27, and there were two alpha releases before that on the same day. That release cadence shows momentum, but it also means the upgrade cost is real. Each new version may change the API, and since the project is below 1.0, there is no promise of backward compatibility. The README points to a capability matrix and a compatibility guide, which are the resources to consult before upgrading. The maintenance cost is not just about upgrading the library itself. It is also about your own code. If you use the matplotlib compatibility layer, you are relying on a subset of the matplotlib API that may not cover every function you use. If you use the declarative API, you are learning a new API that is still in flux. The project's own benchmarks are a useful reference, but they are not a substitute for testing your own datasets. Before adopting XY, run your own data through it, check the capability matrix for the chart types you need, and plan for the possibility that the API will change before 1.0.

Editorial conclusion

Adopt XY if you need interactive web charts or notebooks with datasets in the millions to billions of points, and you accept that above 200k rows you are viewing a density surface, not exact markers. Do not adopt it if you require exact-marker rendering at scale, full matplotlib API compatibility, or production stability, since the project is in alpha and the compatibility guide lists unsupported charts. Before adopting, verify that your chart types exist in the capability matrix, test the matplotlib compatibility layer against your existing plotting code, and check the current release notes for breaking changes, as the version is still below 1.0.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Community notes

Community notes