Skip to main content

Reflex integration for xy: WebGL charts over the app's own websocket

Project description

reflex-xy

xy figures as first-class Reflex components: WebGL rendering, million-point interactivity, and streaming updates — with chart data riding the app's existing websocket, not a sidecar API.

How it works

  • Control plane (Reflex-native). The only chart state is a token string, minted by a @reflex_xy.figure computed var. Semantic events — on_point_hover(event), on_point_click(event), on_select_end(event), and on_view_change(event) — arrive as ordinary Reflex event handlers with small JSON payloads.
  • Data plane (xy-native). A second socket.io namespace (/_xy) multiplexed onto the app's own engine.io websocket ships the spec as JSON and every data column as a binary frame (no JSON numbers, no base64, no extra endpoints to reverse-proxy). Pan/zoom/hover round-trips go straight to the figure kernel and never touch Reflex state.
  • No figure server. Figures live in a per-process registry as rebuildable caches: the token encodes (client, state, var), so any backend worker can re-run the builder against Reflex state (already distributed via redis in prod) when a reconnect lands on it.

Usage

# rxconfig.py
import reflex as rx
import reflex_xy

config = rx.Config(app_name="dash", plugins=[reflex_xy.XYPlugin()])
# dash/dash.py
import numpy as np
import reflex as rx
import xy
import reflex_xy


class Dash(rx.State):
    points: int = 200_000
    hovered: dict = {}

    @reflex_xy.figure
    def chart(self) -> xy.Chart:
        rng = np.random.default_rng(7)
        xs = rng.normal(size=self.points)
        ys = xs * 0.6 + rng.normal(scale=0.6, size=self.points)
        return xy.scatter_chart(xy.scatter(xs, ys), width="100%", height=460)

    @rx.event
    def on_hover(self, row: dict):
        self.hovered = row


def index() -> rx.Component:
    return rx.vstack(
        reflex_xy.chart(Dash.chart, on_point_hover=Dash.on_hover, height="460px"),
        rx.text(Dash.hovered.to_string()),
        width="100%",
    )


app = rx.App()

Change points in an event handler and the chart re-publishes itself to every subscriber — the token never changes, so nothing re-renders except pixels.

Builders can be async def (they become reflex AsyncComputedVars, same rule as rx.var) — await a database, HTTP endpoint, or dataframe store:

    @reflex_xy.figure
    async def remote(self) -> xy.Chart:
        rows = await fetch_rows(self.query)
        return xy.line_chart(xy.line(rows.t, rows.value))

Streaming: reflex_xy.append(token, x=[...], y=[...]) from any handler or background task pushes an incremental update over the same socket.

Events and cross-filtering

Live token-backed charts emit versioned, bounded dictionaries with a stable token and canonical row IDs. Selection rows can update ordinary State, which causes dependent @reflex_xy.figure builders to republish without changing their tokens or remounting the chart:

class Dash(rx.State):
    selected_groups: list[str] = []

    @rx.event
    def filter_groups(self, event: dict):
        selection = event["selection"]
        self.selected_groups = [] if selection["cleared"] else sorted({
            row["color_category"] for row in selection["rows"]
        })

def index():
    return rx.grid(
        reflex_xy.chart(Dash.groups, on_select_end=Dash.filter_groups),
        reflex_xy.chart(Dash.filtered_detail),
    )

Fixed-data charts

For a chart that doesn't depend on state, skip the state var entirely — pass the Chart straight in:

def index() -> rx.Component:
    return reflex_xy.chart(
        xy.line_chart(xy.line(t, np.sin(t)), width="100%", height=220),
        height="220px",
    )

That compiles the figure to a content-addressed binary asset at page build and renders it with zero backend involvement — client-side hover, pan/zoom, and density re-bin, same as Figure.to_html() exports; works under reflex export. When a fixed chart still needs kernel round-trips (deep drilldown into millions of points), register it once at module scope instead:

cloud = reflex_xy.inline(xy.scatter_chart(xy.scatter(x, y)))  # module scope

def index() -> rx.Component:
    return reflex_xy.chart(cloud, height="460px")

inline() tokens are content-addressed, so every backend worker derives the same one — no state, no coordination. The escalation path is: direct Chart (static) → inline() (fixed data, live kernel) → @reflex_xy.figure (per-session, state-driven).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reflex_xy-0.0.1.tar.gz (151.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reflex_xy-0.0.1-py3-none-any.whl (40.3 kB view details)

Uploaded Python 3

File details

Details for the file reflex_xy-0.0.1.tar.gz.

File metadata

  • Download URL: reflex_xy-0.0.1.tar.gz
  • Upload date:
  • Size: 151.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for reflex_xy-0.0.1.tar.gz
Algorithm Hash digest
SHA256 0162e9ebbf89e16adcd09c450d0cd7c41dd0bc2ea103ac556c03fcef38677d0b
MD5 b792738c0149a0f9794d8850e405c1ee
BLAKE2b-256 0775f7517e97824ad56cf8fc32cc272f51cec2f17690d15a61bb7b685c2edba5

See more details on using hashes here.

Provenance

The following attestation bundles were made for reflex_xy-0.0.1.tar.gz:

Publisher: release-reflex-xy.yml on reflex-dev/xy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file reflex_xy-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: reflex_xy-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 40.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for reflex_xy-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 40cfc5cf343ed2722fe304a967ed4170e146d5d2f1b61434b3e50a9a2acd7ad0
MD5 7be19b808d4361a50cfb6be48b2e3ddf
BLAKE2b-256 1b3ddacbec5ac1d37ccb2cafee682b016f9645ec77e0e7c23f297cf3261dd19a

See more details on using hashes here.

Provenance

The following attestation bundles were made for reflex_xy-0.0.1-py3-none-any.whl:

Publisher: release-reflex-xy.yml on reflex-dev/xy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page