Skip to main content

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:

Upstream Libraries

nbimplot is built on top of these upstream projects:

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.9" "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:

  • x and y must be 1D, finite, and equal length.
  • x must 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 / xAxis selects 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_line
  • scatter, bubbles, stairs, stems, digital
  • bars, bar_groups, bars_h, shaded
  • error_bars, error_bars_h
  • inf_lines, vlines, hlines
  • histogram, histogram2d, heatmap, image, pie_chart
  • text, annotation, dummy
  • tag_x, tag_y, colormap_slider, colormap_button, colormap_selector
  • drag_line_x, drag_line_y, drag_point, drag_rect
  • drag_drop_plot, drag_drop_axis, drag_drop_legend

For a broader cookbook, see docs/EXAMPLES.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.ipynb
  • notebooks/nbimplot_api_gallery.ipynb
  • notebooks/nbimplot_benchmarks.ipynb
  • notebooks/nbimplot_colab_complete.ipynb (Colab-ready complete examples)

Open the Colab notebook directly:

Open In Colab

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.9" "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:

Build:

scripts/build_wasm.sh

Expected outputs in nbimplot/wasm/:

  • nbimplot_wasm.js
  • nbimplot_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

nbimplot-0.1.9.tar.gz (397.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nbimplot-0.1.9-py3-none-any.whl (397.8 kB view details)

Uploaded Python 3

File details

Details for the file nbimplot-0.1.9.tar.gz.

File metadata

  • Download URL: nbimplot-0.1.9.tar.gz
  • Upload date:
  • Size: 397.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for nbimplot-0.1.9.tar.gz
Algorithm Hash digest
SHA256 9a4b90469e8bf77e6a4e01822803a80688f9b5d8fccaf3c58528c2845a2393b5
MD5 0d77fadcdbf8e4f44d7a71b912395831
BLAKE2b-256 fb357aa958f27d19f9e4cd51bdcc6dc2975c0dd65037851799c2aabdc306bd43

See more details on using hashes here.

File details

Details for the file nbimplot-0.1.9-py3-none-any.whl.

File metadata

  • Download URL: nbimplot-0.1.9-py3-none-any.whl
  • Upload date:
  • Size: 397.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.12

File hashes

Hashes for nbimplot-0.1.9-py3-none-any.whl
Algorithm Hash digest
SHA256 3d95f9136e8137e54f93e57ed3afb970df638e5c0d7149291e9f3a9842486af1
MD5 5ebd5ac8c846f543bbcca7fa162a9ca4
BLAKE2b-256 1394e24a2c5dc5dc40ea9e4959f384dfc9b02b315f768cb7ac64427b9d755198

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.13

2 files

0.1.11

2 files

0.1.10

2 files

This release

0.1.9 This release

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page