@trikomi/core · Three.js WebGPU · React · Vite

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.

ℹ Info
The core source code is private. The demo repo ships only the compiled 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
✓ Tip
To build and preview a single app without rebuilding everything, use the workspace shortcuts: yarn build:viewer, yarn build:sportswear, yarn build:eyewear, etc.

Project Structure

3dviewer/ ├── apps/ ← Demo applications (each is a Vite+React workspace) │ ├── box-configurator/ │ ├── eyewear-tryon/ │ ├── face-mocap/ │ ├── jewelry-configurator/ │ ├── sportswear-configurator/ │ └── viewer/ │ ├── packages/ │ └── core/ ← @trikomi/core (private source, compiled SDK) │ ├── src/ ← Private source (not in demo repo) │ └── dist/ ← Compiled output consumed by apps │ ├── sdk.js ← Bundled ES module │ ├── index.d.ts ← TypeScript declarations │ └── assets/ ← 3D assets (GLBs, HDRs, textures) │ ├── public/ ← Shared static assets served by all apps │ ├── assets/wasm/ ← MediaPipe WASM files (face tracking) │ ├── draco/gltf/ ← Draco GLTF decoder binaries │ ├── textures/ ← Shared textures (fabric normals, SVGs) │ └── js/ ← Third-party scripts (jQuery, Kendo, Pako) │ ├── build-monorepo.js ← Merges all app builds into ./dist/ ├── inject-token-plugin.ts ← Vite plugin: injects master license token ├── index.html ← Root hub page (links to all apps) └── package.json ← Yarn workspaces root

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

PropertyTypeDescription
viewer.sceneTHREE.SceneThe Three.js scene. Add your meshes here.
viewer.cameraTHREE.PerspectiveCameraMain perspective camera (45° FOV by default).
viewer.rendererTHREE.WebGPURendererWebGPU renderer with ACESFilmic tone mapping.
viewer.storeViewerStoreReactive MobX store driving UI state.
viewer.directionalLightTHREE.DirectionalLightPrimary directional light (adjustable).
viewer.ambientLightTHREE.AmbientLightAmbient fill light.
viewer.secureSdkSecureWasmSdkWASM sandbox for premium shader execution.
viewer.authStatusAuthStatus'AUTHENTICATING' | 'AUTHORIZED' | 'UNAUTHORIZED' | 'TAMPERED'

Key Methods

MethodDescription
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 shared dist/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 via addPlugin(). 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.
⚠ Warning
Some plugins are license-gated (Premium). Attaching them before 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

GLTFPlugin
Loads .glb/.gltf files with Draco and Meshopt decompression. Call plugin.loadModel(url) to swap models at runtime.
Free
OrbitControlsPlugin
Mouse/touch orbit, pan, and zoom. Configurable damping, polar angle limits, and auto-rotate.
Free
EnvironmentPlugin
Loads HDR/EXR environment maps for Image-Based Lighting. Includes several built-in env maps from the core assets.
Premium
DiamondPlugin
Physically-based diamond ray-tracing shader. Multi-bounce internal reflections, dispersion, and fire effects.
Premium
SSGIPlugin
Screen-Space Global Illumination for indirect lighting bounces. Significant realism boost for metallic scenes.
Premium
SSRPlugin
Screen-Space Reflections for glossy surfaces. Works best with environment maps.
Premium
ProgressiveShadowPlugin
Accumulates soft ground shadows over multiple frames. Provides studio-quality contact shadows.
Premium
FloorPlugin
Adds a reflective ground plane. Configurable opacity, color, and roughness.
Premium
BloomPlugin
HDR bloom post-processing using Three.js TSL. Configurable threshold, strength, and radius.
Premium
CinematicPlugin
Premium cinematic grading and post-processing. Controls Vignette, Chromatic Aberration, Film Grain, Exposure, Contrast, and Saturation.
Premium
DOFPlugin
Depth of Field (bokeh) post-processing. Configurable focus distance and aperture.
Premium
FaceMocapPlugin
Real-time face tracking via MediaPipe. Drives 3D avatar blend shapes. Supports live video, LiveLink, and debug overlays.
Premium
AnimationPlugin
Plays and controls GLTF animation clips. Supports blend weights, cross-fading, and speed control.
Premium
CenterModelPlugin
Centers and floors a model based on its bounding box. Auto-adjusts camera distance.
Free
TransformGizmoPlugin
Translate / rotate / scale gizmo for selected objects. Keyboard shortcuts: G, R, S.
Premium
MaterialRaycasterPlugin
Click-to-select mesh by raycasting. Fires selection events consumed by MaterialEditor UI.
Premium
MeasurementPlugin
Click two points to measure distance in 3D space. Renders line and label in scene.
Premium
OutlinePlugin
Post-process outline effect for selected objects. Configurable color and thickness.
Premium
ExportPlugin
Exports the scene as PNG screenshot or GLTF file.
Premium
GridHelperPlugin
Toggleable infinite grid overlay for spatial reference.
Free
StatsPlugin
Overlays FPS, draw calls, and memory stats. Uses stats.js internally.
Free
CameraPlugin
Save and recall named camera positions. Smooth tween between viewpoints.
Premium
TourPlugin
Renders 360-degree spherical panoramas and manages interactive room-linking hotspots.
Premium

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.

