This is a Jupyter Widget for Niivue based on anywidget. It is designed to visualize neuroimaging volumes, meshes, streamlines and connectomes and their corresponding statistical maps. This repository is designed for advanced developers to build and extend ipyniivue. The minimal installation end users is pip install ipyniivue. Most users will likely find the example jupyter notebooks the ideal introduction to the capability.
ipyniivue is a Jupyter widget for the Niivue. It enables interactive visualization of neuroimaging data directly in Jupyter environments, including volumetric images, surface meshes, streamlines, connectomes, and their associated statistical maps.
This repository is intended solely for advanced developers who want to build and extend ipyniivue. For others, the example Jupyter notebooks provides a simple installation.
For most users, the best introduction to ipyniivue’s capabilities is the collection of example Jupyter notebooks. However, the minimal installation is simply:
pip install ipyniivueSee the Documentation for usage.
ipyniivue uses the recommended hatchling build system, which is convenient to use via the hatch CLI. We recommend installing hatch globally (e.g., via pipx) and running the various commands defined within pyproject.toml. hatch will take care of creating and synchronizing a virtual environment with all dependencies defined in pyproject.toml.
These steps set up a local development environment for ipyniivue with live JavaScript rebuilding via anywidget.
- Python 3.10+
- Node.js (LTS recommended)
hatchinstalled (e.g. viapipx install hatch)- Ensure
ipyniivueis not already installed in your Python environment
Make sure hatch is available and ipyniivue is not installed:
hatch --version
pip show ipyniivue || echo "ipyniivue not installed (good)"git clone https://github.com/niivue/ipyniivue.gitcd ipyniivueFrom the repository root:
npm installhatch shellThis activates the project’s Python development environment.
pip install -e ".[dev]"This activates the project’s Python development environment.
In the same terminal:
cd js
npm run devThis runs the JavaScript build in watch mode, rebuilding the widget automatically when files change.
Leave this terminal running.
Open a second terminal tab/window, again from the ipyniivue repository root.
hatch shellpip install -e .This ensures Python picks up the locally built widget assets.
jupyter labYou can now open an example notebook and develop ipyniivue with live JavaScript updates.
- For best results with live updates, set:
before launching JupyterLab.
export ANYWIDGET_HMR=1 - If the widget does not appear or fails to load, ensure:
npm run devis still runningpip install -e .was run after starting the JS build
- If you switch branches or pull updates that change JS files, restart
npm run dev.
Run these commands from the root of the project:
| Command | Description |
|---|---|
hatch run format |
Format the project with ruff format . and apply linting with ruff --fix . |
hatch run lint |
Lint the project with ruff check . |
hatch run test |
Run unit tests with pytest |
hatch run docs |
Build docs with Sphinx |
Alternatively, you can manually create a virtual environment and manage installation and dependencies with pip:
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"This is an anywidget project, meaning the codebase is a hybrid of Python and JavaScript. The JavaScript code resides under the js/ directory and uses esbuild for bundling. Whenever you make changes to the JavaScript code, you need to rebuild the files under src/ipyniivue/static.
You have two options:
-
Build Once: Build the JavaScript code one time:
npm run build
-
Start Development Server: Start a development server that automatically rebuilds the code as you make changes:
npm run dev
We recommend this approach for a smoother development experience.
Working with Jupyter
Once the development server is running, you can start JupyterLab or Visual Studio Code to develop the widget. When you're finished, stop the development server with Ctrl+C.
Note: To have
anywidgetautomatically apply changes as you work, set the environment variableANYWIDGET_HMR=1. You can set this directly in a notebook cell:%env ANYWIDGET_HMR=1or in the shell:
export ANYWIDGET_HMR=1
Releases are automated using GitHub Actions via the release.yml workflow.
-
Commit Changes: Ensure all your changes are committed.
-
Create a Tag: Create a new tag matching the pattern
v*:git tag -a vX.X.X -m "vX.X.X" git push --follow-tags -
Workflow Actions: When triggered, the workflow will:
- Publish the package to PyPI with the tag version.
- Generate a changelog based on conventional commits.
- Create a GitHub Release with the changelog.
- We generate a changelog for GitHub releases with
antfu/changelogithub. - Each changelog entry is grouped and rendered based on conventional commits.
- It's recommended to follow the Conventional Commits specification.
ipyniivue includes tooling to generate a static HTML gallery from example notebooks.
Each notebook is:
- Executed in isolation
- Converted to static HTML
- Snapshotted using the final rendered canvas
- Added to a gallery page with thumbnails that link to the generated HTML
All generated artifacts live in a test-only output directory and are never committed.
tests-out/
html/ # generated static HTML per notebook
gallery/
thumbnails/ # canvas thumbnails
index.html # gallery page
The entire tests-out/ directory is gitignored and safe to delete at any time.
npm run galleryThis will:
- Execute notebooks found in
examples/ - Generate static HTML into
tests-out/html/ - Create thumbnails from the final rendered canvas
- Generate
tests-out/gallery/index.html
You can open the gallery locally:
open tests-out/gallery/index.htmlTo remove all generated HTML, thumbnails, and executed notebooks:
npm run clean:generated-htmlTo preview what would be removed:
npm run clean:generated-html:dryipyniivue uses Playwright to run end-to-end tests against pre-executed static HTML, rather than interacting with live Jupyter notebooks.
This approach provides:
- Deterministic results
- No UI flakiness
- No dependency on JupyterLab state
- True visual regression testing for WebGL output
The E2E pipeline consists of two steps:
- Prepare static HTML
- Run Playwright tests
npm run test:e2eThis runs:
scripts/prepare-e2e.cjs # executes notebooks → static HTML in tests-out/
playwright test # loads static HTML and runs assertions
For each notebook:
- The static HTML loads successfully
- A
<canvas>element is rendered - A WebGL context is available
- The rendered canvas matches a saved visual snapshot
Snapshots are stored under:
e2e-tests/__screenshots__/
These snapshot images should be committed, as they define the visual baseline.
When a visualization changes intentionally:
npx playwright test --update-snapshotsThis regenerates the baseline screenshots.
All generated HTML and intermediate files are written to:
tests-out/
This directory is:
- Automatically reused between runs
- Cleaned via
npm run clean:generated-html - Ignored by git
We intentionally avoid driving the JupyterLab UI in tests.
Static HTML testing provides:
- Stable WebGL output
- Faster CI
- Clear visual diffs
- No notebook state pollution
- No source-control churn
This is the recommended approach for testing interactive notebook-based visualization libraries.