A WebGL-powered Jupyter Widget for Niivue based on anywidget
ipyniivue
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.
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 ipyniivue
Documentation
See the Documentation for usage.
Development
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.
Getting Started
These steps set up a local development environment for ipyniivue with live JavaScript rebuilding via anywidget.
Prerequisites
- Python 3.10+
- Node.js (LTS recommended)
hatchinstalled (e.g. viapipx install hatch)- Ensure
ipyniivueis not already installed in your Python environment
Step-by-step setup
0. Verify your Python environment
Make surehatch is available and ipyniivue is not installed:
hatch --version
pip show ipyniivue || echo "ipyniivue not installed (good)"
1. Clone the repository
git clone https://github.com/niivue/ipyniivue.git
2. Enter the repository
cd ipyniivue
3. Install JavaScript dependencies
From the repository root:npm install
4. Start a Hatch shell (Python environment)
hatch shell
This activates the project’s Python development environment.
5. Install Python dependencies (editable mode)
pip install -e ".[dev]"
This activates the project’s Python development environment.
6. Start the JavaScript dev server
In the same terminal:cd js
npm run dev
This runs the JavaScript build in watch mode, rebuilding the widget automatically when files change.
Leave this terminal running.
7. Open a new terminal tab
Open a second terminal tab/window, again from theipyniivue repository root.
8. Start another Hatch shell
hatch shell
9. Install ipyniivue in editable mode
pip install -e .
This ensures Python picks up the locally built widget assets.
10. Launch JupyterLab
jupyter lab
You can now open an example notebook and develop ipyniivue with live JavaScript updates.
Development tips
- For best results with live updates, set:
export ANYWIDGET_HMR=1
before launching JupyterLab.
- If the widget does not appear or fails to load, ensure:
npm run dev is still running
- pip install -e . was run after starting the JS build
- If you switch branches or pull updates that change JS files, restart
npm run dev.
Command Cheat Sheet
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]"
Making Changes to the JavaScript Code
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=1
or in the shell:
>> export ANYWIDGET_HMR=1
Release Process
Releases are automated using GitHub Actions via the release.yml workflow.
Steps to Create a New Release
- 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:
Changelog Generation
- 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.
Generating the Notebook Gallery
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
Gallery output location
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.
Generate the gallery
npm run gallery
This 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
open tests-out/gallery/index.html
Clean generated gallery artifacts
To remove all generated HTML, thumbnails, and executed notebooks:
npm run clean:generated-html
To preview what would be removed:
npm run clean:generated-html:dry
End-to-End (E2E) Testing with Playwright
ipyniivue 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
E2E testing workflow
The E2E pipeline consists of two steps:
- Prepare static HTML
- Run Playwright tests
npm run test:e2e
This runs:
scripts/prepare-e2e.cjs # executes notebooks → static HTML in tests-out/
playwright test # loads static HTML and runs assertions
What is tested?
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
e2e-tests/screenshots/
These snapshot images should be committed, as they define the visual baseline.
Updating visual snapshots
When a visualization changes intentionally:
npx playwright test --update-snapshots
This regenerates the baseline screenshots.
Generated test artifacts
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
Why static HTML testing?
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