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.
- Built for large data. Reduces long lines and dense scatters to what the screen can show, and brings detail back as you zoom.
- 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.
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.
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.
The committed launch baseline uses identical seeded data, a 900×420 output, and three isolated cold runs. See the launch report and benchmark runbook for the environment, methodology, and raw results.
pip install xy
# or, with uv
uv add xyPublished wheels contain the Python package, JavaScript client, and native Rust core. End users do not need Rust, Node, npm, or a CDN.
Create a small business chart:
import xy
months = [1, 2, 3, 4, 5, 6]
revenue = [42, 45, 48, 51, 55, 59]
pipeline = [35, 38, 42, 40, 46, 50]
chart = xy.line_chart(
xy.line(months, revenue, name="revenue", color="#2563eb"),
xy.line(months, pipeline, name="pipeline", color="#16a34a"),
xy.x_axis(label="month"),
xy.y_axis(label="USD thousands"),
xy.legend(),
title="Revenue vs pipeline",
)
# chart.to_html("chart.html")
# chart.to_png("chart.png")
# chart.to_svg("chart.svg")
chartThe same chart can be exported without changing how it is built.
XY currently includes line, scatter, area, histogram, bar and column, heatmap, error bar and band, box, violin, ECDF, hexbin, contour, step, stairs, stem, triangle mesh, and faceted charts. See the copyable examples for the complete surface.
For common pyplot workflows, change the import and keep the plotting code:
import numpy as np
import xy.pyplot as plt
x = np.linspace(0, 10, 200)
fig, ax = plt.subplots()
ax.plot(x, np.sin(x), "r--", label="signal")
ax.legend()
plt.show()The shim intentionally covers common plotting workflows rather than every matplotlib feature. See the compatibility guide.
Customize marks and chart chrome with Python, CSS, or Tailwind. See the styling guide.
chart = xy.line_chart(
xy.line(x, y, color="#7c3aed", width=3),
class_name="rounded-xl bg-white",
class_names={"tooltip": "rounded-lg bg-zinc-900 text-white"},
)With the reflex-xy adapter, any XY chart becomes a regular Reflex component.
Place it inside cards, grids, tabs, or dashboards with no JavaScript, iframe,
or separate chart service.
Register the adapter once:
# rxconfig.py
import reflex as rx
import reflex_xy
config = rx.Config(
app_name="dashboard",
plugins=[reflex_xy.XYPlugin()],
)Then add a chart anywhere in the component tree:
import reflex as rx
import reflex_xy
import xy
signups = xy.line_chart(
xy.line([1, 2, 3, 4, 5], [120, 180, 165, 240, 310]),
title="Weekly signups",
)
def index() -> rx.Component:
return rx.card(
rx.heading("Growth"),
reflex_xy.chart(signups, height="320px"),
width="100%",
)
app = rx.App()
app.add_page(index)The chart keeps its built-in hover, pan, and zoom behavior. For charts driven by Reflex state, events, or live streams, see the Reflex integration guide and the runnable example app.
Most chart stacks serialize every value as JSON and ask the browser to draw
every mark. XY instead keeps exact values in a ColumnStore, computes an
appropriate level of detail in Rust, and transfers typed binary buffers.
Decimated and density views are bounded by the visible result.
flowchart TB
API["Python API<br/>Build the chart"]
STORE["ColumnStore<br/>Keep canonical f64 columns"]
CORE["Native Rust compute<br/>Direct · decimated · density"]
PAYLOAD["Compact payload<br/>Data-less JSON spec + typed binary buffers"]
RENDER["Browser or notebook<br/>WebGL2 marks · Canvas axes · DOM interface"]
API --> STORE --> CORE --> PAYLOAD --> RENDER
This is why zooming matters: a dense overview can use aggregation, while a narrow view can return to exact points. With a live host, pan and zoom can request a refined payload. Canonical f64 data stays in Python so hover and selection can still return original rows.
For the full design, see the design dossier.
- Declarative 2D charts with marks, axes, annotations, legends, tooltips, and CSS/Tailwind-friendly styling hooks.
- Interactive notebook and application views with pan, zoom, hover, and selection.
- Self-contained HTML and browser-free PNG, JPEG, WebP, SVG, and PDF exports from the same chart object.
- Large-data views that adapt from direct rendering to decimated and density representations as the visible range changes.
Start with the XY documentation for installation, the chart gallery, guides, and API reference. The repository also includes copyable API examples, benchmark details, and the changelog.
