Skip to main content

Options Watching

NiiVue provides a powerful options watching system that allows you to monitor changes to any configuration option in real-time. This is particularly useful for creating responsive UIs, synchronizing multiple viewers, or implementing custom behaviors based on user interactions.

Try the interactive demo below! Use the controls to change various options and watch the change log update in real-time.

Loading...

Overview

The options watching feature uses JavaScript Proxies to detect when any property in the opts object changes. When a change occurs, a callback function is triggered with information about what changed, including the property name, old value, and new value.

Basic Usage

Setting up Options Watching

Use the watchOptsChanges() method to start monitoring option changes:

// Create a Niivue instance
const nv = new Niivue();
await nv.attachToCanvas(canvas);

// Set up options watcher
nv.watchOptsChanges((propertyName, newValue, oldValue) => {
console.log(`Option ${propertyName} changed:`);
console.log(` Old value:`, oldValue);
console.log(` New value:`, newValue);
});

// Now any changes to options will trigger the callback
nv.opts.crosshairWidth = 5; // Triggers callback
nv.opts.backColor = [1, 0, 0, 1]; // Triggers callback
nv.opts.isColorbar = true; // Triggers callback

Alternative: Direct Callback Assignment

You can also assign the callback directly to the onOptsChange property:

nv.onOptsChange = (propertyName, newValue, oldValue) => {
console.log(`${propertyName}: ${oldValue}${newValue}`);
};

Stopping Options Watching

Use unwatchOptsChanges() to stop monitoring changes:

// Stop watching for changes
nv.unwatchOptsChanges();

// Changes after this point will not trigger callbacks
nv.opts.crosshairWidth = 10; // No callback triggered

Advanced Features

Change Detection Logic

The options watcher is intelligent about change detection:

  • No duplicate events: Setting the same value multiple times only triggers the callback once
  • Deep value comparison: Array and object changes are properly detected
  • Type preservation: Original data types are maintained in old/new value parameters
// Only triggers callback once, even though set multiple times
nv.opts.crosshairWidth = 3;
nv.opts.crosshairWidth = 3; // No callback (same value)
nv.opts.crosshairWidth = 3; // No callback (same value)
nv.opts.crosshairWidth = 5; // Triggers callback (different value)

Array and Object Changes

The watcher properly handles complex data types:

nv.watchOptsChanges((propertyName, newValue, oldValue) => {
if (propertyName === 'backColor') {
console.log('Background color changed:');
console.log('Old:', oldValue); // [0, 0, 0, 1]
console.log('New:', newValue); // [1, 0, 0, 1]
}
});

// Array changes are detected
nv.opts.backColor = [1, 0, 0, 1]; // Red background
nv.opts.crosshairColor = [0, 1, 0, 1]; // Green crosshair
nv.opts.clipVolumeLow = [0.1, 0.1, 0.1]; // Clipping bounds

Automatic Cleanup

The options watcher is automatically cleaned up when the Niivue instance is destroyed:

// Watcher is active
nv.watchOptsChanges(callback);

// Cleanup the instance
nv.cleanup();

// Watcher is automatically removed - no memory leaks

Example Use Cases

Creating Responsive UIs

nv.watchOptsChanges((propertyName, newValue, oldValue) => {
// Update UI controls when options change programmatically
if (propertyName === 'isColorbar') {
document.getElementById('colorbarToggle').checked = newValue;
}

if (propertyName === 'backColor') {
updateColorPicker('backgroundColor', newValue);
}
});

Implementing Undo/Redo

const changeHistory = [];

nv.watchOptsChanges((propertyName, newValue, oldValue) => {
// Store change for undo functionality
changeHistory.push({
property: propertyName,
oldValue: oldValue,
newValue: newValue,
timestamp: Date.now()
});

// Keep history manageable
if (changeHistory.length > 100) {
changeHistory.shift();
}
});

function undo() {
const lastChange = changeHistory.pop();
if (lastChange) {
// Temporarily stop watching to avoid creating new history entries
nv.unwatchOptsChanges();
nv.opts[lastChange.property] = lastChange.oldValue;
nv.watchOptsChanges(/* restore watcher */);
}
}

Analytics and Logging

nv.watchOptsChanges((propertyName, newValue, oldValue) => {
// Log user interactions for analytics
analytics.track('niivue_option_changed', {
property: propertyName,
oldValue: JSON.stringify(oldValue),
newValue: JSON.stringify(newValue),
sessionId: getCurrentSessionId()
});
});

Supported Options

The options watcher monitors all properties in the NVConfigOptions interface, including:

Visual Options

  • crosshairWidth, crosshairColor, crosshairGap
  • backColor, fontColor
  • textHeight, colorbarHeight, colorbarWidth
  • isColorbar, isRuler, isOrientCube

Interaction Options

  • dragMode, doubleTouchTimeout, longTouchTimeout
  • penValue, penSize, isFilledPen
  • drawingEnabled

Display Options

  • isRadiologicalConvention, isNearestInterpolation
  • show3Dcrosshair, showColorbarBorder
  • multiplanarShowRender, multiplanarEqualSize

Layout Options

  • sliceMosaicString
  • clipVolumeLow, clipVolumeHigh

And many more! Any option that can be set through nv.opts will trigger the watcher when changed.

Technical Details

Performance

The options watching system is designed to be lightweight:

  • Uses JavaScript Proxies for efficient property interception
  • No polling or periodic checks required
  • Minimal overhead for unchanged properties
  • Automatic cleanup prevents memory leaks

Type Safety

When using TypeScript, the options watcher provides full type safety:

import { NVConfigOptions } from '@niivue/niivue';

nv.watchOptsChanges((
propertyName: keyof NVConfigOptions,
newValue: NVConfigOptions[keyof NVConfigOptions],
oldValue: NVConfigOptions[keyof NVConfigOptions]
) => {
// TypeScript knows the exact types of all parameters
console.log(`${propertyName} changed from ${oldValue} to ${newValue}`);
});