Open .nii, .nii.gz, and .hdr volumes directly inside VS Code. The extension is optimized for both local files and VS Code Remote / SSH workflows, with fast preview-first loading, worker-based decoding, slice-on-demand fetches, and optional native acceleration.
Highlights
- Fast initial preview for large volumes
- Axial, coronal, sagittal, and 3D MIP views (grab-to-rotate virtual trackball)
- Intensity-based image registration (To MNI / Align to reference) with resolution-preserving overlay & side-by-side compare
- Remote-friendly loading through a local proxy
- Debounced full-volume loading to avoid slowdowns during rapid switching
- Canvas-first rendering for broad compatibility, with WebGL fast paths
- HTTP/1.1 local proxy with concurrent range-request slice fetching
- IndexedDB disk cache for remote file slices
- Progressive LOD loading for .nii.gz files
- GZIP random-access index for on-demand slice extraction
- Zarr v2 format support
- Optional Rust native acceleration (mmap, fast decompress)
- Window/level, zoom, pan, crosshair, orientation labels, and compare mode
Supported Files
.nii
.nii.gz
.hdr
.zarr (Zarr v2 format)
Zarr Support
NiftiSpy supports reading Zarr v2 directories for volumetric image data.
- Supported formats: Zarr v2 directories (
.zarr)
- Supported compressors: zlib/gzip, blosc, lz4
- How to open: Double-click a
.zarr directory in the VS Code file explorer
- Limitations: Read-only — no write support for Zarr volumes
Install
Marketplace
Search for NiftiSpy in the VS Code Extensions view.
Open VSX
Install from Open VSX.
From Source
git clone https://github.com/MaiwulanjiangMaiming/NiftiSpy.git
cd NiftiSpy
npm install
npm run build
Press F5 in VS Code to launch the Extension Development Host.
Optional Native Acceleration
The extension works without any native module. If you want the optional Rust fast path for hotspot parsing / extraction:
npm run build:native
This builds the Rust crate and copies the resulting binding to native/index.node.
Multi-Core Parallel Gzip Decompression (v2.2.0)
For local .nii.gz files, NiftiSpy 2.2.0 ships a multi-core parallel decompression backend built on rusty-rapidgzip (libdeflate-based). A 3-stage pipeline (speculative DEFLATE block-boundary scan → multi-core decode → ordered serialize) runs on all available CPU cores via a libuv worker thread, so the extension host stays responsive.
Benchmark on a 342MB compressed / 465MB decompressed MRI volume (Apple Silicon, 12 cores):
| Backend |
Time |
Throughput |
Speedup |
| zlib streaming (Node.js) |
~1150 ms |
~400 MB/s |
1.0× |
| libdeflate single-core (v2.1.4) |
~830 ms |
~560 MB/s |
1.4× |
| rusty-rapidgzip multi-core (v2.2.0) |
~300 ms |
~1550 MB/s |
3.8× |
The binary is keyed by platform and architecture (niftispy_native.darwin-arm64.node, niftispy_native.linux-x64-gnu.node, niftispy_native.win32-x64-msvc.node) and selected automatically at runtime. Falls back to the legacy wasm-pack build when a platform binary is absent. Byte-for-byte verified against the zlib reference.
Usage
- Open a
.nii, .nii.gz, or .hdr file.
- The custom viewer opens automatically.
- Scroll to change slices.
- Use
Ctrl + Scroll to zoom.
- Drag to pan when zoomed in.
- Click to set the crosshair.
- Use
+ Add Image for overlay or side-by-side comparison.
Controls
| Action |
Input |
| Scroll slices |
Mouse wheel |
| Zoom |
Ctrl + Scroll |
| Pan |
Drag |
| Set crosshair |
Click |
| Maximize a view |
A / C / S / M buttons |
| Auto contrast |
Auto |
| Reset view |
Reset |
Loading Pipeline
NiftiSpy uses a preview-first pipeline so large images become viewable quickly before the full volume is ready:
- The extension host opens the custom editor and sends viewer config to the webview.
- Local and remote files are normalized through
LocalFileProxy for preview extraction, slice endpoints, and caching.
- The webview shows an initial preview using direct binary slice payloads or HTTP preview fallback.
- Full-volume loading runs in workers and is debounced during rapid image switching.
- Slice requests, cached volumes, and cleanup logic reduce repeated decode and transfer cost.
For Remote-SSH .nii.gz files, version 2.3.3 builds a seekable cache on the remote host in one streaming decompression pass. Later slices read from that cache; only the requested plane crosses the tunnel. Server-side downsampling reduces interactive slice payloads, and gzip transport compresses the Float32 values losslessly. Full-resolution source volumes retain their original data.
The first preview skips uniform background planes, then advances to the center plane and complete orthogonal views. Verified center previews and coarse volumes are persisted for repeat opens. Background previews stop changing the view after the user scrolls.
niftispy.remoteCacheBudgetMB: remote disk budget, default 4096 MiB; older entries are evicted by access time. Set 0 to keep the gzip-index fallback without an unpacked cache.
niftispy.remoteCacheDirectory: optional directory on the extension host. The default is a managed directory under the host's temporary directory. Original images are never modified; read-only or space-limited hosts retain the index fallback.
Run npm run bench:host -- 256 512 to profile a synthetic 256 MiB volume through the production host implementation. Reports include useful first preview, complete orthogonal preview, warm reopen, slice bytes, compression and server timings. These local measurements exclude actual SSH latency and bandwidth.
Architecture
VS Code Extension Host
├── extension.ts
├── NiiEditorProvider.ts
├── VolumeCache.ts
└── LocalFileProxy.ts
├── /header
├── /preview
├── /preview-bin
├── /slice
└── /file
Webview
├── viewer.ts
├── worker.ts
└── nii-parser.ts
Optional Native Path
├── native/src/lib.rs
└── src/nativeBridge.ts
Configuration
The extension contributes these settings:
| Setting |
Default |
Description |
niftispy.proxyPort |
0 |
HTTP proxy port, 0 means auto-assign |
niftispy.defaultColormap |
gray |
Initial colormap |
niftispy.enableLOD |
true |
Enables preview / LOD flow for gzip volumes |
niftispy.previewMode |
binary |
Preview transport format |
niftispy.renderBackend |
auto |
Slice renderer, auto selects best available |
niftispy.fullVolumePolicy |
debounced |
When full-volume loading is triggered |
niftispy.nativeAcceleration |
auto |
Native module policy |
Development
Build
npm run build
Watch
npm run watch
Tests
npm test
Benchmarks
npm run bench:load
npm run bench:multi
npm run bench:remote
Package
npm run package
Repo Hygiene
native/target/ and native/index.node are ignored to keep the repository clean
.vscodeignore excludes development-only assets from the VSIX package
- Native acceleration is optional, so source builds remain usable without Rust
Version
For release notes, see CHANGELOG.md.
Acknowledgments
This project was built upon and inspired by the following open-source projects. We are deeply grateful for their excellent work:
ITK-SNAP — An open-source software application for segmenting structures in 3D medical images. Our orientation handling, coordinate mapping (voxelToWorld / worldToVoxel), and anatomical direction label logic are based on ITK-SNAP's ImageCoordinateGeometry and GenericSliceModel implementations. Licensed under GPL.
niivue — A lightweight web-based NIfTI viewer. Our NIfTI parsing approach and webview rendering architecture were inspired by niivue's design. Licensed under BSD-2-Clause.
License
MIT © Maiwulanjiang Maiming