Three.js extension
@unseenco/backstage/threejs provides runtime helpers and a Studio extension entry point.
Install
yarn add @unseenco/backstage @unseenco/backstage/studio @unseenco/backstage/threejs threeBackstage Lite: Peer
@unseenco/backstage/core-liteand@unseenco/backstage/studio-liteinstead for static + variant workflows. Runtime helpers and/extensionwork the same; see Three.js with Backstage Lite.
Studio (development)
Import the extension from the /extension subpath so production bundles do not pull Studio:
import studio from '@unseenco/backstage/studio'
import extension from '@unseenco/backstage/threejs/extension'
studio.initialize()
studio.extend(extension({renderer, studio, scenes: [{name: 'Main', scene, camera}]}))Breaking change from upstream Backstage: buildExtension() lives on /extension, not the package root (0.1.8 changelog).
autoAddObject
Registers a THREE.Object3D on a sheet with parsed transform, material, shader uniform, and texture props:
import {autoAddObject, configureBackstageThreejs} from '@unseenco/backstage/threejs'
configureBackstageThreejs({
autoAddObject: {
exclude: ['matrixAutoUpdate'],
transient: ['material.map'], // session-only texture slots
static: ['renderOrder'],
},
})
const sheetObject = autoAddObject(mesh, sheet, {
name: 'Hero mesh',
// Merged with configureBackstageThreejs() defaults (dot or array paths)
transient: ['someSessionFlag'],
static: ['renderOrder'],
})transient/static— same semantics assheet.object()(Objects).- Unit-interval material scalars (
opacity,roughness,metalness, …) use a 0–1 Studio range.
autoAddMaterial
Track a shared Material on its own sheet object (material props only—no mesh transform):
import {autoAddMaterial} from '@unseenco/backstage/threejs'
autoAddMaterial(material, sheet, {name: 'Glass'})Call this before autoAddObject() when you want explicit control over the material object key.
Shared materials
When a second autoAddObject() uses the same Material instance as an earlier mesh:
- Material props move to a dedicated object under
Shared Materials / <name>. - Both meshes link that object via
showPropsOf(showPropsOf). - The first mesh stops applying material props locally.
Unnamed materials warn and fall back to a UUID-based key. Pass trackMaterial: false on autoAddObject() to keep material props on the mesh, or call autoAddMaterial() first to own the shared object.
Playground: /shared/three-basic-vanilla-devtools/ (instanced grid + shared materials).
autoAddCamera
Registers camera transform and lens props (focalLength, near, far, zoom) plus a viewport hitbox for orbit-mode picking.
Scenes and orbit mode
buildExtension() config can register callbacks before persisted state restores:
studio.extend(
extension({
renderer,
studio,
scenes: [{name: 'Main', scene, camera}],
onSceneSwitch: (name) => console.log('active scene', name),
onOrbitModeSwitch: (orbit) => console.log('orbit mode', orbit),
}),
)
// Later, from the returned API:
api.switchScene('Main') // or index
api.getActiveSceneName()
api.isOrbitMode()
const off = api.onSceneSwitch((name) => {})
off() // unsubscribeMulti-scene setups hide sheets that only contain objects in inactive scenes from the outline (updates on scene switch).
Selection sync
With buildExtension() active, clicking a registered mesh in the viewport selects it in the outline; outline selection shows a BoxHelper in orbit mode.
Production
Ship only @unseenco/backstage/threejs runtime imports (autoAddObject, …). Do not import /extension or @unseenco/backstage/studio in production.