Jupyter-native plotting, powered by ImPlot

WASM plots for serious notebook data.

nbimplot gives notebooks and browser apps the same interaction model: binary arrays into a strict ImGui + ImPlot WASM core, rendered on a canvas with pixel-bounded LOD for large time series.

Notebook output cell onlyStrict WASM + ImPlot renderingBinary array transportLOD inside the core
Live WASM SurfaceImPlot canvas inside the page
drag pan / wheel zoom / double-click fit

Minimal API, no global state

One plotting model for notebooks and web apps.

The public surface is intentionally small: create a plot, attach typed arrays, update handles in place, and let the WASM core manage interaction, state, LOD, subplots, and export.

Pythonnotebooks
pip install nbimplot

import nbimplot as ip
p = ip.Plot(width=900, height=450, title="Signal")
h = p.line("mid", df, x="time", y="mid")
h.set_data(df_next, x="time", y="mid")
p.show()
TypeScriptweb apps
npm install @nbimplot/web

import { createPlot } from "@nbimplot/web";
const plot = await createPlot(host, { title: "Signal" });
const h = plot.line("mid", y, { x: t });
h.setData(yNext, { x: tNext });
plot.render();
10Mline-point workflow target
O(px)pan and zoom work per frame
0JSON point arrays on the hot path
Strict WASMRuntime
ImPlot + WebGL2Renderer
Typed arraysTransport
Min/max bucketsLOD
initializingruntime status
-- mslast frame

Why this architecture

Designed around the expensive parts of plotting.

The Python layer validates and transfers buffers. The browser view owns the canvas lifecycle. The WASM core owns plot state, LOD, and ImPlot rendering.

01

Binary data path

NumPy and typed arrays move as buffers. The Python and browser APIs avoid JSON point lists for series data.

02

ImPlot interaction model

Pan, zoom, axis menus, legends, selection, and hover behavior come from the ImGui + ImPlot WASM runtime.

03

Pixel-bounded rendering

Large time series switch to min/max LOD so interaction cost tracks screen resolution, not raw array size.

Live API surface

Operate the examples from one control panel.

These buttons load the target canvas, scroll it into view, and call the same public APIs available in notebooks and web apps.

Pick an action. The app will load the matching example and report the result here.

Global controlsLoaded plots

Interaction checklist

Examples lazy-load near the viewport and offscreen canvases are released to keep WebGL contexts bounded. Once loaded, left-drag pans, wheel zooms, right-click opens ImPlot menus, right-drag box-select/box-zoom follows ImPlot behavior, and double-click autofits.

Documentation

Guides for users, agents, and web integrations.

Direct resources for fast notebook plotting, million-point visualization, web app integration, and LLM/search positioning.

Examples gallery

Coverage across ImPlot-style primitives.

Each canvas is an independent WASM session. Scroll to lazy-load examples and verify lifecycle cleanup, interactions, subplots, colormaps, and exports. Every card shows the equivalent Python notebook API and direct web API.

PerformanceData InputPointsCurvesCategoricalStatisticsMatricesOverlaysAxesLayoutInteractionColormapsSpecialtyAdvanced API
01

Million Point Line + Custom X + LOD

Explicit x/y buffers, WASM min/max LOD, and callback-driven hover/click/selection inspection.

Python notebooknbimplot
p = ip.Plot(width=1000, height=420, title="Line + LOD")
h = p.line("signal", df, x="time", y="signal")
p.on_hover(lambda plot, e: print(e["index"], e["x"], e["y"]))
p.on_select(lambda plot, e: plot.indices_for_selection(e, h))
p
Web app@nbimplot/web
const h = plot.line("signal", y, { x });
plot.onHover(console.log);
plot.onSelection((e) => plot.indicesForSelection(e, h));
Million Point Line + Custom X + LODWASM
02

Realtime Streaming Ring Buffer

Append explicit x/y chunks into a fixed-capacity line without recreating the plot object.

Python notebooknbimplot
p = ip.Plot(width=1000, height=360, title="Realtime")
h = p.stream_line("ticks", capacity=12000, initial=y0, initial_x=x0, auto_render=True)
h.append(chunk, x=chunk_x)
h.pause(); h.resume()
p
Web app@nbimplot/web
const h = plot.streamLine("ticks", { capacity: 12000, x: initialX });
h.append(chunk, { x: chunkX });
h.pause(); h.resume();
Realtime Streaming Ring BufferWASM
03

Batch Lines + Datetime + Categories + HTML Export

