3D Viewer SDK Documentation
A modular, license-protected 3D rendering SDK built on Three.js (WebGPU renderer), React, MobX, and Vite.
The SDK ships as a pre-compiled package (@trikomi/core) consumed by six demo applications in a Yarn workspace monorepo.
dist/sdk.js and TypeScript type declarations. App developers build on top of the public plugin API.
Quick Start
Prerequisites
Install & Run Dev Server
# Clone the monorepo
git clone <repo-url> 3dviewer
cd 3dviewer
# Install all workspaces
yarn install
# Start the root dev server (serves all apps at their paths)
yarn dev
The Vite dev server starts at http://localhost:5173. Individual apps are accessible at:
/apps/viewer/— 3D Core Viewer/apps/sportswear-configurator/— Sportswear Configurator/apps/eyewear-tryon/— Eyewear Try-on/apps/face-mocap/— Face Motion Capture/apps/jewelry-configurator/— Jewelry Configurator/apps/box-configurator/— Box Configurator
Production Build
# Builds core SDK + all apps → ./dist/
yarn build
# Serve the built output locally
yarn serve
# → http://localhost:3000
yarn build:viewer, yarn build:sportswear, yarn build:eyewear, etc.
Project Structure
Why a Shared public/?
All six apps share a single public/ directory at the monorepo root. Each app's vite.config.*
sets publicDir: '../../public', so static assets like WASM binaries, Draco decoders, and textures
are always available without duplicating them per app.
ThreeViewer
ThreeViewer is the core engine class. It sets up the Three.js WebGPU renderer, scene, camera,
lighting, and render loop. It is the central object that all plugins attach to.
Constructor
import { ThreeViewer, viewerStore } from '@trikomi/core';
const viewer = new ThreeViewer(
containerElement, // HTMLDivElement to mount the canvas into
viewerStore, // MobX ViewerStore instance (shared across components)
{
assetBaseUrl?: string, // Path prefix for built-in 3D assets
forceLicenseCheck?: boolean, // Skip cache, always re-verify license
onAuthorized?: () => void // Called when license verification succeeds
}
);
Key Properties
| Property | Type | Description |
|---|---|---|
viewer.scene | THREE.Scene | The Three.js scene. Add your meshes here. |
viewer.camera | THREE.PerspectiveCamera | Main perspective camera (45° FOV by default). |
viewer.renderer | THREE.WebGPURenderer | WebGPU renderer with ACESFilmic tone mapping. |
viewer.store | ViewerStore | Reactive MobX store driving UI state. |
viewer.directionalLight | THREE.DirectionalLight | Primary directional light (adjustable). |
viewer.ambientLight | THREE.AmbientLight | Ambient fill light. |
viewer.secureSdk | SecureWasmSdk | WASM sandbox for premium shader execution. |
viewer.authStatus | AuthStatus | 'AUTHENTICATING' | 'AUTHORIZED' | 'UNAUTHORIZED' | 'TAMPERED' |
Key Methods
| Method | Description |
|---|---|
viewer.addPlugin(plugin) | Attaches a plugin to the viewer. Calls plugin.onAttach(viewer). |
viewer.getPlugin(id) | Returns a plugin by its id string, or undefined. |
viewer.removePlugin(id) | Detaches and disposes a plugin. |
viewer.mount(container) | Mounts the canvas into a new DOM element (re-mount after navigation). |
viewer.dispose() | Cleans up all resources, cancels animation frame, disposes plugins. |
viewer.handleResize() | Updates camera aspect ratio and renderer size. Called automatically on resize. |
The assetBaseUrl Rule
Built-in assets (GLB models, HDR env maps) live in packages/core/dist/assets/ at the source level
and are copied to dist/assets/core/ in the production build. The viewer resolves asset URLs using
assetBaseUrl:
- Dev:
'/packages/core/dist/assets/'(absolute, served from Vite root) - Prod:
'../assets/core/'(navigates up from the app subfolder to the shareddist/assets/)
Plugin System
Every feature of the viewer — from orbit controls to diamond shaders — is encapsulated in a plugin.
Plugins are decoupled, composable, and lifecycle-managed by ThreeViewer.
Creating a Custom Plugin
import { ViewerPlugin, ThreeViewer } from '@trikomi/core';
export class MyPlugin extends ViewerPlugin {
readonly id = 'MyPlugin'; // Unique identifier
onAttach(viewer: ThreeViewer): void {
super.onAttach(viewer);
// Setup code here – viewer.scene, viewer.camera, etc. are available
}
onUpdate(deltaTime: number): void {
// Called every animation frame with seconds since last frame
}
onDetach(): void {
// Cleanup resources (remove event listeners, dispose geometries, etc.)
super.onDetach();
}
}
// Attach to a viewer instance:
viewer.addPlugin(new MyPlugin());
// Retrieve later:
const myPlugin = viewer.getPlugin('MyPlugin');
Plugin Lifecycle
onAttach(viewer)— called immediately when the plugin is added viaaddPlugin(). Sets up Three.js objects, event listeners, reactions.onUpdate(deltaTime)— called each animation frame (optional). Use for animations and per-frame logic.onRender()— called after the Three.js render call (optional). Use for post-render operations.onDetach()— called when plugin is removed. Must dispose all resources to prevent memory leaks.
authStatus === 'AUTHORIZED'
will silently no-op or show a watermark. Always wait for authorization before loading premium plugins.
See the License System section.
Plugin Reference
.glb/.gltf files with Draco and Meshopt decompression. Call plugin.loadModel(url) to swap models at runtime.stats.js internally.ViewerStore
viewerStore is a singleton MobX observable store exported from @trikomi/core.
It acts as the reactive bridge between the 3D engine and React UI components.
Wrap components with MobX's observer() to auto-render on store changes.
import { viewerStore } from '@trikomi/core';
import { observer } from 'mobx-react-lite';
// In React components:
const MyUI = observer(() => (
<div>{viewerStore.activeModelName}</div>
));
// Update from anywhere – UI auto-updates:
viewerStore.setBackgroundColor('#1a1a2e');
viewerStore.setActiveModelName('My Model');
Key observable properties include activeModelName, backgroundColor, isModelLoading, modelError, floorTransparent, and environmentIntensity.
Demo Applications
Each app is an independent Vite + React workspace under apps/ that imports @trikomi/core as a dependency.
public/ directory via publicDir: '../../public' in their vite.config. This means static assets (WASM, Draco, textures) are never duplicated.
3D Viewer (apps/viewer)
Full-featured model viewer
A complete 3D model viewer with a full plugin suite. Demonstrates using nearly all available plugins together. Features a side panel MaterialEditor for PBR material editing, a ModelLoader for drag-and-drop GLTF uploads, and a DiamondTest page for showcasing the ray-tracing diamond shader.
| File | Purpose |
|---|---|
src/components/ThreeCanvas.tsx | Engine init, plugin attachment, auth-gating pattern |
src/components/MaterialEditor.tsx | PBR material property editor panel |
src/pages/DiamondTest.tsx | Diamond shader demo page |
Sportswear Configurator (apps/sportswear-configurator)
Product customization
An interactive jersey configurator. Users can change panel colors, upload logos, add text, switch fabric normal maps (mesh / knit / smooth), and move graphics freely on the UV canvas. Uses a custom TextureCompositor to composite SVG layers onto a WebGL canvas texture in real time.
Key architecture
- TextureCompositor — composites SVG base + user logos + text into a single
THREE.CanvasTexture. - ConfiguratorStore — MobX store tracking parts, colors, logos, and text items.
- Fabric normal maps loaded from
public/textures/; SVG base frompublic/textures/JSY-85 RL.svg.
Eyewear Try-on (apps/eyewear-tryon)
AR face try-on
Augmented Reality eyewear try-on using FaceMocapPlugin and a live webcam feed. Tracks 478 facial landmarks via MediaPipe and positions/orients 3D glasses onto the face in real time. Allows material editing of lens and frame properties. Includes a natural lighting mode that samples the webcam background for environment-matched reflections.
Setup notes
- Requires HTTPS (webcam permission). In dev, uses
@vitejs/plugin-basic-sslfor a self-signed cert. - MediaPipe WASM is loaded from
public/assets/wasm/. In prod the path resolves to../assets/wasm/. - The face occluder (
head_occluder.obj) is rendered as a depth mask to hide the glasses behind the face boundary.
Face Motion Capture (apps/face-mocap)
Unreal/Unity LiveLink
Full-face motion capture streaming blend shapes to Unreal Engine or Unity via a WebSocket LiveLink protocol. Tracks 52 ARKit-compatible blend shapes. Supports raccoon and glasses avatar modes for local preview. Blend shapes are displayed in real time in the UI panel.
LiveLink
Enter your Unreal/Unity LiveLink server address (e.g. 192.168.1.100:11111) in the panel
and click Connect. Blend shape data is streamed over WebSocket.
Jewelry Configurator (apps/jewelry-configurator)
Diamond rendering
Showcases the DiamondPlugin with an engagement ring model. Demonstrates multi-bounce ray-traced internal reflections with wavelength-accurate dispersion (fire). Supports switching gem cut styles and adjusting render quality settings.
Box Configurator (apps/box-configurator)
Parametric packaging
A parametric 3D box builder. Users set width, height, depth, wall thickness, and flap style.
The box geometry is procedurally generated by the BoxBuilder class from scratch
using THREE.BufferGeometry. Supports switching between different lid types.
360 Virtual Tour (apps/360tour)
Interactive tour builder
An interactive 360-degree virtual tour editor and viewer. Uses the TourPlugin from the @trikomi/tour package to render
spherical panorama images and place interactive hotspots. Features a TourEditor interface
for dragging and connecting hotspots to link rooms together, and a VirtualTour viewer
for navigating the finalized tours.
Build Pipeline
1. Packages Build (Core SDK & Plugins)
The core SDK and all separated plugin packages (like @trikomi/face-mocap, @trikomi/tour, etc.) are bundled with esbuild, not Vite. This keeps them modular, framework-agnostic, and produces clean ES modules. The core build also:
- Performs security hardening and integrity checks on the compiled output.
- Copies 3D assets (
.glb,.hdr,.exr) todist/assets/. - Generates TypeScript type declarations.
2. App Builds (Vite)
Each app is built independently with vite build. All apps use these key config values:
// apps/<any-app>/vite.config.ts
defineConfig({
base: './', // Relative asset references → works in any subfolder
publicDir: '../../public', // Shared WASM, Draco, textures
build: {
outDir: '../../dist/<appname>',
}
})
3. Monorepo Merge (build-monorepo.js)
After all apps are built, this script:
- Creates a shared
dist/assets/directory. - Rewrites
index.htmlasset references from./assets/→../assets/so they point to the shared root. - Moves each app's hashed JS/CSS/WASM chunks into the shared
dist/assets/folder. - Copies the root
index.htmlhub page todist/index.html.
Final dist layout
Asset Path Strategy
Because apps are built with base: './' and deployed in subfolders
(e.g. /demos/viewer/), absolute paths like /draco/
would resolve to the domain root — not the demo subfolder root.
The following pattern is used throughout:
| Environment | Path Used | Resolves To |
|---|---|---|
Dev (yarn dev) |
'/draco/gltf/' |
http://localhost:5173/draco/gltf/ — served from public/draco/gltf/ |
| Prod (any subfolder) | '../draco/gltf/' |
Navigates up from dist/viewer/ → dist/draco/gltf/ |
The same pattern applies to:
- Draco decoder:
GLTFPlugin—import.meta.env.DEV ? '/draco/gltf/' : '../draco/gltf/' - MediaPipe WASM:
FaceMocapPluginconstructor arg —DEV ? '/assets/wasm' : '../assets/wasm' - Textures: Sportswear ThreeCanvas —
DEV ? '/textures/' : '../textures/' - Core assets:
ThreeViewerassetBaseUrl —DEV ? '/packages/core/dist/assets/' : '../assets/core/'
public/, then reference them as
import.meta.env.DEV ? '/your-path' : '../your-path' in JavaScript.
For assets imported via Vite (images, SVGs, models), use a standard ES import — Vite handles hashing and path resolution automatically.
License & Auth System
ThreeViewer performs an automatic license verification on instantiation.
The verification is non-blocking — the engine starts immediately, but premium plugins
are gated until authorization completes.
Auth Flow
- On construction,
ThreeViewerchecks for a global token provided by the environment (usually viawindow.trikomi_licset to a.licfile path or raw string). - If a `.lic` URL is provided, the engine automatically fetches the file's contents.
- The engine splits the file into multiple tokens (if a user has purchased multiple module subscriptions).
- Each token is securely verified to prevent tampering, ensure domain whitelisting, and perform silent revocation checks.
- The engine aggregates all valid token feature bitfields into a master capability matrix.
authStatustransitions:AUTHENTICATING → AUTHORIZED(orUNAUTHORIZED/TAMPERED).- Premium plugins check the authorization status and capability matrix internally. If unauthorized or lacking the specific feature bit, they degrade gracefully (e.g., showing a watermark or disabling premium features).
React Integration Pattern
// 1. Initialize viewer with an onAuthorized callback
const viewer = new ThreeViewer(container, viewerStore, {
onAuthorized: () => {
// 3. Add premium plugins securely once authorization completes
viewer.addPlugin(new EnvironmentPlugin());
viewer.addPlugin(new DiamondPlugin());
// ... other premium plugins
}
});
// 2. Add free plugins immediately
viewer.addPlugin(new OrbitControlsPlugin());
viewer.addPlugin(new GLTFPlugin());
import.meta.env.DEV), you can test all premium plugins without a token.
However, the viewer runs in a restricted Trial Mode displaying a watermark and enforcing a strict session time limit. A valid token is required to remove these restrictions.
@trikomi/core SDK — Internal Developer Documentation