@unseenco/theatre-threejs
v0.1.16
Published
Three.js devtools extension for Theatre.js Studio
Readme
@unseenco/theatre-threejs
Three.js helpers and Studio extension for Theatre.js.
Runtime helpers (autoAddObject, autoAddMaterial, autoAddCamera, …) import from the package root and do not load Studio. Studio devtools (buildExtension) import from @unseenco/theatre-threejs/extension.
Usage
import studio from '@unseenco/theatre-studio'
import {autoAddObject} from '@unseenco/theatre-threejs'
import {buildExtension} from '@unseenco/theatre-threejs/extension'
let activeScene = scene1
// Register Three.js objects on Theatre sheets
autoAddObject(mesh, sheet)
const devtools = buildExtension({
renderer,
scenes: [
{scene: scene1, camera: camera1},
{name: 'Scene 2', scene: scene2, camera: camera2},
],
studio,
// Prefer config callbacks to receive persisted restore during init.
onSceneSwitch(_name, scene) {
activeScene = scene
},
onOrbitModeSwitch(enabled) {
// e.g. pause gameplay camera while orbiting
},
})
studio.extend(devtools.extension)
// When the app switches scenes outside the toolbar flyout:
// devtools.switchScene('Scene 2')
// or: devtools.switchScene(1)
function loop() {
requestAnimationFrame(loop)
devtools.update()
renderer.render(activeScene, devtools.getCamera())
}The extension adds a toolbar flyout to switch between scenes (when more than one is configured), a toggle between your scene camera and an OrbitControls dev camera, and orbit-mode tools for camera frustum visualization, line overlays, and interactive transform editing. Use devtools.isOrbitMode() to read the current mode. Pass onOrbitModeSwitch / onSceneSwitch in the buildExtension config to react to persisted restore during init; the returned devtools.onOrbitModeSwitch() / devtools.onSceneSwitch() methods also accept late subscribers but only receive subsequent changes. Call devtools.switchScene(nameOrIndex) when your app changes scenes outside the toolbar so the extension stays in sync; devtools.getActiveSceneName() returns the current scene name.
configureTheatreThreejs
Set project-wide defaults once at startup. Excludes from defaults and per-call autoAddObject options are merged.
import {configureTheatreThreejs} from '@unseenco/theatre-threejs'
configureTheatreThreejs({
autoAddObject: {
exclude: {uniforms: ['uTime']},
},
})autoAddObject
Automatically adds a Three.js Object3D to a Theatre sheet, parsing transform data (position, rotation, scale, visible) and material properties (colors, scalars, vectors, textures, shader uniforms).
import {autoAddObject} from '@unseenco/theatre-threejs'
const sheetObject = autoAddObject(mesh, sheet, {
objectKey: 'My Mesh',
exclude: {transform: ['scale'], material: ['normalMap']},
include: {material: ['color', 'metalness', 'roughness']},
trackMaterial: true, // default when the object has a material
})Shared materials
If two meshes registered with autoAddObject share the same Three.js Material instance, the package auto-splits that material into its own Theatre object under Shared Materials / <material.name>, removes material props from the first mesh, and links both via showPropsOf. Name your materials for stable keys; unnamed materials warn and fall back to a UUID-based key. Use trackMaterial: false to opt out, or call autoAddMaterial first to own the material object yourself.
Static and transient props
When autoAddObject registers props on a Theatre sheet object, most props are static: they are saved in project state, can be keyframed, and reload on refresh. Transform props, material colors/scalars/vectors, and shader uniform numbers all fall in this category.
Transient props are excluded from exported project state JSON. They still appear in Studio for the current session but reset on refresh. autoAddObject registers material texture slots and shader uniform textures as transient image props (persist: false).
Texture props
Material texture slots (map, normalMap, etc.) and shader uniform textures are exposed as transient Theatre image props (see Static and transient props). Assignments apply for the current session only and are cleared on refresh. When you swap a texture, wrap/repeat/filter settings from the existing texture are preserved.
Shader uniform textures are detected by name (uDiffuseMap, tDiffuse, etc.) or by gui: { type: 'texture' } on the uniform.
When used together with buildExtension, selection is synced bidirectionally in orbit mode:
- Click a registered mesh in the viewport to select it in the Theatre outline
- Select an object in the outline to show a
BoxHelperaround the matching mesh
Shader uniform gui options
For ShaderMaterial / RawShaderMaterial, autoAddObject reads optional gui metadata on each uniform and maps it to Theatre number prop options:
| Uniform gui | Theatre types.number() option |
| --- | --- |
| min / max | range: [min, max] |
| step | nudgeMultiplier |
| type: 'texture' | Registers the uniform as a Theatre image prop (useful when the uniform value is null) |
import {ShaderMaterial, Color} from 'three'
import {autoAddObject} from '@unseenco/theatre-threejs'
const material = new ShaderMaterial({
uniforms: {
uColor: {value: new Color(1, 0.4, 0.2)},
uOpacity: {
value: 0.85,
gui: {min: 0, max: 1, step: 0.01},
},
uDiffuseMap: {value: null},
uTime: {value: 0}, // often excluded via configureTheatreThreejs when driven by your render loop
},
// vertexShader / fragmentShader ...
})
autoAddObject(new Mesh(geometry, material), sheet)For Vector2 / Vector3 uniforms, set per-component gui options:
uOffset: {
value: new Vector2(0, 0),
gui: {
x: {min: -1, max: 1, step: 0.01},
y: {min: -1, max: 1, step: 0.01},
},
}If gui is omitted, number uniforms default to nudgeMultiplier: 0.01 with no range.
autoAddMaterial
Register a Three.js Material (or material array) on a Theatre sheet with auto-parsed material properties only — no transforms or Object3D selection sync. Use this when a material is shared across meshes, or when you only want to animate material props.
import {autoAddMaterial} from '@unseenco/theatre-threejs'
autoAddMaterial(mesh.material, sheet, {
objectKey: 'Shared Material',
exclude: {uniforms: ['uTime']},
include: {material: ['color', 'metalness', 'roughness']},
})Material / uniform exclude and include lists merge with configureTheatreThreejs({ autoAddObject }) defaults the same way as autoAddObject. Texture slots are registered as transient image props (see Static and transient props).
autoAddCamera
Register a Three.js Camera on a Theatre sheet with transform props and camera-specific props. Scale is excluded by default (cameras are not meaningfully scaled in Three.js).
import {autoAddCamera} from '@unseenco/theatre-threejs'
autoAddCamera(camera, sheet, {
objectKey: 'Main Camera',
scene, // add camera to scene if it has no parent; required for transform controls
sensorHeight: 24, // mm, for focal length ↔ FOV conversion (default: 24)
exclude: {
transform: ['scale'],
camera: ['zoom'], // focalLength, near, far, zoom
},
})For PerspectiveCamera instances, camera props include focal length (converted from FOV), near, far, and zoom. All camera props are static. An invisible selection hitbox is attached so the camera can be picked in orbit mode when used with buildExtension.
Orbit-mode devtools
When the orbit camera is active, the toolbar exposes three optional helpers. Each toggle persists per scene (along with orbit mode and camera pose).
Camera helper
Shows a CameraHelper frustum for the active scene camera. Useful for comparing the scene camera to the orbit camera while framing a shot.
Line helpers
Reveals scene geometry that would otherwise be invisible:
Line,LineSegments, andLineLoopobjects in the scene graphCatmullRomCurve3instances stored onobject.userData(for exampleuserData.path)
The extension clones matching geometry into cyan overlay helpers; the original lines stay as-is. Helpers are rebuilt when you switch scenes or toggle the tool on.
Transform controls
When enabled and a registered object with transform props is selected in the Theatre outline, Three.js TransformControls appear in the viewport. Dragging the gizmo writes position, rotation, and scale back to the Theatre sheet as an undoable scrub. Orbit controls are disabled while dragging.
The toolbar adds translate / rotate / scale and world / local space switches when an object is attached. The selected object must be in the active scene graph — pass scene to autoAddCamera if your camera is not already parented to a scene.
Persistence
Scene names are taken from the optional name property, then from scene.name on the Three.js Scene instance, then default to "Scene" (with numeric suffixes when needed).
Devtools state (orbit mode, camera position, orbit target, and the camera / line / transform-controls helper toggles) is persisted per scene across page refreshes via Studio sheet objects (Devtools: <scene name>), using non-undoable transactions so it does not pollute the undo history.