Multi-series upload, automatic time/category x normalization, C++ theme presets, and standalone HTML state export.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Batch + Axes")
p.set_theme("publication")
handles = p.lines({"mid": {"x": ts, "y": mid}, "vwap": {"x": ts, "y": vwap}})
p.scatter("scores", scores, x=["A", "B", "C"])
html = p.export_html(title="snapshot")
p
Web app@nbimplot/web
plot.setTheme("publication");
const handles = plot.lines({ mid: { x: dates, y: mid }, vwap: { x: dates, y: vwap } });
plot.scatter("scores", scores, { x: ["A", "B", "C"] });
const html = plot.exportHTML({ title: "snapshot" });
Batch Lines + Datetime + Categories + HTML ExportWASM
04

Scatter + Bubble Encodings

Point-cloud rendering with explicit x/y data and bubble-size encodings for dense browser workflows.

Python notebooknbimplot
p = ip.Plot(width=1000, height=420, title="Scatter + Bubbles")
p.scatter("samples", df, x="x", y="y", size=2.5)
p.bubbles("volume", df, x="x", y="y", sizes="volume")
p
Web app@nbimplot/web
plot.scatter("samples", y, { x });
plot.bubbles("volume", y, sizes, { x });
Scatter + Bubble EncodingsWASM
05

Stairs, Stems, Digital, Shaded, Error Bars

Signal-analysis overlays in one canvas: stepped series, impulses, bands, digital states, and uncertainty.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Signal Overlays")
p.stairs("step", y, x=x)
p.stems("stem", impulses, x=x)
p.digital("state", states, x=x)
p.shaded("band", lower, upper, x=x)
p.error_bars("fit", fit, err=err, x=x)
p
Web app@nbimplot/web
plot.stairs("step", y, { x });
plot.shaded("band", lower, upper, { x });
plot.errorBars("fit", y, { x, err });
Stairs, Stems, Digital, Shaded, Error BarsWASM
06

Bars, Grouped Bars, Horizontal Bars

Vertical bars, grouped categories, and horizontal rankings across ImPlot subplots.

Python notebooknbimplot
sp = ip.Subplots(1, 3, width=1100, height=360, title="Bars")
sp.subplot(0, 0).bars("sales", values)
sp.subplot(0, 1).bar_groups(["A", "B", "C"], matrix)
sp.subplot(0, 2).bars_h("rank", values)
sp
Web app@nbimplot/web
plot.setSubplots(1, 3);
plot.bars("sales", values);
plot.barGroups(labels, matrix);
plot.barsH("rank", values);
Bars, Grouped Bars, Horizontal BarsWASM
07

Histogram + 2D Histogram

1D and 2D distributions with colorbar-backed density inspection.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Distributions")
p.histogram("returns", df, y="returns", bins=80)
p.histogram2d("density", df, x="x", y="y", x_bins=80, y_bins=60, show_colorbar=True)
p
Web app@nbimplot/web
plot.histogram("returns", values, { bins: 80 });
plot.histogram2d("density", x, y, { xBins: 80, yBins: 60 });
Histogram + 2D HistogramWASM
08

Heatmap + Image

Matrix and image plotting with empty heatmap labels, colorbar formatting, and float RGB buffers.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Heatmap + Image")
p.set_colormap("Viridis")
p.heatmap("z", matrix, label_fmt="", show_colorbar=True, colorbar_format="%.2f")
p.image("rgb", image, bounds=((0, 0), (cols, rows)))
p
Web app@nbimplot/web
plot.setColormap("Viridis");
plot.heatmap("z", matrix, { rows, cols, labelFmt: "" });
plot.image("rgb", image, { rows, cols, channels: 3 });
Heatmap + ImageWASM
09

Annotations, Tags, Text, Infinite Lines, Pie

Thresholds, callouts, labels, tags, and pie-chart composition using ImPlot primitives.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Overlays")
p.vlines("events", xs)
p.hlines("limits", ys)
p.tag_y(0.0, label_fmt="zero")
p.annotation("peak", x0, y0)
p.pie_chart("mix", values, labels=labels, x=8, y=0, radius=1)
p
Web app@nbimplot/web
plot.vlines("events", xs);
plot.tagY(0, { labelFmt: "zero" });
plot.annotation("peak", x, y);
plot.pieChart("mix", values, { labels });
Annotations, Tags, Text, Infinite Lines, PieWASM
10

Axis Labels, Formats, Ticks, Log Scale, Secondary Axis

Secondary axes, custom ticks, numeric formats, linked axes, and log/time scale controls.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Axes")
p.set_secondary_axes(y2=True)
p.set_axis_scale(x="linear", y="log")
p.set_axis_label("x1", "time")
p.set_axis_format("y1", "%.2e")
p.set_axis_ticks("x1", ticks, labels=labels)
p.line("primary", y, x=x)
p.line("secondary", y2, x=x, y_axis="y2")
p
Web app@nbimplot/web
plot.setSecondaryAxes({ y2: true });
plot.setAxisScale({ x: "linear", y: "log" });
plot.setAxisTicks("x1", ticks, { labels });
Axis Labels, Formats, Ticks, Log Scale, Secondary AxisWASM
11

