Appearance
Plain JavaScript
The following examples rely on loading the vtk.umd.js
bundle from a CDN. Also to mainly focus on the initialization part, we've externalized the JS/WASM code since that part does not change.
Load WASM as module
In this example we pre-load the WASM module and therefore we don't need to provide any URL for loading it when creating the vtk namespace.
html
<html>
<head>
<script
src="/vtk-wasm/wasm32/latest/vtkWebAssembly.mjs"
type="module"
></script>
<script src="https://unpkg.com/@kitware/trame-vtklocal@1.1.1/dist/umd/vtk.umd.js"></script>
<script src="example.js"></script>
</head>
<body>
<canvas id="vtk-wasm-window"></canvas>
<script>
vtkWASM.createNamespace().then(buildWASMScene);
</script>
</body>
</html>
js
async function buildWASMScene(vtk) {
// Make up some data array to generate a mesh (JS-only)
function makeQuadMesh(nx, ny) {
// Create a grid of points on the XY plane from (0, 0) to (nx, ny)
const pointJSArray = []
for (let i = 0; i < ny + 1; i++) {
for (let j = 0; j < nx + 1; j++) {
const x = (j - 0.5 * nx) / nx;
const y = (i - 0.5 * ny) / ny;
pointJSArray.push(x); // x-coordinate
pointJSArray.push(y); // y-coordinate
pointJSArray.push(2 * Math.sqrt(x * x + y * y) * Math.sin(x) * Math.cos(y)); // z-coordinate
}
}
const connectivityJSArray = [];
const offsetsJSArray = [];
for (let i = 0; i < ny; i++) {
for (let j = 0; j < nx; j++) {
offsetsJSArray.push(connectivityJSArray.length);
connectivityJSArray.push(j + i * (nx + 1));
connectivityJSArray.push(j + i * (nx + 1) + 1);
connectivityJSArray.push(j + i * (nx + 1) + nx + 2);
connectivityJSArray.push(j + i * (nx + 1) + nx + 1);
}
}
offsetsJSArray.push(connectivityJSArray.length);
return {
points: pointJSArray,
offsets: offsetsJSArray,
connectivity: connectivityJSArray,
};
}
const meshData = makeQuadMesh(50, 50);
// Working on VTK.wasm
// => vtkObject creation is directly available on the vtk namespace
const points = vtk.vtkPoints();
const polys = vtk.vtkCellArray()
const connectivity = vtk.vtkTypeInt32Array();
const offsets = vtk.vtkTypeInt32Array();
// Ways to bind JS data to VTK.wasm types
// => method call are async and needs to be awaited
// => property can be accessed using the dot notation
await points.data.setArray(new Float32Array(meshData.points));
await connectivity.setArray(new Int32Array(meshData.connectivity));
await offsets.setArray(new Int32Array(meshData.offsets));
// Calling methods with other vtkObject as arguments
await polys.setData(offsets, connectivity);
// Using properties to set values as a batch update
const polyData = vtk.vtkPolyData();
polyData.set({ points, polys })
// Getting values from method call (async) or property (sync)
console.log("NumberOfPoints:", await polyData.getNumberOfPoints());
console.log("NumberOfCells:", await polyData.getNumberOfCells());
console.log("PolyDataBounds:", await polyData.getBounds());
// Create object with properties in constructor
const mapper = vtk.vtkPolyDataMapper();
await mapper.setInputData(polyData);
const actor = vtk.vtkActor({ mapper });
// Setting a property even across vtkObjects
// Same as: await (await actor.getProperty()).setEdgeVisibility(true);
actor.property.edgeVisibility = true;
// Setup rendering part
const renderer = vtk.vtkRenderer();
await renderer.addActor(actor);
await renderer.resetCamera();
// Create a RenderWindow and bind it to a canvas in the DOM
const canvasSelector = "#vtk-wasm-window";
const renderWindow = vtk.vtkRenderWindow({ canvasSelector });
await renderWindow.addRenderer(renderer);
const interactor = vtk.vtkRenderWindowInteractor({
canvasSelector,
renderWindow,
});
// Trigger render and start interactor
await interactor.render();
await interactor.start();
// Observing vtkObject
const tag = renderWindow.observe("StartEvent", () => {
console.log("Camera position", renderer.activeCamera.position);
});
setTimeout(() => {
// Remove observer for one specific tag
renderWindow.unObserve(tag);
// Remove all observers
renderWindow.unObserveAll();
// Print the full state of a vtkObject
console.log("Camera state:", renderer.activeCamera.state);
}, 30000);
}
Defer WASM loading
In this example, since we didn't load the WASM module, we need to specify from where it should be loaded.
In this context we provide the URL where the WASM bundle can be found and used from.
html
<html>
<head>
<script src="https://unpkg.com/@kitware/trame-vtklocal@1.1.1/dist/umd/vtk.umd.js"></script>
<script src="example.js"></script>
</head>
<body>
<canvas id="vtk-wasm-window"></canvas>
<script>
vtkWASM
.createNamespace("/vtk-wasm/wasm32/latest")
.then(buildWASMScene);
</script>
</body>
</html>
js
async function buildWASMScene(vtk) {
// Make up some data array to generate a mesh (JS-only)
function makeQuadMesh(nx, ny) {
// Create a grid of points on the XY plane from (0, 0) to (nx, ny)
const pointJSArray = []
for (let i = 0; i < ny + 1; i++) {
for (let j = 0; j < nx + 1; j++) {
const x = (j - 0.5 * nx) / nx;
const y = (i - 0.5 * ny) / ny;
pointJSArray.push(x); // x-coordinate
pointJSArray.push(y); // y-coordinate
pointJSArray.push(2 * Math.sqrt(x * x + y * y) * Math.sin(x) * Math.cos(y)); // z-coordinate
}
}
const connectivityJSArray = [];
const offsetsJSArray = [];
for (let i = 0; i < ny; i++) {
for (let j = 0; j < nx; j++) {
offsetsJSArray.push(connectivityJSArray.length);
connectivityJSArray.push(j + i * (nx + 1));
connectivityJSArray.push(j + i * (nx + 1) + 1);
connectivityJSArray.push(j + i * (nx + 1) + nx + 2);
connectivityJSArray.push(j + i * (nx + 1) + nx + 1);
}
}
offsetsJSArray.push(connectivityJSArray.length);
return {
points: pointJSArray,
offsets: offsetsJSArray,
connectivity: connectivityJSArray,
};
}
const meshData = makeQuadMesh(50, 50);
// Working on VTK.wasm
// => vtkObject creation is directly available on the vtk namespace
const points = vtk.vtkPoints();
const polys = vtk.vtkCellArray()
const connectivity = vtk.vtkTypeInt32Array();
const offsets = vtk.vtkTypeInt32Array();
// Ways to bind JS data to VTK.wasm types
// => method call are async and needs to be awaited
// => property can be accessed using the dot notation
await points.data.setArray(new Float32Array(meshData.points));
await connectivity.setArray(new Int32Array(meshData.connectivity));
await offsets.setArray(new Int32Array(meshData.offsets));
// Calling methods with other vtkObject as arguments
await polys.setData(offsets, connectivity);
// Using properties to set values as a batch update
const polyData = vtk.vtkPolyData();
polyData.set({ points, polys })
// Getting values from method call (async) or property (sync)
console.log("NumberOfPoints:", await polyData.getNumberOfPoints());
console.log("NumberOfCells:", await polyData.getNumberOfCells());
console.log("PolyDataBounds:", await polyData.getBounds());
// Create object with properties in constructor
const mapper = vtk.vtkPolyDataMapper();
await mapper.setInputData(polyData);
const actor = vtk.vtkActor({ mapper });
// Setting a property even across vtkObjects
// Same as: await (await actor.getProperty()).setEdgeVisibility(true);
actor.property.edgeVisibility = true;
// Setup rendering part
const renderer = vtk.vtkRenderer();
await renderer.addActor(actor);
await renderer.resetCamera();
// Create a RenderWindow and bind it to a canvas in the DOM
const canvasSelector = "#vtk-wasm-window";
const renderWindow = vtk.vtkRenderWindow({ canvasSelector });
await renderWindow.addRenderer(renderer);
const interactor = vtk.vtkRenderWindowInteractor({
canvasSelector,
renderWindow,
});
// Trigger render and start interactor
await interactor.render();
await interactor.start();
// Observing vtkObject
const tag = renderWindow.observe("StartEvent", () => {
console.log("Camera position", renderer.activeCamera.position);
});
setTimeout(() => {
// Remove observer for one specific tag
renderWindow.unObserve(tag);
// Remove all observers
renderWindow.unObserveAll();
// Print the full state of a vtkObject
console.log("Camera state:", renderer.activeCamera.state);
}, 30000);
}
Defer WASM loading with annotation
In this example we tag the script to autoload WASM and create a global vtk namespace.
html
<html>
<head>
<script
src="https://unpkg.com/@kitware/trame-vtklocal@1.1.1/dist/umd/vtk.umd.js"
id="vtk-wasm"
data-url="/vtk-wasm/wasm32/latest"
></script>
<script src="example.js"></script>
</head>
<body>
<canvas id="vtk-wasm-window"></canvas>
<script>
vtkReady.then((vtk) => {
buildWASMScene(vtk); // Also available on window.vtk
});
</script>
</body>
</html>
js
async function buildWASMScene(vtk) {
// Make up some data array to generate a mesh (JS-only)
function makeQuadMesh(nx, ny) {
// Create a grid of points on the XY plane from (0, 0) to (nx, ny)
const pointJSArray = []
for (let i = 0; i < ny + 1; i++) {
for (let j = 0; j < nx + 1; j++) {
const x = (j - 0.5 * nx) / nx;
const y = (i - 0.5 * ny) / ny;
pointJSArray.push(x); // x-coordinate
pointJSArray.push(y); // y-coordinate
pointJSArray.push(2 * Math.sqrt(x * x + y * y) * Math.sin(x) * Math.cos(y)); // z-coordinate
}
}
const connectivityJSArray = [];
const offsetsJSArray = [];
for (let i = 0; i < ny; i++) {
for (let j = 0; j < nx; j++) {
offsetsJSArray.push(connectivityJSArray.length);
connectivityJSArray.push(j + i * (nx + 1));
connectivityJSArray.push(j + i * (nx + 1) + 1);
connectivityJSArray.push(j + i * (nx + 1) + nx + 2);
connectivityJSArray.push(j + i * (nx + 1) + nx + 1);
}
}
offsetsJSArray.push(connectivityJSArray.length);
return {
points: pointJSArray,
offsets: offsetsJSArray,
connectivity: connectivityJSArray,
};
}
const meshData = makeQuadMesh(50, 50);
// Working on VTK.wasm
// => vtkObject creation is directly available on the vtk namespace
const points = vtk.vtkPoints();
const polys = vtk.vtkCellArray()
const connectivity = vtk.vtkTypeInt32Array();
const offsets = vtk.vtkTypeInt32Array();
// Ways to bind JS data to VTK.wasm types
// => method call are async and needs to be awaited
// => property can be accessed using the dot notation
await points.data.setArray(new Float32Array(meshData.points));
await connectivity.setArray(new Int32Array(meshData.connectivity));
await offsets.setArray(new Int32Array(meshData.offsets));
// Calling methods with other vtkObject as arguments
await polys.setData(offsets, connectivity);
// Using properties to set values as a batch update
const polyData = vtk.vtkPolyData();
polyData.set({ points, polys })
// Getting values from method call (async) or property (sync)
console.log("NumberOfPoints:", await polyData.getNumberOfPoints());
console.log("NumberOfCells:", await polyData.getNumberOfCells());
console.log("PolyDataBounds:", await polyData.getBounds());
// Create object with properties in constructor
const mapper = vtk.vtkPolyDataMapper();
await mapper.setInputData(polyData);
const actor = vtk.vtkActor({ mapper });
// Setting a property even across vtkObjects
// Same as: await (await actor.getProperty()).setEdgeVisibility(true);
actor.property.edgeVisibility = true;
// Setup rendering part
const renderer = vtk.vtkRenderer();
await renderer.addActor(actor);
await renderer.resetCamera();
// Create a RenderWindow and bind it to a canvas in the DOM
const canvasSelector = "#vtk-wasm-window";
const renderWindow = vtk.vtkRenderWindow({ canvasSelector });
await renderWindow.addRenderer(renderer);
const interactor = vtk.vtkRenderWindowInteractor({
canvasSelector,
renderWindow,
});
// Trigger render and start interactor
await interactor.render();
await interactor.start();
// Observing vtkObject
const tag = renderWindow.observe("StartEvent", () => {
console.log("Camera position", renderer.activeCamera.position);
});
setTimeout(() => {
// Remove observer for one specific tag
renderWindow.unObserve(tag);
// Remove all observers
renderWindow.unObserveAll();
// Print the full state of a vtkObject
console.log("Camera state:", renderer.activeCamera.state);
}, 30000);
}
Configuration options
The method createNamespace(url, config)
takes two arguments. The first one is used to specify the base directory where the wasm file from VTK will be find. When the module is loaded, the url parameter could be skipped. For the config it is aimed to tune how you would like your WASM environement to behave. The following sections cover the various options and what it means.
{ rendering: 'webgl', mode: 'sync' }
- Using WebGL2 for rendering.
- Using synchronous method execution.
{ rendering: 'webgl', mode: 'async' }
- Using WebGL2 for rendering.
- Using asynchronous method execution.
- This require WebAssembly JavaScript Promise Integration (JSPI) support in your browser
{ rendering: 'webgpu' }
- Using WebGPU for rendering.
- WebGPU only works with the asynchronous implementation of method execution.
- This require WebAssembly JavaScript Promise Integration (JSPI) support in your browser
For the annotation usecase you can add data-config="{'rendering': 'webgpu'}"
attribute in your HTML to adjust the config setting.