CanvasXpress Live Streaming

Charts that follow data as it arrives — append new readings, keep a rolling window, redraw in a couple of milliseconds.

Introduction to Live Streaming

Call pushData with each new reading and the chart appends it, drops the oldest samples beyond streamWindow, and redraws. The chart stays a fixed-size rolling window however long the stream runs, and any statistics drawn over it are recomputed for the current window.

The engine only draws: it does not open connections. Where the data comes from is up to you — a WebSocket, a poll, a device — or, with no code at all, a dashboard panel bound to a live stream served by canvasxpress-connectors.

Quick Start

Create the chart once, set streamWindow, then push each reading. This chart receives a new sample every half second and keeps the last 30.

var cX = new CanvasXpress("canvasStream", {
  y: { vars: ["cpu", "memory"], smps: ["t1"], data: [[50], [40]] }
}, {
  graphType: "Line",
  graphOrientation: "vertical",
  streamWindow: 30,        // keep the newest 30 samples
  smpLabelInterval: 5
});

var n = 1;
setInterval(function () {
  n++;
  cX.pushData({
    y: { vars: ["cpu", "memory"], smps: ["t" + n], data: [[readCpu()], [readMemory()]] }
  });
}, 500);

What to Push

A message is a CanvasXpress data object holding only the new samples:

{
  y: { vars: ["cpu", "memory"],          // optional: rows are matched by name
       smps: ["t41", "t42"],             // one or more new samples
       data: [[51.2, 49.8], [63.1, 63.4]] },
  x: { time: ["12:00:41", "12:00:42"] }  // optional per-sample annotations
}

The Rolling Window

OptionDefaultMeaning
streamWindownullSamples to keep. When more arrive, the oldest are dropped. null keeps everything.
streamEvictdrop-oldestWhat to drop when the window is full. drop-oldest is the only policy.

Set a window for any stream that runs for long: without one, the chart keeps every sample and memory grows with it. Charts that do not use pushData are unaffected by these options.

Each push counts as new data: grouping, regression and smoothing fits, density and clustering are recomputed for the current window, and active data filters are applied to it.

In Dashboards, Without Code

In canvasxpress-dashboards, a data source of kind live subscribes a panel to a stream. In the builder, pick a 📡 stream in a panel’s Data list and set Window and Every (s); the spec records it as:

"data": {
  "feed": { "kind": "live", "url": "/connectors/api/stream/demo?vars=cpu,mem",
            "window": 60, "variables": ["cpu", "mem"] }
}

The Live Ops example shows two live panels next to a 24-hour snapshot. The live streaming guide covers the details.

Serving a Stream

canvasxpress-connectors serves streams over Server-Sent Events: GET /api/streams lists them and GET /api/stream/<name> opens one. It ships a simulated demo feed. A feed of your own is an object with an interval and a poll() that returns the next message, registered when you mount the app:

def open_quotes(query, interval, user):
    # Runs on the server for each subscription: the upstream secret stays here.
    return MyQuoteFeed(symbols=query.get("symbols", "IBM").split(","), interval=interval)

app.mount("/connectors", create_byo_app(
    store=store, serve_static=False,
    live_streams={"quotes": {"title": "Quotes", "variables": ["IBM"], "open": open_quotes}}))

Performance

Measured per push on a 200-sample window, including the redraw:

ChartMedian95th percentile
Line1.9 ms3.7 ms
Scatter2D1.1 ms2.1 ms

That is well inside one animation frame (16.7 ms), so a chart can take several updates a second with room to spare.

Scope and Limits

⇧