Streaming & Realtime
Install:
dotnet add package MatPlotLibNet
Live line chart
Append data points from any thread. The chart auto-updates at the configured frame rate:
StreamingLineSeries? series = null;
var sf = Plt.Create()
.WithTitle("Live Telemetry")
.AddSubPlot(1, 1, 1, ax =>
{
series = ax.StreamingPlot(capacity: 5000, configure: s =>
{
s.Color = Colors.Blue;
s.Label = "Sensor A";
});
})
.BuildStreaming(TimeSpan.FromMilliseconds(33)); // 30fps
// Append from data source (any thread)
for (int i = 0; i < 1000; i++)
{
series!.AppendPoint(i, Math.Sin(i * 0.1) + Random.Shared.NextDouble() * 0.5);
await Task.Delay(10);
}
sf.Dispose();
Two units on one live chart (secondary Y-axis)
A live panel usually shows two values that do not share a scale, such as a rate and a volume, or a count and a size. Stream one value on each axis and name both: in a legend, as here, or at the end of each line (see Direct labels and axis colours).
StreamingLineSeries? delivered = null;
StreamingLineSeries? traffic = null;
var sf = Plt.Create()
.WithTitle("Ops performance")
.AddSubPlot(1, 1, 1, ax =>
{
delivered = ax.StreamingPlot(600, s => s.Label = "Delivered (msg/s)");
ax.SetYLabel("messages / sec")
.WithSecondaryYAxis(right =>
{
traffic = right.StreamingPlot(600, s => s.Label = "Traffic (kB/s)");
right.SetYLabel("kB / sec");
})
.WithLegend();
})
.BuildStreaming(TimeSpan.FromSeconds(1)); // one frame per second
delivered!.AppendPoint(t, msgPerSecond);
traffic!.AppendPoint(t, kbPerSecond);
SecondaryAxisBuilder.StreamingPlotreturns the series, not the builder. You need that series object to append the live data. The figure makes room on the right for the right-hand axis's tick labels and title by itself, with or withoutTightLayout().
Streaming candlestick with Bollinger Bands
This example builds a live financial chart. The indicators attach to the candlestick series automatically:
var figure = new Figure();
var axes = figure.AddSubPlot();
var candles = axes.AddSeries(new StreamingCandlestickSeries(5000));
// Indicators auto-subscribe to BarAppended — O(1) per bar
candles.WithStreamingSma(axes, 20);
candles.WithStreamingBollinger(axes, 20, 2.0);
var sf = new StreamingFigure(figure);
sf.DefaultConfig = new StreamingAxesConfig(
new AxisScaleMode.SlidingWindow(100),
new AxisScaleMode.AutoScale());
// Feed price data
var rng = new Random(42);
double price = 100;
for (int i = 0; i < 200; i++)
{
double change = (rng.NextDouble() - 0.48) * 3;
candles.AppendBar(new OhlcBar(price, price + 5, price - 3, price + change));
price += change;
}
Streaming signal (oscilloscope)
This series stores only the Y values and computes X from the sample rate. It is the best fit for audio and sensor data:
var signal = ax.StreamingSignal(
capacity: 100_000,
sampleRate: 44100.0,
configure: s => { s.Color = Colors.Green; });
// Append individual samples or batches
signal.AppendSample(amplitude);
signal.AppendSamples(audioBuffer);
Axis scale modes
// Sliding window: always show last 100 X-units
sf.DefaultConfig = new StreamingAxesConfig(
new AxisScaleMode.SlidingWindow(100.0),
new AxisScaleMode.AutoScale());
// Sticky right: scroll only when data is at the edge
sf.DefaultConfig = new StreamingAxesConfig(
new AxisScaleMode.StickyRight(100.0),
new AxisScaleMode.AutoScale());
// Fixed: manual control, no auto-scaling
sf.DefaultConfig = new StreamingAxesConfig(
new AxisScaleMode.Fixed(),
new AxisScaleMode.Fixed());
Putting it on screen in a desktop application
Each native host has a control that takes the StreamingFigure itself. Bind it once and leave it there: the
control subscribes to the figure, marshals the redraw to the UI thread, and scales the axes before each draw.
<!-- WPF -->
<wpf:MplStreamingChartControl StreamingFigure="{Binding LiveChart}" />
<!-- Avalonia -->
<avalonia:MplStreamingChartControl StreamingFigure="{Binding LiveChart}" />
<!-- Uno -->
<uno:MplStreamingChartElement StreamingFigure="{Binding LiveChart}" />
Reach for these rather than the ordinary chart control. MplChartControl takes a Figure and redraws when
that property changes — which, for a dependency property, means when the reference changes. A streaming
figure keeps its identity while its data moves, so the ordinary control shows the first frame and nothing
after it unless you hand it a different figure.
How fast it redraws is the figure's decision, not the control's:
sf.MinRenderInterval = TimeSpan.FromMilliseconds(100); // ten frames a second instead of thirty
Rx integration
You can connect IObservable<T> sources without taking a dependency on System.Reactive:
IObservable<StreamingPoint> sensorStream = ...;
using var sub = series.SubscribeTo(sensorStream);