Complete reference for all public classes, methods, and properties exported by the three.ws web component bundle (agent-3d.js).
For the HTTP/REST surface, see api-reference.md. For the high-level npm SDK, see sdk.md. For the
<agent-3d>element attributes, see web-component.md.
File: src/app.js
The top-level application controller. Manages user interaction (drag-and-drop, file upload), URL parameter parsing, and orchestrates the Viewer and Validator.
new App(el: Element, location: Location)| Parameter | Type | Description |
|---|---|---|
el |
Element |
Root DOM element (typically document.body) |
location |
Location |
Browser location object for hash parameter parsing |
Parses the following URL hash parameters via query-string:
| Hash Param | Type | Default | Description |
|---|---|---|---|
model |
string |
'/avatars/cz.glb' |
URL of a glTF/GLB model to load |
kiosk |
boolean |
false |
Hides header; intended for iframe embedding |
preset |
string |
'' |
'assetgenerator' activates asset generator mode |
cameraPosition |
string |
null |
Comma-separated x,y,z camera coordinates |
| Property | Type | Description |
|---|---|---|
el |
Element |
Root DOM element |
viewer |
Viewer | null |
Viewer instance (created on first model load) |
viewerEl |
Element | null |
Viewer container element |
spinnerEl |
Element |
Loading spinner element |
dropEl |
Element |
Drop target element (.wrap) |
inputEl |
Element |
Hidden file input element |
viewerContainerEl |
Element |
Container for the 3D viewport |
validator |
Validator |
Validator instance |
options |
object |
Parsed URL hash options |
Sets up the simple-dropzone controller on the .wrap element. Listens for:
drop→ callsthis.load(files)dropstart→ shows spinnerdroperror→ hides spinner
Instantiates a new Viewer on the #viewer-container element. Called once on first model load; subsequent loads reuse the same viewer.
Processes a fileset from a drag-and-drop or file input event.
- Iterates the
fileMapto find the root.gltfor.glbfile - Extracts the
rootPath(directory portion of the file path) - Calls
this.view(rootFile, rootPath, fileMap) - Shows an alert if no
.gltf/.glbfile is found
Loads a model into the viewer and runs validation.
| Parameter | Type | Description |
|---|---|---|
rootFile |
File | string |
The root glTF/GLB file object or URL string |
rootPath |
string |
Directory prefix for resolving relative URIs |
fileMap |
Map<string, File> |
All files from the drop (for multi-file glTF) |
Flow:
- Clears any existing scene from the viewer
- Creates a blob URL if
rootFileis aFileobject - Calls
Viewer.load()to render the model - Calls
Validator.validate()(unless in kiosk mode) - Revokes the blob URL in the cleanup callback
Normalizes and displays an error message via window.alert(). Special case handling:
| Pattern | Displayed Message |
|---|---|
Contains ProgressEvent |
"Unable to retrieve this file..." |
Contains Unexpected token |
"Unable to parse file content..." |
error.target instanceof Image |
"Missing texture: {filename}" |
Toggle the loading spinner's display style.
File: src/viewer.js
The Three.js rendering engine. Manages the WebGL scene, camera, renderer, controls, lighting, environment maps, animations, morph targets, and the dat.gui panel.
new Viewer(el: Element, options: object)| Parameter | Type | Description |
|---|---|---|
el |
Element |
Container element for the WebGL canvas |
options |
object |
Options from App (kiosk, preset, cameraPosition) |
The constructor:
- Creates
WebGLRenderer,PerspectiveCamera,Scene,OrbitControls - Initializes
PMREMGeneratorfor environment map processing - Creates the neutral environment from
THREE.RoomEnvironment - Sets up the axes helper (mini viewport in bottom-left)
- Builds the dat.gui panel
- Starts the render loop via
requestAnimationFrame - Binds the
resizeevent
| Property | Type | Description |
|---|---|---|
el |
Element |
Container element |
options |
object |
Configuration options |
scene |
THREE.Scene |
The Three.js scene |
defaultCamera |
THREE.PerspectiveCamera |
Default orbit camera |
activeCamera |
THREE.Camera |
Currently active camera (default or embedded) |
renderer |
THREE.WebGLRenderer |
WebGL renderer (also on window.renderer) |
controls |
OrbitControls |
Orbit controls instance |
content |
THREE.Object3D | null |
Currently loaded model root |
mixer |
THREE.AnimationMixer | null |
Animation mixer |
clips |
THREE.AnimationClip[] |
Animation clips from the loaded model |
lights |
THREE.Light[] |
App-provided lights (ambient + directional) |
gui |
GUI |
dat.gui instance |
state |
object |
Current GUI state (see State Object below) |
stats |
Stats |
stats.js performance monitor |
backgroundColor |
THREE.Color |
Current background color |
pmremGenerator |
THREE.PMREMGenerator |
Environment map processor |
neutralEnvironment |
THREE.Texture |
Pre-computed neutral environment |
skeletonHelpers |
THREE.SkeletonHelper[] |
Active skeleton overlays |
gridHelper |
THREE.GridHelper | null |
Grid overlay |
axesHelper |
THREE.AxesHelper | null |
Axes overlay in main scene |
axesScene |
THREE.Scene |
Mini axes viewport scene |
axesCamera |
THREE.PerspectiveCamera |
Mini axes viewport camera |
axesRenderer |
THREE.WebGLRenderer |
Mini axes viewport renderer |
axesCorner |
THREE.AxesHelper |
Axes object in mini viewport |
The this.state object holds all GUI-controllable values:
{
environment: 'Neutral', // Environment map name
background: false, // Show environment as background
playbackSpeed: 1.0, // Animation playback speed (0–1)
actionStates: {}, // Per-clip play state { clipName: bool }
camera: '[default]', // Active camera name
wireframe: false, // Wireframe rendering
skeleton: false, // Skeleton helper visibility
grid: false, // Grid + axes visibility
autoRotate: false, // Auto-rotate orbit
// Lighting
punctualLights: true, // App-provided lights enabled
exposure: 0.0, // Exposure compensation (EV)
toneMapping: LinearToneMapping, // Tone mapping mode
ambientIntensity: 0.3, // Ambient light intensity
ambientColor: '#FFFFFF', // Ambient light color
directIntensity: 0.8 * Math.PI, // Directional light intensity
directColor: '#FFFFFF', // Directional light color
bgColor: '#191919', // Background color
pointSize: 1.0, // Point cloud vertex size
}Main render loop callback. Called every frame via requestAnimationFrame.
- Updates OrbitControls
- Updates stats.js
- Advances AnimationMixer by delta time
- Calls
this.render()
Renders the main scene and the axes helper mini-viewport.
Handles window resize. Updates camera aspect ratio, renderer size, and axes renderer size.
Loads a glTF/GLB model.
| Parameter | Type | Description |
|---|---|---|
url |
string |
URL to the model file (or blob URL) |
rootPath |
string |
Directory prefix for resolving relative URIs |
assetMap |
Map<string, File> |
Dropped files for local resource resolution |
Returns a Promise that resolves with the parsed glTF object. The method:
- Installs a URL modifier on the
LoadingManagerto intercept relative URIs and serve them fromassetMapas blob URLs - Configures
GLTFLoaderwith Draco, KTX2, and Meshopt decoders - On success, calls
setContent()with the scene and animation clips - Exports the raw glTF JSON to
window.VIEWER.json
Adds a loaded model to the scene.
- Calls
this.clear()to remove any existing model - Computes bounding box and centers the model at origin
- Configures camera near/far planes and position based on model size
- Respects
options.cameraPositionif provided - Saves initial OrbitControls state
- Detects embedded lights (sets
state.punctualLights = falseif found) - Sets up animation clips via
setClips() - Updates lighting, GUI, environment, and display
- Prints the scene graph to the console
- Exports the scene to
window.VIEWER.scene
Replaces the current animation clips. Stops and disposes the existing AnimationMixer if one exists, then creates a new one.
Plays all animation clips simultaneously by calling mixer.clipAction(clip).reset().play() on each.
Switches the active camera.
| Value | Behavior |
|---|---|
'[default]' |
Activates the orbit camera; enables OrbitControls |
| Any other string | Traverses the scene for a camera with node.name === name; disables OrbitControls |
Synchronizes light state with the GUI:
- Adds or removes punctual lights based on
state.punctualLights - Sets
renderer.toneMappingandrenderer.toneMappingExposure - Updates ambient/directional light intensity and color
Creates and adds the ambient + directional light pair (or a single hemisphere light in asset generator mode).
Removes all app-provided lights from the scene and empties the lights array.
Loads the selected environment map and applies it to scene.environment. If state.background is true, also sets it as scene.background.
Processes an environment definition into a usable environment map:
| Environment ID | Processing |
|---|---|
'neutral' |
Returns pre-computed RoomEnvironment texture |
'' |
Returns null (no environment) |
| Any other | Loads .exr via EXRLoader, processes through PMREMGenerator |
Applies display state changes:
- Wireframe mode on all materials
- Point size for
PointsMaterial - Skeleton helpers on skinned meshes
- Grid + axes helpers
- Auto-rotate on OrbitControls
Updates backgroundColor from state.bgColor.
Creates the mini axes viewport:
- 100×100 px
<div>in the bottom-left corner - Separate
WebGLRendererwith transparent background AxesHelper(5)scaled to match the loaded model
Builds the entire dat.gui panel. On mobile (≤ 700 px), uses 220 px width and starts closed; on desktop, 260 px width and starts open.
See Architecture overview for the full folder tree.
Rebuilds the dynamic GUI folders (Animation, Morph Targets, Cameras) based on the currently loaded model. Called by setContent().
- Removes all previous dynamic controls
- Traverses the model to discover morph target meshes and embedded cameras
- Auto-plays the first animation clip
- Creates per-clip checkboxes and per-morph-target sliders
Removes the current model from the scene and disposes all resources:
- Disposes all geometries
- Disposes all textures (except
envMap) - Disposes all materials
Recursively prints the scene graph to the console using nested console.group() calls. Each line shows <NodeType> name.
File: src/validator.js
Integrates the Khronos glTF-Validator and renders validation results.
new Validator(el: Element)| Parameter | Type | Description |
|---|---|---|
el |
Element |
Root DOM element for appending the toggle bar |
| Property | Type | Description |
|---|---|---|
el |
Element |
Root DOM element |
report |
object | null |
Last validation report |
toggleEl |
Element |
Container for the toggle bar |
validate(rootFile: string, rootPath: string, assetMap: Map<string, File>, response: object) → Promise
Runs the glTF validator against a loaded model.
| Parameter | Type | Description |
|---|---|---|
rootFile |
string |
URL of the model file |
rootPath |
string |
Directory prefix for resolving relative URIs |
assetMap |
Map<string, File> |
Dropped files for local resource resolution |
response |
object |
The parsed GLTF object from GLTFLoader |
Flow:
- Fetches the model URL as an
ArrayBuffer - Calls
validateBytes()fromgltf-validator - Provides
externalResourceFunctionfor resolving external resources - Passes the result to
setReport()
resolveExternalResource(uri: string, rootFile: string, rootPath: string, assetMap: Map<string, File>) → Promise<Uint8Array>
Resolves an external resource (texture, bin) referenced by the glTF during validation.
- Normalizes the URI by decoding and removing the base URL
- Checks
assetMapfor a local match → creates blob URL - Falls back to a network fetch
- Returns the resource as a
Uint8Array
Processes the raw validator report:
- Extracts the generator string from
report.info.generator - Determines
maxSeverity(lowest severity index with > 0 messages) - Splits messages into
errors[],warnings[],infos[],hints[] - Aggregates high-frequency messages (
ACCESSOR_NON_UNIT,ACCESSOR_ANIMATION_INPUT_NON_INCREASING) - Extracts
asset.extrasmetadata (author, license, source, title) from the GLTF response - Renders
ValidatorToggleHTML into the toggle element
Extracts metadata from the glTF asset.extras field:
| Extra | Processing |
|---|---|
author |
HTML-escaped then linkified |
license |
HTML-escaped then linkified |
source |
HTML-escaped then linkified |
title |
Stored as-is |
Called when validation fails. Sets report to null and renders an error message in the toggle.
Binds click event on the toggle bar to open the lightbox report, and the close button to dismiss it.
Add/remove the hidden CSS class on the toggle element.
Opens a new browser tab with the full validation report HTML (rendered via ValidatorReport component).
All components are pure functions that return HTML strings via vhtml JSX.
File: src/components/footer.jsx
Returns a <footer> with:
- X (Twitter) link to @nichxbt
- "help & feedback" link to GitHub issues
- GitHub repository link
- Pipe separators
No props.
File: src/components/validator-toggle.jsx
Renders the validation status bar.
| Prop | Type | Description |
|---|---|---|
issues |
object | undefined |
Validation issues summary |
reportError |
Error | undefined |
Error if validation failed to run |
Message logic:
numErrors > 0→ "X errors."numWarnings > 0→ "X warnings."numHints > 0→ "X hints."numInfos > 0→ "X notes."- Otherwise → "Model details"
CSS class level-{maxSeverity} controls the color (0 = red, 1 = yellow).
File: src/components/validator-report.jsx
Renders the full validation report.
| Prop | Type | Description |
|---|---|---|
info |
object |
Model info (version, generator, counts, extensions, extras) |
validatorVersion |
string |
glTF-Validator version |
issues |
object |
Issues summary with counts |
errors |
array |
Error messages |
warnings |
array |
Warning messages |
hints |
array |
Hint messages |
infos |
array |
Info messages |
Displays:
- Format version and generator
- Metadata from
asset.extras(title, author, license, source) - Stats: draw calls, animations, materials, vertices, triangles
- Extensions used
- Issue tables (one
ValidatorTableper severity level with messages)
File: src/components/validator-table.jsx
Renders a color-coded table of validation issues.
| Prop | Type | Description |
|---|---|---|
title |
string |
Severity title ("Error", "Warning", "Hint", "Info") |
color |
string |
Header background color (CSS color string) |
messages |
array |
Array of { code, message, pointer } objects |
Columns: Code, Message, Pointer (JSON pointer into the glTF).
File: src/environments.js
Exports environments — an array of environment map definitions.
interface Environment {
id: string; // Unique identifier ('' for none, 'neutral' for RoomEnvironment)
name: string; // Display name in the GUI dropdown
path: string | null; // URL to the EXR file (null for procedural/none)
format?: string; // File format (e.g., '.exr')
}| ID | Name | Source |
|---|---|---|
'' |
None | No environment map |
neutral |
Neutral | THREE.RoomEnvironment (procedural) |
venice-sunset |
Venice Sunset | venice_sunset_1k.exr from GCS |
footprint-court |
Footprint Court (HDR Labs) | footprint_court_2k.exr from GCS |
Append to the environments array in src/environments.js:
{
id: 'my-studio',
name: 'My Studio',
path: 'https://your-cdn.com/studio_1k.exr',
format: '.exr',
}Requirements:
- Must be an equirectangular HDR image in
.exrformat - 1K resolution (1024×512) is recommended for web delivery
- Must be served with CORS headers allowing the app's origin
The app exports debugging state to window.VIEWER:
| Property | Type | Set By | Description |
|---|---|---|---|
window.VIEWER.app |
App |
app.js at DOMContentLoaded |
Full App instance |
window.VIEWER.scene |
THREE.Object3D |
Viewer.setContent() |
Current model scene graph |
window.VIEWER.json |
GLTF |
Viewer.load() |
Raw parsed glTF object from GLTFLoader |
The WebGL renderer is also available at window.renderer.
// List all meshes
window.VIEWER.scene.traverse((n) => n.isMesh && console.log(n.name, n));
// Get renderer stats
window.VIEWER.app.viewer.renderer.info;
// Get current camera position
window.VIEWER.app.viewer.defaultCamera.position.toArray();
// Force play animation clip by index
window.VIEWER.app.viewer.mixer.clipAction(window.VIEWER.app.viewer.clips[0]).play();
// Change background color programmatically
window.VIEWER.app.viewer.state.bgColor = '#ff0000';
window.VIEWER.app.viewer.updateBackground();
// Toggle wireframe
window.VIEWER.app.viewer.state.wireframe = true;
window.VIEWER.app.viewer.updateDisplay();