With THREE.js
Animate a Three.js scene by wiring meshes to Backstage sheet objects. This guide follows the classic torus-knot tutorial pattern; package names match craftedbygc/backstage (@unseenco/backstage-*).
Prerequisites
You need a bundler (Vite recommended) and a basic Three.js scene. The upstream sample repo vanilla-threejs-project still works as a starting point.
git clone https://github.com/fulopkovacs/vanilla-threejs-project
cd vanilla-threejs-project
yarn install
yarn devInstall Backstage
yarn add @unseenco/backstage @unseenco/backstage/studioOptional: add @unseenco/backstage/threejs if you want autoAddObject() and the Studio Three.js extension (see Three.js extension).
Initialize Studio
In your entry file (e.g. main.ts):
import studio from '@unseenco/backstage/studio'
studio.initialize()Press Alt/Option + \ to show or hide Studio.
Create a project and sheet
import {getProject, types} from '@unseenco/backstage'
const project = getProject('THREE.js x Backstage')
const sheet = project.sheet('Animated scene')Projects persist in the browser while Studio is open. Export JSON from the outline when you need a portable state file (see Projects).
Bind the mesh
After you create a mesh and add it to the scene:
const torusKnotObj = sheet.object('Torus Knot', {
rotation: types.compound({
x: types.number(mesh.rotation.x, {range: [-2, 2]}),
y: types.number(mesh.rotation.y, {range: [-2, 2]}),
z: types.number(mesh.rotation.z, {range: [-2, 2]}),
}),
})
torusKnotObj.onValuesChange((values) => {
const {x, y, z} = values.rotation
mesh.rotation.set(x * Math.PI, y * Math.PI, z * Math.PI)
})Animate in Studio
- Select the object in the outline.
- In the Details Panel, right-click a prop → Sequence.
- Add keyframes in the Sequence Editor (click the diamond next to a prop at different times).
- Press
Spaceto play.
More detail: Working with sequences.
Production
- Export project state to
state.jsonfrom the outline menu. - Pass it into
getProject:
import projectState from './state.json'
const project = getProject('THREE.js x Backstage', {state: projectState})- Play when ready:
project.ready.then(() => {
sheet.sequence.play({iterationCount: Infinity})
})- Do not call
studio.initialize()in production (gate it withimport.meta.env.DEVor equivalent).
Three.js helper (optional)
Instead of hand-written sheet.object + onValuesChange, you can register meshes with:
import {autoAddObject} from '@unseenco/backstage/threejs'
import extension from '@unseenco/backstage/threejs/extension'
studio.extend(extension)
autoAddObject(mesh, sheet)Use transient / static prop paths when you need session-only or non-sequenced props (see Three.js extension).