nbimplot
Jupyter-native, ImPlot-powered plotting for very large arrays.
nbimplot is built around three constraints:
- notebook cell rendering only (no native windows, no side process)
- binary transfer (
numpy -> bytes -> wasm heap) - WASM-owned interaction, state, and LOD
The runtime is strict: ImPlot + WASM + WebGL2 are required.
Project note: nbimplot was vibe coded with Codex, then
hardened with tests, packaging checks, and runtime validation.
nbimplot now has two surfaces:
nbimplot: Python/Jupyter notebook package@nbimplot/web: standalone browser package for web apps
Public web demo:
AI/agent-readable docs:
Upstream Libraries
nbimplot is built on top of these upstream projects:
- Dear ImGui: https://github.com/ocornut/imgui
- ImPlot: https://github.com/epezent/implot
Thanks
Special thanks to the ImPlot and Dear ImGui maintainers and contributors for
building and maintaining the core libraries that make nbimplot possible.
Install
python -m pip install -U nbimplot
Minimum recommended widget/runtime stack:
python -m pip install -U "nbimplot>=0.1.10" "anywidget>=0.9.21" ipywidgets jupyterlab_widgets
Compatibility
- Python
>=3.10 - Jupyter widget stack:
anywidget,ipywidgets,traitlets - Frontend: JupyterLab/Notebook with widget manager enabled
- Browser/GPU: WebGL2 required
- Runtime mode: strict WASM + ImPlot only (no JS renderer fallback)
Quick Start
import numpy as np
import nbimplot as ip
x = np.linspace(0, 100, 1_000_000, dtype=np.float32)
y = np.sin(x)
p = ip.Plot(width=900, height=450, title="Signal")
h = p.line("mid", y, x=x)
p.show()
# Update in place, then redraw
h.set_data((0.8 * y).astype(np.float32), x=x)
p.render()
Direct Web App Usage
Use @nbimplot/web when you want ImPlot/WASM rendering in a normal browser
application without Jupyter:
npm install /path/to/nbimplot/packages/web
After publishing the web package to npm:
npm install @nbimplot/web
import { createPlot } from "@nbimplot/web";
const plot = await createPlot("#plot", {
width: 900,
height: 450,
title: "Signal",
});
const x = new Float32Array(1_000_000);
const y = new Float32Array(x.length);
for (let i = 0; i < y.length; i += 1) {
x[i] = i * 0.001;
y[i] = Math.sin(x[i]);
}
const h = plot.line("mid", y, { x });
plot.render();
h.setData(y, { x });
plot.dispose();
The web package lives in packages/web. See docs/WEB.md and
packages/web/examples/plain/index.html.
The repository also includes a full Next.js examples gallery that can run locally or as a static GitHub Pages site:
npm install
npm run dev -- --hostname 0.0.0.0 --port 3001
The gallery lazy-loads examples as their cards approach the viewport and releases offscreen canvases to keep active WebGL contexts bounded.
For GitHub Pages, the workflow in .github/workflows/pages.yml builds with:
NEXT_PUBLIC_BASE_PATH=/nbimplot npm run build
GitHub Pages should be configured once as:
- Source:
Deploy from a branch - Branch:
gh-pages - Folder:
/root
Interaction Defaults
- initial X/Y view auto-fits to available data
- double-click inside plot area resets view (
autoscale) - right-drag box zoom, wheel zoom, drag pan, legend toggle
Core API
import numpy as np
import nbimplot as ip
x = np.linspace(0, 10_000, 200_000, dtype=np.float32)
y = np.random.randn(x.size).astype(np.float32).cumsum()
p = ip.Plot(width=1000, height=420, title="Core API")
h = p.line("price", y, x=x, color="#22c55e", line_weight=2.0, marker="none")
p.set_plot_flags(no_legend=False, no_menus=False, no_box_select=False)
p.set_colormap("Viridis")
p.show()
# Later update
h.set_data((y * 1.01).astype(np.float32), x=x)
p.render()
Explicit X Data
Line plots accept explicit x coordinates on both public surfaces:
h = p.line("signal", y, x=x)
h.set_data(y_new, x=x_new)
const h = plot.line("signal", y, { x });
h.setData(yNew, { x: xNew });
Rules:
xandymust be 1D, finite, and equal length.xmust be sorted in non-decreasing order so the WASM LOD path can binary-search the visible range.- If a custom-x line keeps the same length,
h.set_data(y_new)/h.setData(yNew)preserves the existing x buffer. x_axis/xAxisselects the ImPlot axis slot (x1,x2,x3); it is not the x-data argument.- Streaming append uses implicit sample indices. For custom x streaming, update with
set_data(y, x=x)/setData(y, { x }).
Common Examples
1) Line + Streaming
import numpy as np
import nbimplot as ip
p = ip.Plot(width=1000, height=380, title="Streaming")
h = p.stream_line("ticks", capacity=200_000, initial=np.zeros(1000, dtype=np.float32))
p.show()
chunk = np.random.randn(20_000).astype(np.float32)
h.append(chunk)
p.render()
2) Scatter / Bars / Histogram
import numpy as np
import nbimplot as ip
rng = np.random.default_rng(7)
x = rng.normal(0, 1, 4000).astype(np.float32)
y = (0.5 * x + 0.2 * rng.normal(size=x.size)).astype(np.float32)
p = ip.Plot(width=1100, height=420, title="Stat Plots")
p.scatter("cloud", y, x=x, size=2.0)
p.vlines("cuts", np.array([-1.0, 1.0], dtype=np.float32))
p.hlines("zero", np.array([0.0], dtype=np.float32))
p.show()
p2 = ip.Plot(width=1100, height=360, title="Histogram")
p2.histogram("x-dist", x, bins=60)
p2.show()
3) Heatmap / Histogram2D / Image
import numpy as np
import nbimplot as ip
rng = np.random.default_rng(0)
z = rng.normal(size=(50, 80)).astype(np.float32)
p = ip.Plot(width=1100, height=420, title="Heatmap")
p.set_colormap("Plasma")
p.heatmap(
"z",
z,
label_fmt="", # empty format disables cell text
show_colorbar=True,
colorbar_label="Intensity",
colorbar_format="%.3f",
)
p.show()
x = rng.normal(size=200_000).astype(np.float32)
y = (0.3 * x + rng.normal(size=x.size)).astype(np.float32)
p2 = ip.Plot(width=1100, height=420, title="Histogram2D")
p2.histogram2d(
"h2d",
x,
y,
x_bins=100,
y_bins=80,
label_fmt="",
show_colorbar=True,
colorbar_label="Count",
)
p2.show()
4) Subplots
import numpy as np
import nbimplot as ip
sp = ip.Subplots(
2,
2,
title="Dashboard",
width=1100,
height=760,
link_rows=True,
link_cols=True,
share_items=True,
)
t = np.linspace(0, 30, 4000, dtype=np.float32)
sp.subplot(0, 0).line("sin", np.sin(t))
sp.subplot(0, 1).scatter("noise", np.random.randn(3000).astype(np.float32))
sp.subplot(1, 0).bars("bars", np.abs(np.random.randn(120)).astype(np.float32))
sp.subplot(1, 1).histogram("hist", np.random.randn(20_000).astype(np.float32), bins=50)
sp.show()
Plot and Primitive Coverage
Implemented plot/primitive APIs include:
line,stream_linescatter,bubbles,stairs,stems,digitalbars,bar_groups,bars_h,shadederror_bars,error_bars_hinf_lines,vlines,hlineshistogram,histogram2d,heatmap,image,pie_charttext,annotation,dummytag_x,tag_y,colormap_slider,colormap_button,colormap_selectordrag_line_x,drag_line_y,drag_point,drag_rectdrag_drop_plot,drag_drop_axis,drag_drop_legend
For a broader cookbook, see docs/EXAMPLES.md.
Search-focused guides:
docs/FAST_JUPYTER_PLOTTING.mddocs/MILLION_POINT_NOTEBOOK_PLOTTING.mddocs/WEBAPP_INTEGRATION.mddocs/POSITIONING.md
View, Axes, and Performance Controls
p.set_view(x_min, x_max, y_min, y_max)p.autoscale()p.set_axis_scale(x="linear|log", y="linear|log")p.set_axis_state("x2|x3|y2|y3", enabled=True|False, scale="linear|log|time")p.set_secondary_axes(x2=..., x3=..., y2=..., y3=...)p.set_time_axis("x1|x2|x3|y1|y2|y3")p.set_axis_label(...),p.set_axis_format(...)p.set_axis_ticks(...),p.clear_axis_ticks(...)p.set_axis_limits_constraints(...),p.set_axis_zoom_constraints(...),p.set_axis_link(...)p.hide_next_item()p.on_perf_stats(callback, interval_ms=500)p.on_tool_change(callback)p.on_selection_change(callback)
Example Notebooks
notebooks/nbimplot_examples.ipynbnotebooks/nbimplot_api_gallery.ipynbnotebooks/nbimplot_benchmarks.ipynbnotebooks/nbimplot_colab_complete.ipynb(Colab-ready complete examples)
Open the Colab notebook directly:
Troubleshooting
Failed to load model class 'AnyModel' from module 'anywidget'
This is usually a server-kernel env mismatch or stale lab assets.
python -m pip install -U "nbimplot>=0.1.10" "anywidget>=0.9.21" ipywidgets jupyterlab_widgets
jupyter lab clean
Then restart the full JupyterLab server.
Quick verification:
import nbimplot as ip, anywidget, sys
print("python:", sys.executable)
print("anywidget:", anywidget.__version__)
print("has Plot:", hasattr(ip, "Plot"))
Unable to enable ImPlot in the WASM core or WebGL context errors
Strict mode requires WebGL2.
!!document.createElement("canvas").getContext("webgl2")
If this is false, run from a local desktop browser session with GPU
acceleration enabled.
Known Limitations
- Rendering requires WebGL2-capable browser/runtime.
- Strict mode is enforced; non-ImPlot or non-WASM fallback is disabled.
- Headless/browser-restricted environments may fail to create GL context.
Build WASM Core
Prerequisites:
- Emscripten SDK (
emcmake,emcc) - CMake >= 3.20
- Dear ImGui sources (
imgui): https://github.com/ocornut/imgui - ImPlot sources (
implot): https://github.com/epezent/implot
Build:
scripts/build_wasm.sh
Expected outputs in nbimplot/wasm/:
nbimplot_wasm.jsnbimplot_wasm.wasm
Build explicitly with local ImGui/ImPlot sources:
NBIMPLOT_WITH_IMPLOT=ON \
NBIMPLOT_IMGUI_DIR=/path/to/imgui \
NBIMPLOT_IMPLOT_DIR=/path/to/implot \
scripts/build_wasm.sh
If you use vendored deps:
NBIMPLOT_WITH_IMPLOT=ON scripts/build_wasm.sh
Performance Model
- raw path when visible points
<= 3 * pixel_width - min/max LOD when visible points
> 3 * pixel_width - LOD computed in WASM, complexity scales with screen pixels
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nbimplot-0.1.10.tar.gz.
File metadata
- Download URL: nbimplot-0.1.10.tar.gz
- Upload date:
- Size: 398.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6f33c5d3e20cc1d519d58268486960752d86dd41a68a9e7ad8dc3e2c9b0a9779
|
|
| MD5 |
074afc7bda717d964ab009e4e6113af2
|
|
| BLAKE2b-256 |
12e77969f5f29039084ef716170a7dfbb22b21320cea046608dd560dc2dbef52
|
File details
Details for the file nbimplot-0.1.10-py3-none-any.whl.
File metadata
- Download URL: nbimplot-0.1.10-py3-none-any.whl
- Upload date:
- Size: 399.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3be0ac590bfe7d81ce606626c629b2e1e54216882771811e2c9f8720a9caab22
|
|
| MD5 |
752d8837fe9a3e76e1fb27da7e67b1b1
|
|
| BLAKE2b-256 |
49cfb3f5b2f6cba97935e08d0bcbceb71809ed1084f5fbe7f209412ffc62ce8f
|