Embedding <Visualizer />
Common use cases: embedding a snapshot viewer next to a robot’s data, rendering test fixtures in a custom dashboard, or shipping the visualizer in a larger debugging surface.
Getting started
Section titled “Getting started”Prerequisites
Section titled “Prerequisites”A Svelte 5 / SvelteKit (or Astro+Svelte, Vite+Svelte, etc.) project. The package is svelte-typed and built for runes; you cannot consume it from Svelte 4.
Install
Section titled “Install”The package declares a list of peerDependencies (Svelte, Threlte, Three.js, the Viam SDK, prime-core, several Zag.js components, etc.). pnpm will print warnings for any that aren’t already in your project — install whatever it asks for.
Peers that only a plugin needs are marked optional, so you install them only if you mount that plugin: @dimforge/rapier3d-compat and @threlte/rapier for MoveFrame, @zag-js/tree-view and svelte-virtuallists for <WorldTree />, @zag-js/tabs for <Settings />, @threlte/xr for <XR />, earcut for <SelectionTool />, and the Connect RPC client for <DrawService />.
Configure your bundler
Section titled “Configure your bundler”The visualizer’s scene imports an HDR environment map, which a stock Vite config won’t handle.
Add it to assetsInclude, or the build fails parsing it as JavaScript.
Disable SSR for the page
Section titled “Disable SSR for the page”The visualizer renders with WebGPU (falling back to WebGL2), which needs the browser. Disable SSR on any page that mounts <Visualizer />. In SvelteKit:
Mounting <Visualizer />
Section titled “Mounting <Visualizer />”<Visualizer /> is the entire visualizer rendered as a single Svelte component. It expects an explicitly-sized parent because it stretches to fill its container:
That’s enough to get an empty scene: the camera and its controls, the grid, the entity renderer, and the portal targets plugins render into. From there, you can:
- Pass
partIDto bind it to a specific Viam machine part. - Pass
cameraPoseto set an initial camera position and look-at target. - Provide a
childrensnippet to render your own Threlte primitives or the<Snapshot />component inside the scene.
What’s in the box, and what isn’t
Section titled “What’s in the box, and what isn’t”<Visualizer /> is the scene, not the app. It renders:
- the
<Canvas>, inrenderMode="on-demand" - the camera, camera controls, and the reset / orthographic buttons
- the grid, lighting, and environment
- the entity renderer — everything spawned into the ECS world, including
<PCD /> - selection and hover affordances
- the portal targets that plugins render their UI into
Everything else is a plugin you opt into — including the workspace modes: with no mode plugin mounted the mode is none and the visualizer is a bare renderer, with no toggle, no details cards, and no live-query pausing. The visualizer’s own chrome ships as six plugins:
| Plugin | Adds |
|---|---|
<Monitor /> | Monitor mode: the mode button and details cards |
<WorldTree /> | The scene hierarchy panel |
<Settings /> | The settings cog and its tabbed popover |
<BuildFrames /> | Build mode: gizmos, snapping, frame editing |
<FileDrop /> | Drag-and-drop loading of snapshots, PCD and PLY |
<FramePov /> | Per-frame point-of-view panels |
To reproduce the standalone app, mount all six plus the feature plugins you want, keeping
Monitor before BuildFrames before MoveFrame — mode plugins’ mount order sets both the
toggle’s button order and the fallback priority. To embed a focused viewer, mount none of them —
or just the one or two that earn their space.
A focused point-cloud viewer
Section titled “A focused point-cloud viewer”The smallest useful embed: a scene, a point cloud you already have the bytes for, and a ruler.
<PCD /> parses in a worker and spawns an entity the visualizer’s renderer picks up, so nothing
else is needed — no partID, and no network calls of its own.
Rebinding data reparses and respawns the entity, so this works for a polling feed as well as a
one-shot capture.
Passing props and children
Section titled “Passing props and children”Rendering snapshots
Section titled “Rendering snapshots”A snapshot is a serialized scene — every transform, drawing, and camera setting captured into one protobuf payload. Snapshots render purely client-side; no draw server, no live machine, no network calls.
Use the <Snapshot /> component as a child of <Visualizer />:
Put the snapshot in your app’s public/ (or static/) directory and fetch it at runtime. This is the pattern the playground uses.
Fetch and decode at runtime:
JSON snapshots are bigger but easier to debug:
You can re-bind a different snapshot at any time — <Snapshot /> watches the prop and respawns the scene entities when the value changes.
The playground renders this snapshot by default. To experiment with it, download visualization_snapshot.json, edit a few values, and drag the modified file onto the playground — the scene re-renders with your changes. The drop zone accepts .json, .pb, and .pb.gz snapshot files, but they must be named with a visualization_snapshot prefix (see src/lib/plugins/FileDrop/file-names.ts) for the loader to recognize them.
Producing snapshots
Section titled “Producing snapshots”Snapshots are produced from Go using the draw package. The most common pattern: build up a *draw.Snapshot, then call MarshalBinary() (or MarshalJSON() for human-readable output) and ship the bytes wherever your frontend can fetch them.
A minimal producer:
See the draw API reference for the full set of Snapshot.Draw* methods.
Using only the standalone components
Section titled “Using only the standalone components”A few components are exported via the /lib subpath and work without a <Visualizer /> parent — useful if you just need a piece of the visualizer (e.g. an axes helper) inside your own Threlte canvas:
The standalone components use node materials, so they render only inside a WebGPURenderer. Use the Canvas from @threlte/core/webgpu, not the one from @threlte/core.
<Snapshot /> is also exported from /lib, but unlike <AxesHelper /> it requires the context that <Visualizer /> provides — use it as a child of <Visualizer />, not standalone.