Use when embedding the Speckle viewer in a web application, loading 3D models, or implementing viewer interactions. Prevents missing extension initialization, incorrect loader setup, and broken event handling. Covers @speckle/viewer setup, Viewer class lifecycle, SpeckleLoader, UrlHelper, extensions (CameraController, FilteringExtension, SelectionExtension, DiffExtension, MeasurementsTool, SectionTool), events, and custom extension development. Keywords: speckle viewer, 3d viewer, webgl, load model, filter, select, diff, measure, section, camera, extension, embed, embed viewer, view model in browser, interactive viewer.
Use when embedding the Speckle viewer in a web application, loading 3D models, or implementing viewer interactions. Prevents missing extension initialization, incorrect loader setup, and broken event handling. Covers @speckle/viewer setup, Viewer class lifecycle, SpeckleLoader, UrlHelper, extensions (CameraController, FilteringExtension, SelectionExtension, DiffExtension, MeasurementsTool, SectionTool), events, and custom extension development. Keywords: speckle viewer, 3d viewer, webgl, load model, filter, select, diff, measure, section, camera, extension, embed, embed viewer, view model in browser, interactive viewer.
license
MIT
compatibility
Designed for Claude Code. Requires @speckle/viewer (latest), Speckle Server 2.x/3.x.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
speckle-impl-viewer
Quick Reference
Installation
npm install --save @speckle/viewer
The package is written in TypeScript and provides full type definitions.
Architecture Overview
Component
Role
Viewer
Core class -- manages WebGL context, scene graph, extensions, rendering loop
SpeckleLoader
Fetches and deserializes Speckle objects into the viewer scene
UrlHelper
Resolves Speckle project/model URLs into loader-compatible resource URLs
Extension
Base class for all viewer plugins (camera, selection, filtering, etc.)
WorldTree
Internal scene graph hierarchy storing all loaded objects
SpeckleRenderer
Low-level WebGL rendering engine built on Three.js internals
Viewer Lifecycle
1. Create container element --> HTML div with explicit dimensions
2. new Viewer(container, params) --> Construct viewer instance
3. await viewer.init() --> Initialize WebGL context and assets
4. viewer.createExtension(...) --> Register required extensions
5. await viewer.loadObject(loader) --> Load 3D model data
6. viewer.on(event, handler) --> Subscribe to viewer events
7. viewer.dispose() --> Release all GPU resources on cleanup
Critical Warnings
NEVER call loadObject(), createExtension(), or any other viewer method before await viewer.init() completes. The viewer internals are NOT ready until init resolves. Calling methods before init causes silent failures or runtime errors.
NEVER omit viewer.dispose() when removing the viewer from the DOM. Failing to dispose causes GPU memory leaks that accumulate across page navigations in SPAs.
NEVER pass an empty string as the auth token in SpeckleLoader when loading private models. An empty token causes the loader to silently load zero objects with no error. ALWAYS provide a valid personal access token for private resources.
NEVER create extensions before calling await viewer.init(). Extensions depend on viewer internals that are initialized during init().
ALWAYS set explicit dimensions (width + height) on the container element. A zero-size container causes the WebGL context to fail silently.
ALWAYS call viewer.resize() when the container element changes size dynamically (e.g., window resize, panel toggle). The viewer does NOT detect container size changes automatically.
Accepts a Speckle project/model URL and returns an array of resource URLs suitable for SpeckleLoader. ALWAYS use this method -- NEVER construct resource URLs manually.
import { Extension, IViewer } from"@speckle/viewer";
classMyExtensionextendsExtension {
// Declare dependencies on other extensionsgetinject() {
return [CameraController]; // Auto-injected by viewer
}
// Lifecycle hooksonEarlyUpdate(deltaTime?: number): void {
// Called before viewer update each frame
}
onLateUpdate(deltaTime?: number): void {
// Called after viewer update each frame
}
onRender(): void {
// Called after each render pass
}
onResize(): void {
// Called when viewport resizes
}
}
// Register with viewerconst myExt = viewer.createExtension(MyExtension);
ALWAYS declare dependencies via the inject getter. The viewer resolves and injects them automatically. NEVER manually instantiate extensions with new.
Rendering Pipeline
The viewer uses a custom WebGL rendering pipeline built on Three.js internals. Access via viewer.getRenderer():
Scene management and material overrides
Custom render passes
Shadow configuration
Post-processing effects
This is an advanced API. ALWAYS use extensions for standard interactions. Only use SpeckleRenderer when built-in extensions do not cover your use case.