Charts that follow data as it arrives — append new readings, keep a rolling window, redraw in a couple of milliseconds.
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.
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);
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
}
y.data is one variable; each column is one new sample.y.vars, rows are matched to the chart’s variables by name, in any order; without it, in order. A variable the message omits gets an empty value.x carries annotations for the new samples. An annotation the message omits is left empty for them.false as the second argument to append without redrawing, for example to batch several messages into one redraw.| Option | Default | Meaning |
|---|---|---|
streamWindow | null | Samples to keep. When more arrive, the oldest are dropped. null keeps everything. |
streamEvict | drop-oldest | What 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 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.
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}}))
Measured per push on a 200-sample window, including the redraw:
| Chart | Median | 95th percentile |
|---|---|---|
| Line | 1.9 ms | 3.7 ms |
| Scatter2D | 1.1 ms | 2.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.