MatPlotLibNet.AspNetCore
1.2.2
dotnet add package MatPlotLibNet.AspNetCore --version 1.2.2
NuGet\Install-Package MatPlotLibNet.AspNetCore -Version 1.2.2
<PackageReference Include="MatPlotLibNet.AspNetCore" Version="1.2.2" />
<PackageVersion Include="MatPlotLibNet.AspNetCore" Version="1.2.2" />
<PackageReference Include="MatPlotLibNet.AspNetCore" />
paket add MatPlotLibNet.AspNetCore --version 1.2.2
#r "nuget: MatPlotLibNet.AspNetCore, 1.2.2"
#:package MatPlotLibNet.AspNetCore@1.2.2
#addin nuget:?package=MatPlotLibNet.AspNetCore&version=1.2.2
#tool nuget:?package=MatPlotLibNet.AspNetCore&version=1.2.2
MatPlotLibNet.AspNetCore
ASP.NET Core integration for the MatPlotLibNet charting library. Serves chart JSON specs and SVG output via minimal API endpoints, server-pushes real-time updates via SignalR, and as of v1.2.0 accepts bidirectional interaction events — wheel-zoom, drag-pan, reset, and legend-toggle round-trip from the browser through ChartHub to a server-authoritative Figure that is mutated and re-published automatically.
Installation
dotnet add package MatPlotLibNet.AspNetCore
Quick Start — static endpoints + one-way push
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMatPlotLibNetSignalR();
var app = builder.Build();
app.MapChartEndpoint(); // GET /chart?id=... -> JSON
app.MapChartSvgEndpoint(); // GET /chart/svg?id=... -> SVG
app.MapChartHub(); // SignalR hub at /charts-hub
app.Run();
Publish a one-way update to every subscribed client:
app.MapPost("/update", async (IChartPublisher publisher) =>
{
var figure = Plt.Create()
.WithTitle("Live Data")
.Plot(x, y)
.Build();
await publisher.PublishSvgAsync("dashboard-1", figure);
});
Bidirectional SignalR (v1.2.0)
The server can now receive zoom / pan / reset / legend-toggle events from a connected browser, mutate the authoritative Figure, and push the updated SVG back through the existing publish pipeline. This closes the loop that one-way push leaves open: axis limits stay in sync with the viewer, LTTB downsampling can react to the current zoom level, toggled series survive reloads via the server model.
using MatPlotLibNet.AspNetCore;
using MatPlotLibNet.Interaction;
// 1. Build a figure that opts into server-authoritative interaction.
var figure = Plt.Create()
.WithTitle("Bidirectional demo")
.Plot(xs, ys)
.WithServerInteraction("live-1", i => i.All()) // Zoom + Pan + Reset + LegendToggle
.Build();
figure.SubPlots[0].XAxis.Min = xs[0];
figure.SubPlots[0].XAxis.Max = xs[^1];
figure.SubPlots[0].YAxis.Min = yMin;
figure.SubPlots[0].YAxis.Max = yMax;
// 2. Register the figure with the per-chart channel-based pub/sub pipeline.
var registry = app.Services.GetRequiredService<FigureRegistry>();
registry.Register("live-1", figure);
// 3. Serve the initial SVG.
app.MapGet("/api/chart/live.svg", (ISvgRenderer svg) =>
Results.Content(svg.Render(figure), "image/svg+xml"));
app.MapChartHub();
How the round-trip works
- The browser loads the initial SVG. Because
ServerInteraction = true, the SVG embedsSvgSignalRInteractionScript— a single IIFE that wireswheel/pointerdown/keydown/clicklisteners. - The script discovers the JS-side
HubConnectionviawindow.__mpl_signalr_connection(set by the host page) and invokesOnZoom/OnPan/OnReset/OnLegendTogglewith a payload carrying the new axis limits or toggled series index. ChartHubwrites the event to the per-chartChannel<FigureInteractionEvent>viaFigureRegistry.Publishand returns — the hub method never blocks on rendering.- A single background reader task per chart (
ChartSession) drains the channel in order, applies each event viaFigureInteractionEvent.ApplyTo(figure), and callsIChartPublisher.PublishSvgAsynconce per drained batch. Bursts coalesce naturally — 50 wheel events over one frame produce exactly one re-render. PublishSvgAsyncfans out the new SVG to every subscriber of the chart's SignalR group, so multi-viewer sync falls out as a side-effect of the existing group machinery.
Event hierarchy (MatPlotLibNet.Interaction in the core package)
Stacked records, self-applying, SOLID-OCP — add a new interaction by adding a new subclass:
FigureInteractionEvent (abstract root — ChartId, AxesIndex, abstract ApplyTo)
├── AxisRangeEvent (abstract tier-2 — sealed ApplyTo overwrites X/Y limits)
│ ├── ZoomEvent (wheel — new absolute limits)
│ └── ResetEvent (Home key — original limits captured at render time)
├── PanEvent (drag — delta translation of current limits)
└── LegendToggleEvent (click on data-series-index — flips ChartSeries.Visible)
FigureBuilder.WithServerInteraction
.WithServerInteraction("chart-id", i => i
.EnableZoom()
.EnablePan()
.EnableReset()
.EnableLegendToggle())
// or:
.WithServerInteraction("chart-id", i => i.All())
Opting in sets Figure.ChartId and Figure.ServerInteraction = true, flips the matching existing EnableZoomPan / EnableLegendToggle flags, and makes SvgTransform emit SvgSignalRInteractionScript instead of the local SvgInteractivityScript + SvgLegendToggleScript — the two scripts are mutually exclusive, never both.
Frontend hookup
Any environment that can create a @microsoft/signalr HubConnection will work. The script inside the SVG looks up window.__mpl_signalr_connection when the first event fires, so the host page only has to expose the connection globally once:
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.0/signalr.min.js"></script>
<script>
const conn = new signalR.HubConnectionBuilder()
.withUrl('/charts-hub')
.withAutomaticReconnect()
.build();
window.__mpl_signalr_connection = conn;
conn.on('UpdateChartSvg', (id, svg) => {
if (id === 'live-1') document.getElementById('chart-host').innerHTML = svg;
});
conn.start().then(() => conn.invoke('Subscribe', 'live-1'));
</script>
For Blazor, see MplLiveChart in MatPlotLibNet.Blazor and the Samples/MatPlotLibNet.Samples.Blazor/Components/Pages/Interactive.razor example.
API reference
| Type | Description |
|---|---|
SignalRExtensions.AddMatPlotLibNetSignalR() |
Registers SignalR + renderer + IChartPublisher + FigureRegistry |
SignalRExtensions.MapChartHub() |
Maps the ChartHub SignalR endpoint |
MatPlotLibNetEndpoints.MapChartEndpoint() |
Maps a JSON chart endpoint |
MatPlotLibNetEndpoints.MapChartSvgEndpoint() |
Maps an SVG chart endpoint |
IChartPublisher |
Service for broadcasting chart updates (PublishAsync, PublishSvgAsync) |
ChartHub |
SignalR hub: Subscribe / Unsubscribe / OnZoom / OnPan / OnReset / OnLegendToggle |
FigureRegistry |
Per-chart registry + channel-based pub/sub: Register / UnregisterAsync / Publish |
FigureBuilder.WithServerInteraction |
Fluent opt-in to bidirectional interaction |
ServerInteractionBuilder |
Small fluent selector: EnableZoom/EnablePan/EnableReset/EnableLegendToggle/All |
Samples
Samples/MatPlotLibNet.Samples.AspNetCore— minimal ASP.NET Core + static HTML page demonstrating the full bidirectional loop without any frontend framework. Run withdotnet run, open the browser, wheel-zoom the chart.Samples/MatPlotLibNet.Samples.Blazor/Components/Pages/Interactive.razor— Blazor equivalent at route/interactive.
License
MIT — Copyright (c) 2026 H.P. Gansevoort
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- MatPlotLibNet (>= 1.2.2)
-
net8.0
- MatPlotLibNet (>= 1.2.2)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on MatPlotLibNet.AspNetCore:
| Package | Downloads |
|---|---|
|
MatPlotLibNet.Interactive
Interactive display for MatPlotLibNet. Show charts in a browser popup with live SignalR updates. |
|
|
MatPlotLibNet.GraphQL
GraphQL integration for MatPlotLibNet using HotChocolate. Queries and subscriptions for real-time chart updates. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.2 | 0 | 4/15/2026 |
| 1.2.1 | 0 | 4/15/2026 |
| 1.2.0 | 0 | 4/15/2026 |
| 1.1.4 | 3 | 4/15/2026 |
| 1.1.3 | 66 | 4/13/2026 |
| 1.1.1 | 58 | 4/12/2026 |
| 1.1.0 | 53 | 4/12/2026 |
| 1.0.2 | 53 | 4/12/2026 |
| 1.0.1 | 54 | 4/12/2026 |
| 1.0.0 | 53 | 4/12/2026 |
| 0.9.1 | 59 | 4/12/2026 |
| 0.9.0 | 60 | 4/11/2026 |
| 0.8.9 | 62 | 4/11/2026 |
| 0.8.8 | 55 | 4/11/2026 |
| 0.8.7 | 55 | 4/11/2026 |
| 0.8.6 | 63 | 4/11/2026 |
| 0.8.5 | 51 | 4/11/2026 |
| 0.8.4 | 56 | 4/11/2026 |
| 0.8.3 | 62 | 4/11/2026 |
| 0.8.2 | 54 | 4/11/2026 |