Adding VTK.wasm to a Project
There are two ways to get VTK.wasm into a web project:
- HTML Script Tag — load a prebuilt bundle from a CDN, no build step required. Best for quick prototypes, demos, and embedding into existing pages.
- Bundler — install the
@kitware/vtk-wasmpackage andimportit. Best for application development with a tool like Vite.
Either way you end up calling loadAsync; only how it reaches the page differs.
HTML Script Tag
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. To focus on the initialization part, we've externalized the JS/WASM scene code since that part does not change.
Load WASM as a module
In this example we pre-load the WASM module, so we don't need to provide any URL when loading it.
<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.loadAsync().then((runtime) =>
buildWASMScene(runtime.createStandaloneSession().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();
}Defer WASM loading
Since we didn't pre-load the WASM module here, we provide the URL where the WASM bundle can be found.
<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.loadAsync({ url: "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((runtime) => {
const session = runtime.createStandaloneSession();
buildWASMScene(session.vtk, "This scene passes the VTK.wasm bundle from GitLab registry to loadAsync()");
});
</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
Here we tag the script to autoload WASM directly from the VTK repository's package registry; the VTK namespace is then reached by awaiting vtkwasm.ready. 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.6.20260228/vtk-9.6.20260228-wasm32-emscripten.tar.gz"
></script>
<script src="example.js"></script>
</head>
<body>
<canvas id="vtk-wasm-window" tabindex="-1" onclick="focus()"></canvas>
<script>
vtkwasm.ready.then((vtk) => {
buildWASMScene(vtk,"This scene points the data-url in script tag to the VTK.wasm bundle from GitLab registry");
});
</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();
}The data-config attribute on the annotation <script> accepts the same settings as the options object passed to loadAsync(...) — for example, add data-config='{"rendering": "webgpu"}' to switch the rendering backend. See Loading VTK.wasm for what each option does, or the loadAsync reference for the exact option types.
Bundler
Modern web development relies on a package manager to bring in project dependencies. This section covers how published releases are used within a JavaScript project.
Project setup
In this simple example we use Vite with Vanilla JavaScript. The full code is available for reference here. Use a concrete version, or "latest", for the @kitware/vtk-wasm package. Here, the example uses a relative path to the vtk-wasm project root so the in-repo documentation stays relevant.
{
"name": "modern-app",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"devDependencies": {
"vite": "^6.3.5"
},
"dependencies": {
"@kitware/vtk-wasm": "file:../../.."
}
}<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Standalone VTK.wasm example</title>
</head>
<body>
<div id="app">
<canvas tabindex="-1" onclick="focus()"></canvas>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>import "./style.css";
import { loadAsync } from "@kitware/vtk-wasm";
import { buildWASMScene } from "./example";
const runtime = await loadAsync({
url: "https://gitlab.kitware.com/api/v4/projects/13/packages/generic/vtk-wasm32-emscripten/9.5.20251215/vtk-9.5.20251215-wasm32-emscripten.tar.gz",
});
const session = runtime.createStandaloneSession();
buildWASMScene(session.vtk, "#app > canvas", "This scene passes the VTK.wasm bundle from GitLab registry to loadAsync()");export async function buildWASMScene(vtk, canvasSelector = "#vtk-wasm-window", 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 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();
}:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
height: 100vh;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
}npm install
npm run buildHere, the VTK.wasm bundle is downloaded in the browser directly from the GitLab package registry. See the src/main.js file for the relevant code.