HTML Script Tag
This guide explains how to use VTK-Wasm directly in an HTML file using a <script> tag without a build step.
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>
<head>
<script
src="/path/to/vtkWebAssembly.mjs"
type="module"
></script>
<script src="https://unpkg.com/@kitware/vtk-wasm/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>async function buildWASMScene(vtk, titleText = "Sample VTK.wasm scene") {
function createSharedTextProperty() {
const textProperty = vtk.vtkTextProperty({fontSize: 22});
return textProperty;
}
async function createLookupTable(scalarRange) {
const lut = vtk.vtkColorTransferFunction();
await lut.setColorSpaceToHSV();
const colorSeries = vtk.vtkColorSeries({ colorScheme: 16 });
const numColors = await colorSeries.getNumberOfColors();
const scalarDiff = (scalarRange[1] - scalarRange[0]) / numColors;
for (let i = 0; i < numColors; i++) {
const color = await colorSeries.getColor(i);
const t = scalarRange[0] + i * scalarDiff;
lut.addRGBPoint(
t,
color[0] / 255,
color[1] / 255,
color[2] / 255,
);
}
await lut.build();
return lut;
}
async function createTitleTextActor(titleText, textProperty) {
const textActor = vtk.vtkTextActor({ input: titleText, textProperty });
const position = await textActor.getPositionCoordinate();
await position.setCoordinateSystemToNormalizedViewport();
return textActor;
}
// Create a VTK source. Output has a point data array named "Scalars" whose range is [0, PI].
const shapes = vtk.vtkPartitionedDataSetCollectionSource({ numberOfShapes: 2 });
const lut = await createLookupTable([0.0, Math.PI]);
const mapper = vtk.vtkCompositePolyDataMapper({ lookupTable: lut });
await mapper.setInputConnection(await shapes.getOutputPort());
const actor = vtk.vtkActor({ mapper, scale: [0.1, 0.1, 0.1] });
actor.property.edgeVisibility = true;
actor.property.edgeColor = [0.2, 0.2, 0.2];
const textProperty = createSharedTextProperty();
// Create an actor that displays the title.
const titleTextActor = await createTitleTextActor(titleText, textProperty);
// Setup rendering part
const renderer = vtk.vtkRenderer({ background: [0.384314, 0.364706, 0.352941] });
await renderer.addActor(actor);
await renderer.addActor(titleTextActor);
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,
});
await interactor.interactorStyle.setCurrentStyleToTrackballCamera();
// Create camera widget
const cameraOrientation = vtk.vtkCameraOrientationWidget({ interactor, parentRenderer: renderer });
cameraOrientation.enabled = true;
// Display the scalar bar at the bottom with a horizontal orientation
const scalarBarActor = vtk.vtkScalarBarActor({
lookupTable: lut,
title: "Scalars",
titleTextProperty: textProperty,
labelTextProperty: textProperty,
annotationTextProperty: textProperty,
unconstrainedFontSize: true,
});
const scalarBar = vtk.vtkScalarBarWidget({ scalarBarActor, interactor, defaultRenderer: renderer });
const scalarBarRepresentation = await scalarBar.getRepresentation();
await scalarBarRepresentation.setOrientation(0); // 1: vertical, 0: horizontal
const lowerLeftPosition = await scalarBarRepresentation.getPositionCoordinate();
await lowerLeftPosition.setValue([0.1, 0.05]);
scalarBar.enabled = true;
// Trigger render and start interactor
await interactor.start();
}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>
<head>
<script src="https://unpkg.com/@kitware/vtk-wasm/vtk-umd.js"></script>
<script src="example.js"></script>
</head>
<body>
<canvas id="vtk-wasm-window" tabindex="-1" onclick="focus()"></canvas>
<script>
vtkWASM.createNamespace("https://gitlab.kitware.com/api/v4/projects/13/packages/generic/vtk-wasm32-emscripten/9.5.20251215/vtk-9.5.20251215-wasm32-emscripten.tar.gz")
.then((vtk) => {
buildWASMScene(vtk, "This scene passes the VTK.wasm bundle from GitLab registry to createNamespace()");
});
</script>
</body>
</html>async function buildWASMScene(vtk, titleText = "Sample VTK.wasm scene") {
function createSharedTextProperty() {
const textProperty = vtk.vtkTextProperty({fontSize: 22});
return textProperty;
}
async function createLookupTable(scalarRange) {
const lut = vtk.vtkColorTransferFunction();
await lut.setColorSpaceToHSV();
const colorSeries = vtk.vtkColorSeries({ colorScheme: 16 });
const numColors = await colorSeries.getNumberOfColors();
const scalarDiff = (scalarRange[1] - scalarRange[0]) / numColors;
for (let i = 0; i < numColors; i++) {
const color = await colorSeries.getColor(i);
const t = scalarRange[0] + i * scalarDiff;
lut.addRGBPoint(
t,
color[0] / 255,
color[1] / 255,
color[2] / 255,
);
}
await lut.build();
return lut;
}
async function createTitleTextActor(titleText, textProperty) {
const textActor = vtk.vtkTextActor({ input: titleText, textProperty });
const position = await textActor.getPositionCoordinate();
await position.setCoordinateSystemToNormalizedViewport();
return textActor;
}
// Create a VTK source. Output has a point data array named "Scalars" whose range is [0, PI].
const shapes = vtk.vtkPartitionedDataSetCollectionSource({ numberOfShapes: 2 });
const lut = await createLookupTable([0.0, Math.PI]);
const mapper = vtk.vtkCompositePolyDataMapper({ lookupTable: lut });
await mapper.setInputConnection(await shapes.getOutputPort());
const actor = vtk.vtkActor({ mapper, scale: [0.1, 0.1, 0.1] });
actor.property.edgeVisibility = true;
actor.property.edgeColor = [0.2, 0.2, 0.2];
const textProperty = createSharedTextProperty();
// Create an actor that displays the title.
const titleTextActor = await createTitleTextActor(titleText, textProperty);
// Setup rendering part
const renderer = vtk.vtkRenderer({ background: [0.384314, 0.364706, 0.352941] });
await renderer.addActor(actor);
await renderer.addActor(titleTextActor);
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,
});
await interactor.interactorStyle.setCurrentStyleToTrackballCamera();
// Create camera widget
const cameraOrientation = vtk.vtkCameraOrientationWidget({ interactor, parentRenderer: renderer });
cameraOrientation.enabled = true;
// Display the scalar bar at the bottom with a horizontal orientation
const scalarBarActor = vtk.vtkScalarBarActor({
lookupTable: lut,
title: "Scalars",
titleTextProperty: textProperty,
labelTextProperty: textProperty,
annotationTextProperty: textProperty,
unconstrainedFontSize: true,
});
const scalarBar = vtk.vtkScalarBarWidget({ scalarBarActor, interactor, defaultRenderer: renderer });
const scalarBarRepresentation = await scalarBar.getRepresentation();
await scalarBarRepresentation.setOrientation(0); // 1: vertical, 0: horizontal
const lowerLeftPosition = await scalarBarRepresentation.getPositionCoordinate();
await lowerLeftPosition.setValue([0.1, 0.05]);
scalarBar.enabled = true;
// Trigger render and start interactor
await interactor.start();
}Defer WASM loading with annotation
In this example we tag the script to autoload WASM directly from the VTK repository's package registry and create a global vtk namespace. You can customize the wasm architecture and version by changing the data-url.
<html>
<head>
<script
src="https://unpkg.com/@kitware/vtk-wasm/vtk-umd.js"
id="vtk-wasm"
data-url="https://gitlab.kitware.com/api/v4/projects/13/packages/generic/vtk-wasm32-emscripten/9.5.20250913/vtk-9.5.20250913-wasm32-emscripten.tar.gz"
></script>
<script src="example.js"></script>
</head>
<body>
<canvas id="vtk-wasm-window" tabindex="-1" onclick="focus()"></canvas>
<script>
vtkReady.then((vtk) => {
buildWASMScene(vtk,"This scene points the data-url in script tag to the VTK.wasm bundle from GitLab registry"); // Also available on window.vtk
});
</script>
</body>
</html>async function buildWASMScene(vtk, titleText = "Sample VTK.wasm scene") {
function createSharedTextProperty() {
const textProperty = vtk.vtkTextProperty({fontSize: 22});
return textProperty;
}
async function createLookupTable(scalarRange) {
const lut = vtk.vtkColorTransferFunction();
await lut.setColorSpaceToHSV();
const colorSeries = vtk.vtkColorSeries({ colorScheme: 16 });
const numColors = await colorSeries.getNumberOfColors();
const scalarDiff = (scalarRange[1] - scalarRange[0]) / numColors;
for (let i = 0; i < numColors; i++) {
const color = await colorSeries.getColor(i);
const t = scalarRange[0] + i * scalarDiff;
lut.addRGBPoint(
t,
color[0] / 255,
color[1] / 255,
color[2] / 255,
);
}
await lut.build();
return lut;
}
async function createTitleTextActor(titleText, textProperty) {
const textActor = vtk.vtkTextActor({ input: titleText, textProperty });
const position = await textActor.getPositionCoordinate();
await position.setCoordinateSystemToNormalizedViewport();
return textActor;
}
// Create a VTK source. Output has a point data array named "Scalars" whose range is [0, PI].
const shapes = vtk.vtkPartitionedDataSetCollectionSource({ numberOfShapes: 2 });
const lut = await createLookupTable([0.0, Math.PI]);
const mapper = vtk.vtkCompositePolyDataMapper({ lookupTable: lut });
await mapper.setInputConnection(await shapes.getOutputPort());
const actor = vtk.vtkActor({ mapper, scale: [0.1, 0.1, 0.1] });
actor.property.edgeVisibility = true;
actor.property.edgeColor = [0.2, 0.2, 0.2];
const textProperty = createSharedTextProperty();
// Create an actor that displays the title.
const titleTextActor = await createTitleTextActor(titleText, textProperty);
// Setup rendering part
const renderer = vtk.vtkRenderer({ background: [0.384314, 0.364706, 0.352941] });
await renderer.addActor(actor);
await renderer.addActor(titleTextActor);
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,
});
await interactor.interactorStyle.setCurrentStyleToTrackballCamera();
// Create camera widget
const cameraOrientation = vtk.vtkCameraOrientationWidget({ interactor, parentRenderer: renderer });
cameraOrientation.enabled = true;
// Display the scalar bar at the bottom with a horizontal orientation
const scalarBarActor = vtk.vtkScalarBarActor({
lookupTable: lut,
title: "Scalars",
titleTextProperty: textProperty,
labelTextProperty: textProperty,
annotationTextProperty: textProperty,
unconstrainedFontSize: true,
});
const scalarBar = vtk.vtkScalarBarWidget({ scalarBarActor, interactor, defaultRenderer: renderer });
const scalarBarRepresentation = await scalarBar.getRepresentation();
await scalarBarRepresentation.setOrientation(0); // 1: vertical, 0: horizontal
const lowerLeftPosition = await scalarBarRepresentation.getPositionCoordinate();
await lowerLeftPosition.setValue([0.1, 0.05]);
scalarBar.enabled = true;
// Trigger render and start interactor
await interactor.start();
}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.