Linked Subplots + Crosshair

A 2x2 ImPlot subplot grid with linked x-axis behavior and crosshair synchronization.

Python notebooknbimplot
sp = ip.Subplots(2, 2, link_all_x=True, width=1100, height=650, title="Linked")
sp.set_linked_crosshair("desk", axis="x")
sp.subplot(0, 0).line("sin", y0, x=x)
sp.subplot(0, 1).line("cos", y1, x=x)
sp.subplot(1, 0).scatter("noise", y2, x=x)
sp
Web app@nbimplot/web
plot.setSubplots(2, 2, { linkAllX: true });
plot.setLinkedCrosshair("desk", { axis: "x" });
plot.line("a", y, { x, subplotIndex: 0 });
Linked Subplots + CrosshairWASM
12

Drag Lines, Drag Point, Drag Rect, Drag/Drop Targets

Interactive ImPlot primitives for draggable guides, anchors, rectangles, and drop targets.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Drag Tools")
p.drag_line_x("cursor", 40)
p.drag_line_y("threshold", 0.5)
p.drag_point("anchor", 25, 0.5)
p.drag_rect("roi", 10, -1, 20, 1)
p.on_tool_change(lambda plot, event: print(event))
p
Web app@nbimplot/web
plot.dragLineX("cursor", 40);
plot.dragPoint("anchor", 25, 0.5);
plot.onInteraction(events => ...);
Drag Lines, Drag Point, Drag Rect, Drag/Drop TargetsWASM

Interaction events: move a drag primitive.

13

Colormap Widgets + Runtime Switching

Selector, slider, and color button widgets that update heatmaps and colorbar primitives at runtime.

Python notebooknbimplot
p = ip.Plot(width=1000, height=420, title="Colormaps")
p.set_colormap("Plasma")
p.heatmap("z", matrix, label_fmt="", show_colorbar=True)
p.colormap_selector(label="Choose map")
p.colormap_slider(label="Sample")
p.colormap_button(label="Active")
p
Web app@nbimplot/web
plot.setColormap("Plasma");
plot.colormapSelector({ label: "Choose map" });
plot.colormapSlider({ label: "Sample" });
Colormap Widgets + Runtime SwitchingWASM
14

Candlestick + OHLC

Financial primitives rendered in the ImPlot/WASM layer with hover inspection and autoscale support.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Finance")
p.set_theme("finance")
p.candlestick("candles", x=x, open=open_, high=high, low=low, close=close)
p.ohlc("ohlc", x=x, open=open_, high=high, low=low, close=close)
p
Web app@nbimplot/web
plot.setTheme("finance");
plot.candlestick("candles", open, high, low, close, { x });
plot.ohlc("ohlc", open, high, low, close, { x });
Candlestick + OHLCWASM
15

Contour + Quiver + Waterfall + Spectrogram

Scientific field and matrix workflows backed by WASM-side ImPlot primitives and draw-list integration.

Python notebooknbimplot
p = ip.Plot(width=1100, height=760, title="Scientific")
p.set_subplots_config(rows=2, cols=2)
p.contour("contour", z, levels=levels, subplot_index=0)
p.quiver("field", x, y, u, v, normalize=True, subplot_index=1)
p.waterfall("waterfall", z, subplot_index=2)
p.spectrogram("spectrogram", z, label_fmt="", show_colorbar=True, subplot_index=3)
p
Web app@nbimplot/web
plot.setSubplots(2, 2);
plot.contour("contour", z, { rows, cols, levels, subplotIndex: 0 });
plot.quiver("field", x, y, u, v, { normalize: true, subplotIndex: 1 });
plot.waterfall("waterfall", z, { rows, cols, subplotIndex: 2 });
plot.spectrogram("spectrogram", z, { rows, cols, labelFmt: "", showColorbar: true, subplotIndex: 3 });
Contour + Quiver + Waterfall + SpectrogramWASM
16

State, Selection, Export

View callbacks, state snapshots, PNG export, selection CSV, highlighting, constraints, links, and direct primitive access.

Python notebooknbimplot
p = ip.Plot(width=1100, height=420, title="Advanced")
p.set_theme("nbimplot")
h = p.line("signal", y, x=x)
state = p.get_state(include_data=True)
p.highlight_selection(selection, h)
csv = p.export_csv_selection(selection, h)
p.export_png("advanced.png")
p
Web app@nbimplot/web
plot.setTheme("nbimplot");
const state = plot.getState({ includeData: true });
plot.highlightSelection(selection, h);
const csv = plot.exportCSVSelection(selection, h);
await plot.downloadPNG("advanced.png");
State, Selection, ExportWASM