Skip to main content

nbimplot

Jupyter-native, ImPlot-powered plotting for very large arrays.

PyPI npm GitHub Demo

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:

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:

  • 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.

Search-focused guides:

  • docs/FAST_JUPYTER_PLOTTING.md
  • docs/MILLION_POINT_NOTEBOOK_PLOTTING.md
  • docs/WEBAPP_INTEGRATION.md
  • docs/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.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.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:

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.10.tar.gz (398.2 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.10-py3-none-any.whl (399.0 kB view details)

Uploaded Python 3

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

Hashes for nbimplot-0.1.10.tar.gz
Algorithm Hash digest
SHA256 6f33c5d3e20cc1d519d58268486960752d86dd41a68a9e7ad8dc3e2c9b0a9779
MD5 074afc7bda717d964ab009e4e6113af2
BLAKE2b-256 12e77969f5f29039084ef716170a7dfbb22b21320cea046608dd560dc2dbef52

See more details on using hashes here.

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

Hashes for nbimplot-0.1.10-py3-none-any.whl
Algorithm Hash digest
SHA256 3be0ac590bfe7d81ce606626c629b2e1e54216882771811e2c9f8720a9caab22
MD5 752d8837fe9a3e76e1fb27da7e67b1b1
BLAKE2b-256 49cfb3f5b2f6cba97935e08d0bcbceb71809ed1084f5fbe7f209412ffc62ce8f

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.13

2 files

0.1.11

2 files

This release

0.1.10 This release

2 files

0.1.9

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