TRIKOMI 3D SDK Documentation
A modular 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
PNPM workspace monorepo.
pnpm dev:sdk) supports dynamic source-code mapping if the private repository is adjacent. 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
pnpm install
# Start the root dev server (serves all apps at their paths)
pnpm 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
/apps/360tour/— 360 Virtual Tour
Production Build
# Builds core SDK + all apps → ./dist/
pnpm build
# Serve the built output locally
pnpm serve
# → http://localhost:3000
pnpm build:viewer, pnpm build:sportswear, pnpm 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
}
);
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(PluginClass) |
Returns a plugin by its class constructor, or undefined. |
viewer.removePlugin(PluginClass) |
Detaches and removes a plugin by its class constructor. |
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. |
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.
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 (pnpm 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/'
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.
@trikomi/core SDK — Internal Developer Documentation