Quick start
This page shows how to adapt the verified first example without changing its ownership model.
Canonical example
Create an application-owned <div id="viewer"></div>, then use:
import {
DataLayer,
MultiLayerNeuroSurface,
NeuroSurfaceViewer,
SurfaceGeometry
} from 'surfview';
const container = document.querySelector<HTMLElement>('#viewer');
if (!container) throw new Error('Expected a #viewer element.');
const geometry = new SurfaceGeometry(
new Float32Array([-1, -1, 0, 1, -1, 0, 0, 1, 0]),
new Uint32Array([0, 1, 2]),
'left'
);
const surface = new MultiLayerNeuroSurface(geometry, { baseColor: 0xb8bec8 });
surface.addLayer(new DataLayer(
'activation',
new Float32Array([-2, 0, 2]),
null,
'coolwarm',
{ range: [-2, 2] }
));
const viewer = new NeuroSurfaceViewer(container, 800, 600, { preset: 'paper-light' });
viewer.addSurface(surface, 'left-cortex');
window.addEventListener('pagehide', () => viewer.dispose(), { once: true });This exact file is compiled in strict mode against a clean npm pack archive. It deliberately uses tiny in-memory arrays; the viewer and layer APIs are the same for a cortical mesh.
Size and resize
Pass positive CSS-pixel dimensions to the constructor. If the host changes size, resize the viewer from the application's layout observer:
const observer = new ResizeObserver(([entry]) => {
if (!entry) return;
const { width, height } = entry.contentRect;
if (width > 0 && height > 0) viewer.resize(width, height);
});
observer.observe(container);Disconnect host-owned observers during application teardown. viewer.dispose() cleans up viewer-owned resources, but it cannot remove an observer the host created.
Replace the sample geometry
SurfaceGeometry accepts finite XYZ triples and integer triangle indices. For network data, prefer the validated loader:
const geometry = await loadSurface(
'/subjects/S01/lh.pial.gii',
'auto',
'unknown',
30_000,
false,
100,
{ signal: abortController.signal, maxBytes: 100 * 1024 * 1024 }
);Explicit laterality wins, followed by valid file metadata and then an unambiguous filename token. If none is available, laterality remains unknown. A failed or aborted load returns no geometry and does not mutate a viewer.
Add an overlay
DataLayer accepts dense values when indices is null, or sparse values with an explicit vertex-index array. A dense layer must provide one value per surface vertex before compositing:
const activation = new DataLayer(
'activation',
vertexValues,
null,
'coolwarm',
{ range: [-5, 5], threshold: [-1.96, 1.96], opacity: 0.85 }
);
surface.addLayer(activation);Layer mutations request a coalesced render. Call viewer.render() only when an immediate synchronous paint is specifically required.
Optional controls
The first-party panel is a separate ESM entry and an ordinary DOM sibling of the canvas:
import { mountSurfViewControls } from 'surfview/controls';
const controls = mountSurfViewControls(viewer, controlsContainer, {
theme: 'auto',
density: 'compact'
});
// During host teardown:
controls.dispose();
viewer.dispose();Mounting does not add scene objects, move the camera, or rearrange the host page. See First-party controls for React, report targets, and feature selection.
Continue
- Surfaces for formats and geometry invariants
- Layers for ordering, updates, and compositing
- Viewer for lifecycle and interaction
- Reliability and contracts for the enforced boundaries