ℹ Info
All apps share a root 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.

FilePurpose
src/components/ThreeCanvas.tsxEngine init, plugin attachment, auth-gating pattern
src/components/MaterialEditor.tsxPBR material property editor panel
src/pages/DiamondTest.tsxDiamond 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 from public/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-ssl for 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) to dist/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:

  1. Creates a shared dist/assets/ directory.
  2. Rewrites index.html asset references from ./assets/../assets/ so they point to the shared root.
  3. Moves each app's hashed JS/CSS/WASM chunks into the shared dist/assets/ folder.
  4. Copies the root index.html hub page to dist/index.html.

Final dist layout

dist/ ├── index.html ← Hub page ├── assets/ ← All hashed JS/CSS/WASM chunks (shared) │ ├── index-XXX.js │ └── core/ ← Built-in 3D assets copied from packages/core/dist/assets/ ├── draco/ ← Draco decoder binaries (from public/) ├── textures/ ← Fabric/SVG textures (from public/) ├── assets/wasm/ ← MediaPipe WASM (from public/) ├── viewer/ │ └── index.html ← App entry (references ../assets/) ├── sportswear-configurator/ ├── eyewear-tryon/ ├── face-mocap/ ├── jewelry-configurator/ ├── box-configurator/ ├── 360tour/ └── ...

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:

EnvironmentPath UsedResolves 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: GLTFPluginimport.meta.env.DEV ? '/draco/gltf/' : '../draco/gltf/'
  • MediaPipe WASM: FaceMocapPlugin constructor arg — DEV ? '/assets/wasm' : '../assets/wasm'
  • Textures: Sportswear ThreeCanvas — DEV ? '/textures/' : '../textures/'
  • Core assets: ThreeViewer assetBaseUrl — DEV ? '/packages/core/dist/assets/' : '../assets/core/'
✓ Rule of thumb
When adding new static assets: place them in 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

  1. On construction, ThreeViewer checks for a global token provided by the environment (usually via window.trikomi_lic set to a .lic file path or raw string).
  2. If a `.lic` URL is provided, the engine automatically fetches the file's contents.
  3. The engine splits the file into multiple tokens (if a user has purchased multiple module subscriptions).
  4. Each token is securely verified to prevent tampering, ensure domain whitelisting, and perform silent revocation checks.
  5. The engine aggregates all valid token feature bitfields into a master capability matrix.
  6. authStatus transitions: AUTHENTICATING → AUTHORIZED (or UNAUTHORIZED / TAMPERED).
  7. 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());
⚠ Trial Mode
In development (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