
Theatrejs
- 1 installs
- 2 repo stars
- Updated August 3, 2026
- fandhe-ai/agent-reference-skills
Reference the Theatre.js animation toolkit for @theatre/core, studio, r3f, and dataverse covering projects, sheets, sequences, keyframes, and React Three Fiber integration.
About
A structured reference for Theatre.js covering core, studio, r3f, and dataverse packages, keyframe animation, audio sync, state JSON, and React Three Fiber integration. A frontend developer loads it when building sequenced animations.
- getProject, Sheet, Sequence, Object, and prop-types coverage
- React Three Fiber integration via editable and useCurrentSheet
Theatrejs by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,364 of 1,877 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/fandhe-ai/agent-reference-skills --skill theatrejsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 3, 2026 |
| Repository | fandhe-ai/agent-reference-skills ↗ |
What it does
Reference the Theatre.js animation toolkit for @theatre/core, studio, r3f, and dataverse covering projects, sheets, sequences, keyframes, and React Three Fiber integration.
Files
ディレクトリ構成
skills/theatrejs/
SKILL.md
references/
concepts/
README.md
overview.md
getting-started.md
project.md
sheet.md
sheet-object.md
sequence.md
prop-types.md
studio.md
state.md
core/
README.md
get-project.md
project.md
sheet.md
sequence.md
object.md
types.md
val.md
on-change.md
dataverse.md
studio/
README.md
studio-initialize.md
studio-extend.md
studio-transaction.md
studio-scrub.md
studio-selection.md
studio-create-pane.md
studio-get-studio-project.md
studio-create-content-of-save-file.md
studio-ui.md
keyboard-mouse-controls.md
authoring-extensions.md
r3f/
README.md
SheetProvider.md
editable.md
PerspectiveCamera.md
OrthographicCamera.md
useCurrentSheet.md
refreshSnapshot.md
extension.md
useVal.md
usePrism.md
useAtom.md
manual/
README.md
projects.md
sheets.md
objects.md
prop-types.md
sequences.md
assets.md
audio.md
advanced.md
samples/
README.md
basic-animation-setup.md
react-three-fiber-integration.md
audio-sync.md
studio-editing-workflow.md
html-dom-animation.md
scripts/
README.md
install.md
setup.md
animation.md
studio.md
r3f.md
theatric.md探索手順
タスクからカテゴリを引き、カテゴリの README.md で目的のページを特定する:
1. 下記マッピング表でタスクに対応するカテゴリを探す 2. そのカテゴリの references/{category}/README.md を参照して目的のページを特定する 3. 該当ページの .md を Read して詳細を確認する
タスク → カテゴリ マッピング
| タスク | カテゴリ | 参照 README |
|---|---|---|
| Theatre.js の概念・全体像を把握したい | concepts | references/concepts/README.md |
| セットアップ手順・はじめての実装を知りたい | concepts | references/concepts/README.md |
| Project / Sheet / Sequence / Object の概念を理解したい | concepts | references/concepts/README.md |
| state JSON の構造・シリアライズを知りたい | concepts | references/concepts/README.md |
| getProject でプロジェクトを作成・取得したい | core | references/core/README.md |
| Sheet / Sequence / Object の API シグネチャを調べたい | core | references/core/README.md |
| prop-types(number / rgba / compound / image)を定義したい | core | references/core/README.md |
| val / onChange でポインター値を読み取り・購読したい | core | references/core/README.md |
| @theatre/dataverse の Atom / prism / Ticker を使いたい | core | references/core/README.md |
| Studio を初期化・拡張したい | studio | references/studio/README.md |
| Studio でトランザクション・スクラブ操作をしたい | studio | references/studio/README.md |
| Studio の選択状態・カスタムペインを操作したい | studio | references/studio/README.md |
| Studio の保存ファイルをエクスポート・カスタム永続化したい | studio | references/studio/README.md |
| Studio のキーボード・マウス操作を調べたい | studio | references/studio/README.md |
| Studio 拡張機能(ツールバー・ペイン)を作成したい | studio | references/studio/README.md |
| React Three Fiber シーンをアニメートしたい | r3f | references/r3f/README.md |
| SheetProvider / editable でオブジェクトを編集可能にしたい | r3f | references/r3f/README.md |
| useCurrentSheet / useVal / usePrism / useAtom を使いたい | r3f | references/r3f/README.md |
| R3F カメラ(PerspectiveCamera / OrthographicCamera)をアニメートしたい | r3f | references/r3f/README.md |
| プロジェクト・シート・オブジェクトの手動設定手順を知りたい | manual | references/manual/README.md |
| シーケンス再生・ループ・ポジション制御を知りたい | manual | references/manual/README.md |
| アセット(画像・テクスチャ)を props に使いたい | manual | references/manual/README.md |
| 音声をアニメーションと同期したい | manual | references/manual/README.md |
| カスタム rafDriver で外部レンダーループと連携したい | manual | references/manual/README.md |
| 典型的な使い方を知りたい | samples | samples/README.md |
| インストール・CLI コマンドを知りたい | scripts | scripts/README.md |
Getting Started
Minimal setup to author and play back a Theatre.js animation in a browser without a bundler (CDN approach). The same Project / Sheet / Object / onValuesChange pattern applies to all integrations.
Signature / Usage
<script type="module">
// 1. Import core + studio bundle from CDN (development)
import {
getProject,
types,
} from 'https://cdn.jsdelivr.net/npm/@theatre/browser-bundles@0.5.0-insiders.88df1ef/dist/core-and-studio.js'
import studio from '@theatre/studio'
studio.initialize()
// 2. Create the Project → Sheet → Object hierarchy
const project = getProject('My Animation')
const sheet = project.sheet('Scene')
const box = document.getElementById('box')
const obj = sheet.object('Box', {
y: types.number(0, { range: [-200, 200] }),
opacity: types.number(1, { range: [0, 1] }),
})
// 3. React to value changes and apply to DOM
obj.onValuesChange(({ y, opacity }) => {
box.style.transform = `translateY(${y}px)`
box.style.opacity = String(opacity)
})
</script>Production — after exporting the state JSON via Studio:
import { getProject } from '@theatre/core' // core-only, no Studio
import projectState from './state.json'
const project = getProject('My Animation', { state: projectState })
project.ready.then(() =>
project.sheet('Scene').sequence.play({ iterationCount: Infinity })
)Notes
- Three.js and React Three Fiber have dedicated getting-started guides with framework-specific idioms
- The core-only bundle (
@theatre/core) is used in production; the combined bundle (core-and-studio) is for development only obj.onValuesChange()fires on every tick while the Sequence is playing and also fires once immediately with current values
Related
- Overview
- Project
- Sheet
- Sheet Object
- State
Overview
Theatre.js is an animation library with a professional-grade motion design toolset. It bridges the gap between designer and developer by providing a visual Studio editor for authoring animations and a lightweight runtime for playing them back in production.
Core Idea
Theatre.js decouples animation authoring (done in Studio at development time) from animation playback (done by @theatre/core at runtime). Once animations are authored, the Studio is removed and only the core runtime ships to production.
Signature / Usage
import { getProject } from '@theatre/core'
import studio from '@theatre/studio'
// Development: enable the Studio editor
studio.initialize()
// Create a project (auto-saves to localStorage while Studio is active)
const project = getProject('My Project')
const sheet = project.sheet('Animated Scene')
const obj = sheet.object('Box', { x: 0, opacity: 1 })
// Sync Theatre.js values to your rendering
obj.onValuesChange((values) => {
box.style.transform = `translateX(${values.x}px)`
box.style.opacity = String(values.opacity)
})
// Production: remove studio.initialize(), load exported state, and play
// const project = getProject('My Project', { state: projectState })
// project.ready.then(() => sheet.sequence.play())Notes
- Works with any front-end framework — dedicated integrations exist for Three.js, React Three Fiber, and HTML/SVG
- The Studio UI is toggled with
Alt/Option + \ - Animation state is exported as a JSON file and embedded at runtime for production
Related
- Project
- Sheet
- Sheet Object
- Sequence
- Studio
- State
Project
The top-level container for all Theatre.js work. A project groups related sheets and their objects together and owns the persisted animation state.
Signature / Usage
import { getProject } from '@theatre/core'
// Create or retrieve a project by name
const project = getProject('My Project')
// Load with previously exported state (production pattern)
import projectState from './state.json'
const project = getProject('My Project', { state: projectState })
// Wait until state is fully loaded before playing
project.ready.then(() => sheet.sequence.play({ iterationCount: Infinity }))Options / Props
getProject(name, config?) accepts:
| Name | Type | Description |
|---|---|---|
name | string | Unique project identifier; used as the key in localStorage |
config.state | object | Previously exported JSON state to initialise the project |
Notes
- Calling
getProject()with the same name returns the existing instance (idempotent) - While Studio is active, state is automatically persisted to
localStorage - Export state via Studio UI ("Export" button); embed the resulting JSON at runtime
project.readyis aPromise;project.isReadyis a synchronous boolean- Multiple projects can coexist on a single page, but one is usually sufficient
Related
- Sheet
- State
- Overview
Prop Types
Typed descriptors for Sheet Object properties. Explicit types unlock specialised Studio editing widgets (sliders, colour pickers, dropdowns) and correct keyframe interpolation behaviour.
Signature / Usage
import { types } from '@theatre/core'
const obj = sheet.object('Torus Knot', {
// Compound groups related scalar props
rotation: types.compound({
x: types.number(0, { range: [-2, 2] }),
y: types.number(0, { range: [-2, 2] }),
z: types.number(0, { range: [-2, 2] }),
}),
color: types.rgba({ r: 1, g: 0.5, b: 0.2, a: 1 }),
label: types.string('Hello'),
visible: types.boolean(true),
blendMode: types.stringLiteral('normal', {
normal: 'Normal',
multiply: 'Multiply',
screen: 'Screen',
}),
})Options / Props
| Type | Initial value | Notable options | Studio widget |
|---|---|---|---|
types.number(value, opts?) | number | range: [min, max], nudgeMultiplier | Slider |
types.compound(props) | object literal | — | Collapsible group |
types.boolean(value) | boolean | true/false labels | Toggle |
types.string(value) | string | — | Text input |
types.stringLiteral(value, options) | string | { value: label } map, `as: 'menu' \ | 'switch'` |
types.rgba(value) | { r, g, b, a } | — | Colour picker |
types.image(value?) | asset handle | — | Asset picker |
Notes
- Props without an explicit
types.*wrapper are typed by inference from their initial value (number →types.number, boolean →types.boolean, etc.) types.compound()nests props into a collapsible group in the Details Panel; compound props are sequenced as a unittypes.rgba()accepts shorthand hex strings (#RGB,#RRGGBB,#RRGGBBAA) in the Studio UItypes.image()returns an asset handle; callproject.getAssetUrl(handle)to get a usable URL
Related
- Sheet Object
- Sequence
concepts
| Name | Description | Path |
|---|---|---|
| Overview | What Theatre.js is, Studio-vs-runtime split, and the core development pattern | overview.md |
| Getting Started | Minimal CDN-based setup: Project → Sheet → Object → onValuesChange → production playback | getting-started.md |
| Project | Top-level container that owns all sheets and the persisted animation state | project.md |
| Sheet | Container grouping Sheet Objects onto a shared Sequence timeline; supports independent instances | sheet.md |
| Sheet Object | Animated entity with typed props; values are driven by the Sequence and applied via onValuesChange | sheet-object.md |
| Sequence | The timeline attached to a Sheet; controls keyframes, playback, looping, and audio sync | sequence.md |
| Prop Types | Typed property descriptors (number, compound, boolean, string, rgba, image) for Sheet Objects | prop-types.md |
| Studio | Visual editor for authoring animations at dev time; zero production footprint | studio.md |
| State | JSON serialisation of all keyframes and tweaks; the bridge between Studio authoring and production | state.md |
Sequence
The timeline attached to every Sheet. It stores all keyframes for all objects in the sheet and controls playback position.
Signature / Usage
// Play from current position
sheet.sequence.play()
// Play from the beginning, looping indefinitely
sheet.sequence.play({ iterationCount: Infinity, range: [0, 3] })
// Attach audio to sync playback
sheet.sequence.attachAudio({ source: audioBuffer })
// Programmatically scrub to a specific time (seconds)
sheet.sequence.position = 1.5Options / Props
sheet.sequence.play(config?):
| Name | Type | Description |
|---|---|---|
iterationCount | number | Number of times to repeat; Infinity for looping |
range | [number, number] | Start and end time in seconds |
rate | number | Playback speed multiplier (default 1) |
direction | `'normal' \ | 'reverse' \ |
Notes
sheet.sequence.play()returns aPromisethat resolves when playback endssheet.sequence.pause()pauses at the current positionsheet.sequence.positionis a readable/writable number (seconds)- Keyframes are edited visually in Studio's Sequence Editor (Dope Sheet view)
- The Focus Range (
Shift+drag in Studio) isolates a section of the timeline for editing - Prop values between keyframes are interpolated via the Tween Editor (CSS-style easing curves)
Related
- Sheet
- Sheet Object
- Studio
Sheet Object
Represents everything that is animated in Theatre.js — a Three.js mesh, a DOM element, or a purely virtual entity. Each object holds typed props whose values are driven by the Sequence timeline.
Signature / Usage
import { types } from '@theatre/core'
// Create an object with typed props
const obj = sheet.object('Box', {
position: { x: 0, y: 0, z: 0 },
opacity: types.number(1, { range: [0, 1] }),
})
// Subscribe to value changes and apply them to your scene
obj.onValuesChange((values) => {
mesh.position.set(values.position.x, values.position.y, values.position.z)
mesh.material.opacity = values.opacity
})Notes
- Calling
sheet.object()with an existing key returns the same instance (idempotent) - Namespacing: use
/in the key (e.g.'Scene / Boxes / box-1') to organise objects into collapsible groups in the Studio Outline Panel - Reconfiguration (v0.5.1+): pass
{ reconfigure: true }as the third argument to add or remove props without a page reload - Detachment (v0.5.1+): call
obj.detach()to remove the object while preserving its stored prop values; recreating with the same key restores them - Props without an explicit
types.*wrapper default to the type inferred from their initial value
Related
- Sheet
- Sequence
- Prop Types
Sheet
A container that groups one or more Sheet Objects that can be animated together. Each sheet has its own Sequence (timeline), which can be played independently.
Signature / Usage
// Create or retrieve a sheet within a project
const sheet = project.sheet('Animated Scene')
// Sheet instances — independent copies of the same animation template
const submitBtn = project.sheet('Button', 'Submit')
const cancelBtn = project.sheet('Button', 'Cancel')
// Play the sheet's sequence
sheet.sequence.play()Options / Props
project.sheet(sheetName, instanceId?):
| Name | Type | Description |
|---|---|---|
sheetName | string | Name of the sheet template |
instanceId | string | Optional identifier; creates an independent instance of the same template |
Notes
- Calling
project.sheet()with an existing name returns the same sheet (idempotent) - Sheet instances share the same authored keyframe structure but each maintains independent playback position and prop values
- Access the animation timeline via
sheet.sequence - Add animated objects to a sheet via
sheet.object()
Related
- Project
- Sheet Object
- Sequence
State
The project state is a plain JSON object that captures all keyframes, prop values, and tweaks authored in Studio. It is the serialisation format Theatre.js uses to move animations from development into production.
Signature / Usage
// Development: state is auto-saved to localStorage by Studio
studio.initialize()
const project = getProject('My Project') // reads/writes localStorage
// Export state via Studio UI → saves state.json
// Production: embed the exported JSON and pass it to getProject()
import projectState from './state.json'
const project = getProject('My Project', { state: projectState })
// Defer animation start until state is fully loaded
project.ready.then(() => {
sheet.sequence.play({ iterationCount: Infinity })
})Notes
- State is keyed by project name — the name passed to
getProject()must match the exported state's project name exactly - While Studio is active, state is stored in
localStorageautomatically; no manual save needed during development - Exporting via the Studio UI produces a JSON file; commit it to source control alongside your code
project.readyis a Promise that resolves once the state is hydrated; always await it before callingsequence.play()in productionproject.isReadyprovides a synchronous boolean check when a Promise is inconvenient- State JSON contains only data — no code — so it is safe to commit and diff in version control
Related
- Project
- Studio
- Overview
Studio
Theatre.js's visual editor for authoring animations at development time. The Studio is a separate package (@theatre/studio) that is removed for production builds — it has zero impact on the production bundle.
Signature / Usage
import studio from '@theatre/studio'
// Enable the Studio UI (call once, before creating any projects)
studio.initialize()Studio UI Panels
| Panel | Purpose |
|---|---|
| Outline Panel | Scene hierarchy: Projects → Sheets → Namespaces → Objects |
| Details Panel | View and edit all props of the selected Sheet Object |
| Sequence Editor | Dope Sheet / curve editor for keyframes |
| Global Toolbar | Extension buttons and global controls |
| Extension Panes | Custom windows registered by extensions (e.g. React Three Fiber gizmos) |
Notes
- Toggle Studio visibility with
Alt/Option + \ - The Studio only renders when
studio.initialize()is called; omit it in production - While the Studio is active, project state is automatically saved to
localStorage - Export the final state as JSON via the Studio UI; embed it in your production code via
getProject('Name', { state }) - Extensions (e.g.
@theatre/r3f) can register additional gizmos and toolbar buttons
Related
- Project
- State
- Overview
@theatre/dataverse
The reactive dataflow library Theatre.js is built on. Inspired by functional reactive programming, optimised for interactivity and animation.
Signature / Usage
import { Atom, prism, val, onChange } from '@theatre/dataverse'
// Create a reactive atom
const atom = new Atom({ intensity: 0.5 })
// Derive a computed value
const doubled = prism(() => val(atom.pointer.intensity) * 2)
// Subscribe to changes
onChange(atom.pointer.intensity, (v) => {
console.log('intensity changed to', v)
})
// Update state
atom.setByPointer(atom.pointer.intensity, 0.8)API Overview
Atom
Mutable state container. Access properties via atom.pointer.<prop>.
| Member | Description |
|---|---|
new Atom(initialState) | Creates an atom with initial state |
atom.pointer | Root pointer for type-safe property access |
atom.setByPointer(pointer, value) | Writes a value at the given pointer |
atom.get() | Returns current full state snapshot |
prism(fn)
Creates a derived, reactive computation. Re-evaluates when its dependencies change.
| Member | Description |
|---|---|
prism(fn) | Creates a Prism<T> from a function |
prism.getValue() | Reads the current derived value |
prism.source(fn, cleanup) | Reacts to external (non-reactive) sources |
prism.memo(key, fn, deps) | Memoizes a computation inside a prism |
prism.effect(key, fn) | Runs a side-effect when deps change |
prism.ref(key, init) | Persistent ref scoped to the prism |
val(pointer) / onChange(pointer, callback)
Same semantics as the @theatre/core re-exports. See val and onChange.
Ticker
Schedules batched computations outside React's render cycle. Used by Theatre.js internally to synchronise animation updates.
Notes
@theatre/dataverseis a lower-level primitive; most Theatre.js users interact with it only throughval()andonChange()re-exported from@theatre/core- Full API documentation is maintained on the GitHub repository
- Pointers are type-safe property paths; they are stable (referentially equal) across renders
Related
- val
- onChange
- Object
getProject
Creates or retrieves a Theatre.js Project by ID. If a project with the given ID already exists, it is returned rather than created anew.
Signature / Usage
import { getProject } from '@theatre/core'
const project = getProject(id: string, config?: ProjectConfig): Project// Without Studio (production): pass saved state
import state from './state.json'
const project = getProject('My Project', { state })
// With Studio: state is managed automatically
const project = getProject('My Project')Options / Props
| Name | Type | Default | Description |
|---|---|---|---|
id | string | — | Unique project identifier |
config.state | object | undefined | Saved project state JSON (required in production without Studio) |
Notes
- Project IDs must be unique per application; reusing an ID returns the same instance
- In production (without
@theatre/studio), always passconfig.statewith exported state, otherwise animations have no keyframe data project.readyis a Promise that resolves once the project finishes loading its state
Related
- Project
- Sheet
Object (Sheet Object)
An animatable or tweak-able entity on a Sheet with typed props. Created via sheet.object().
Signature / Usage
const obj = sheet.object(
key: string,
config: Props,
options?: { reconfigure?: boolean }
): ISheetObject
// Read current values
console.log(obj.value.position.x)
// Subscribe to all prop changes
const unsubscribe = obj.onValuesChange((values) => {
mesh.position.x = values.position.x
})
// Unsubscribe when done
unsubscribe()Options / Props
| Name | Type | Description |
|---|---|---|
key | string | Unique key for this object within the sheet |
config | Props | Plain object or typed props using types.* |
options.reconfigure | boolean | If true, updates the prop config of an existing object |
Object properties
| Name | Type | Description |
|---|---|---|
obj.value | Values | Current snapshot of all prop values |
obj.props | Pointer<Props> | Reactive pointer to the object's props (for use with val / onChange) |
obj.initialValue | Values | Override default prop values without creating keyframes |
obj.sheet | Sheet | Parent sheet reference |
obj.project | Project | Parent project reference |
obj.address | { projectId, sheetId, sheetInstanceId, objectKey } | Unique identifier |
obj.onValuesChange(cb) | (callback) => unsubscribe | Subscribes to all prop value changes |
Notes
sheet.object()is idempotent: calling it with the samekeyreturns the existing object unlessreconfigure: trueis passed- Use
obj.onValuesChangeto drive DOM/Three.js updates on every frame obj.initialValuesets values that are used when no keyframe exists, without recording animation data- Call
sheet.detachObject(key)to remove the object; unsubscribe listeners first to avoid memory leaks
Related
- Sheet
- types
- val
- onChange
onChange
Subscribes to value changes on a pointer. The callback fires whenever the pointed-to value changes. Returns an unsubscribe function.
Signature / Usage
import { onChange } from '@theatre/core'
onChange(
pointer: Pointer<T>,
callback: (value: T) => void,
rafDriver?: RafDriver
): () => void// Subscribe to a single prop
const unsub = onChange(obj.props.position.x, (x) => {
mesh.position.x = x
})
// Subscribe to sequence playhead position
onChange(sheet.sequence.pointer.position, (pos) => {
console.log('playhead at', pos)
})
// Stop listening
unsub()Options / Props
| Name | Type | Description |
|---|---|---|
pointer | Pointer<T> | The pointer to observe |
callback | (value: T) => void | Called with the new value on each change |
rafDriver | RafDriver | Optional custom RAF driver for scheduling callbacks |
Notes
- The returned function must be called to avoid memory leaks (analogous to
addEventListener/removeEventListener) onChangefires on every change, including programmatic writes and Studio edits- For subscribing to all props of an object at once, prefer
object.onValuesChange(callback)over multipleonChangecalls rafDriverallows batching updates to a custom animation loop instead of the defaultrequestAnimationFrame
Related
- val
- Object
- dataverse
Project
The top-level container for animations and tweaks. Created via getProject().
Signature / Usage
import { getProject } from '@theatre/core'
const project = getProject('My Project', { state })
// Wait for project to load before playing animations
await project.ready
// Create a sheet
const sheet = project.sheet('Scene')Options / Props
| Name | Type | Description |
|---|---|---|
project.ready | Promise<void> | Resolves when the project finishes loading its state |
project.isReady | boolean | true once state is loaded |
project.address | { projectId: string } | Unique identifier for this project |
project.sheet(name, instanceId?) | (string, string?) => Sheet | Creates or returns a named Sheet |
project.getAssetUrl(handle) | (AssetHandle) => string | Returns URL for an asset handle (v0.6.0+) |
Notes
project.sheet(name)always returns the sameSheetfor a given name; useinstanceIdto create independent instances of the same sheetgetAssetUrl()is only available when using@theatre/studioor a configured asset pipeline (v0.6.0+)- Saved state is exported from Studio as a JSON file and imported at runtime
Related
- getProject
- Sheet
core
| Name | Description | Path |
|---|---|---|
| getProject | Creates or retrieves a Project by ID | get-project.md |
| Project | Top-level animation container; sheet(), ready, isReady, address | project.md |
| Sheet | Collection of animatable Objects sharing one Sequence timeline | sheet.md |
| Sequence | Animation timeline; play(), pause(), position, attachAudio() | sequence.md |
| Object | Animatable entity with typed props; value, props, onValuesChange() | object.md |
| types | Prop type constructors: compound, number, rgba, boolean, string, stringLiteral, image, file | types.md |
| val | Reads the current value of a pointer (one-time, non-reactive) | val.md |
| onChange | Subscribes to pointer value changes; returns unsubscribe function | on-change.md |
| @theatre/dataverse | Reactive dataflow primitives: Atom, prism, Ticker | dataverse.md |
Sequence
The animation timeline of a Sheet. Controls playback and exposes the current playhead position.
Signature / Usage
const sequence = sheet.sequence
// Play the full sequence
await sequence.play()
// Play a sub-range at half speed
await sequence.play({ range: [0, 2], rate: 0.5, iterationCount: Infinity })
// Seek to a position
sequence.position = 1.5
// Pause
sequence.pause()Options / Props
sequence.play(opts?)
Returns Promise<void> that resolves when playback ends (or is paused).
| Name | Type | Default | Description |
|---|---|---|---|
opts.range | [number, number] | full length | Start and end time in seconds |
opts.rate | number | 1 | Playback rate multiplier |
opts.iterationCount | number | 1 | Number of times to repeat; Infinity for looping |
opts.direction | `'normal' \ | 'reverse' \ | 'alternate' \ |
sequence properties
| Name | Type | Description |
|---|---|---|
sequence.pause() | () => void | Stops playback at current position |
sequence.position | number | Current playhead position in seconds (readable and writable) |
sequence.pointer | Pointer<SequenceState> | Reactive pointer to sequence state (position, playing, length) |
sequence.attachAudio(opts) | (AttachAudioOpts) => Promise<AudioGraph> | Attaches an audio source synced to the sequence |
sequence.attachAudio(opts) options
| Name | Type | Description |
|---|---|---|
opts.source | `string \ | AudioBuffer` |
opts.audioContext | AudioContext | Optional custom Web Audio AudioContext |
opts.destinationNode | AudioNode | Optional destination node for audio routing |
attachAudio() returns Promise<{ audioContext, gainNode, destinationNode }>.
Notes
- Setting
sequence.positiondirectly seeks the playhead without triggering playback attachAudio()automatically handles browser autoplay restrictions by waiting for a user gesturesequence.__experimental_getKeyframes(pointer)returns the keyframe array for a given prop pointer (v0.6.1+, unstable API)
Related
- Sheet
- Object
Sheet
A collection of animatable Objects that share a single Sequence timeline. Created via project.sheet().
Signature / Usage
const sheet = project.sheet(sheetId: string, instanceId?: string): Sheet
// Create objects on the sheet
const obj = sheet.object('Box', { position: { x: 0, y: 0 } })
// Access the sequence for playback control
sheet.sequence.play()Options / Props
| Name | Type | Description |
|---|---|---|
sheet.sequence | Sequence | The animation sequence for this sheet |
sheet.project | Project | Reference to the parent project |
sheet.address | { projectId, sheetId, sheetInstanceId } | Unique identifier for this sheet instance |
sheet.object(key, config, opts?) | (string, Props, ObjectOpts?) => Object | Creates or returns a Sheet Object |
sheet.detachObject(key) | (string) => void | Removes a child Object (v0.5.1+) |
Notes
- Use
instanceIdinproject.sheet(name, instanceId)to create multiple independent timeline instances of the same sheet (e.g., multiple animated cards) detachObject()does not automatically unsubscribe anyonValuesChangelisteners; unsubscribe manually before detaching- All objects on a sheet share the same sequence timeline
Related
- Project
- Sequence
- Object
types
Prop type constructors for @theatre/core. Used when defining typed props on a Sheet Object via sheet.object().
Signature / Usage
import { types } from '@theatre/core'
const obj = sheet.object('Box', {
position: types.compound({
x: types.number(0, { range: [-10, 10] }),
y: types.number(0, { range: [-10, 10] }),
}),
color: types.rgba({ r: 1, g: 0, b: 0, a: 1 }),
label: types.string('Hello'),
visible: types.boolean(true),
blendMode: types.stringLiteral('normal', {
normal: 'Normal',
multiply: 'Multiply',
}),
})Options / Props
types.number(default, opts?)
| Name | Type | Default | Description |
|---|---|---|---|
default | number | — | Initial value |
opts.range | [number, number] | none | Clamp range shown in Studio UI |
opts.nudgeMultiplier | number | 1 | Multiplier for arrow-key nudging in Studio |
opts.label | string | — | Display label in Studio |
types.compound(props, opts?)
Groups multiple props into a nested object.
| Name | Type | Description |
|---|---|---|
props | Record<string, PropType> | Child prop definitions |
opts.label | string | Display label in Studio |
types.rgba(default?)
| Name | Type | Default | Description |
|---|---|---|---|
default | { r, g, b, a } | { r:0, g:0, b:0, a:1 } | Initial color; all channels are 0–1 normalized |
Also accepts CSS hex strings: #RGB, #RGBA, #RRGGBB, #RRGGBBAA.
types.boolean(default, opts?)
| Name | Type | Description |
|---|---|---|
default | boolean | Initial value |
opts.label | string | Display label in Studio |
types.string(default, opts?)
| Name | Type | Description |
|---|---|---|
default | string | Initial value |
opts.label | string | Display label in Studio |
types.stringLiteral(default, choices, opts?)
Renders as a radio group or dropdown in Studio.
| Name | Type | Description |
|---|---|---|
default | string | Initial choice key |
choices | Record<string, string> | Map of value → display label |
opts.as | `'menu' \ | 'switch'` |
types.image(default, opts?) (v0.6.0+)
Asset handle prop for images. default is typically an empty string ''.
types.file(default, opts?) (v0.7.0+)
Asset handle prop for arbitrary files. default is typically an empty string ''.
Notes
- Plain object literals in
sheet.object()config are automatically treated astypes.compound; explicittypes.compoundis needed only when passingopts types.rgbachannels are normalized 0–1, not 0–255- Asset types (
image,file) return an asset handle string; useproject.getAssetUrl(handle)to resolve to a URL
Related
- Object
- Project
val
Reads the current value of a pointer non-reactively (a one-time read).
Signature / Usage
import { val } from '@theatre/core'
val(pointer: Pointer<T>): T// Read a single prop
const x = val(obj.props.position.x)
// Read the entire object's props
const allValues = val(obj.props)
// Read sequence state
const pos = val(sheet.sequence.pointer.position)Notes
val()is a snapshot read; it does not subscribe to future changes. UseonChange()orobject.onValuesChange()for reactive updates- Pointers are obtained from
obj.props,sheet.sequence.pointer, or DataverseAtominstances - Calling
val()outside a reactive context (e.g., aprism) simply reads the latest value once
Related
- onChange
- Object
- Sequence
- dataverse
Advanced Uses
Custom rafDrivers (v0.6.0+) for controlling when and how often Theatre.js processes animation computations. Enables synchronization with other libraries and specialized rendering environments.
Signature / Usage
import { createRafDriver } from '@theatre/core'
// Create a custom 5 fps driver
const rafDriver = createRafDriver({ name: 'my-driver' })
setInterval(() => rafDriver.tick(performance.now()), 200)
// Use with sequence playback
sheet.sequence.play({ rafDriver })
// Use with onChange listeners
import { onChange } from '@theatre/core'
onChange(obj.pointer.x, (x) => { mesh.position.x = x }, rafDriver)
// Integrate with Studio
import studio from '@theatre/studio'
studio.initialize({ __experimental_rafDriver: rafDriver })createRafDriver(opts) Options
| Name | Type | Description |
|---|---|---|
name | string | Identifier shown in debugging |
Returned driver object:
| Member | Type | Description |
|---|---|---|
tick(timestamp) | (number) => void | Advance all computations to the given timestamp |
start() | () => void | Optional lifecycle callback when listeners attach |
stop() | () => void | Optional lifecycle callback when all listeners detach |
Notes
- The default Theatre.js driver uses
requestAnimationFrame; replace it only when you need synchronization with another loop - With
@react-three/fiber: wrap components in<RafDriverProvider driver={rafDriver}>to scope the driver to that subtree - With XR environments: use
xr.requestAnimationFrame()as the tick source - Multiple drivers can coexist on the same page
start/stopcallbacks let you pause the driver when no listeners are active, saving resources
Related
- Sequences
- Using Audio
Assets
File-based prop values (images, textures, etc.) managed through Theatre.js Studio. Available since v0.6.0.
Signature / Usage
import { getProject, types } from '@theatre/core'
// Configure project with asset base URL
const project = getProject('My Project', {
assets: {
baseUrl: '/theatrejs-assets',
},
})
// Define an image prop on a sheet object
const obj = sheet.object('My Object', {
texture: types.image('', { label: 'Texture' }),
})
// Resolve asset to a usable URL
obj.onValuesChange(({ texture }) => {
const url = project.getAssetUrl(texture)
if (url) myMesh.material.map = textureLoader.load(url)
})Options / Props
getProject(name, { assets }):
| Name | Type | Description |
|---|---|---|
assets.baseUrl | string | Base URL where exported assets are hosted (default '/') |
types.image(default, opts?):
| Name | Type | Description |
|---|---|---|
default | string | Initial asset handle; use '' for none |
opts.label | string | UI label shown in Studio |
Notes
- Assign files to asset props in Studio; they are initially stored in the browser's IndexedDB
- When exporting project state, Studio generates a ZIP containing all assets — extract to
baseUrl - Once assets are served from
baseUrl, Studio removes them from IndexedDB to save space - An empty string or
undefinedvalue means no asset is assigned - Custom asset types require forking the Theatre.js repo and adding a new prop editor
Related
- Prop Types
- Projects
Using Audio
Synchronize audio playback with a Sheet's animation timeline using sequence.attachAudio(). Theatre.js handles fetching, decoding, and Web Audio API context management automatically.
Signature / Usage
// Load audio from URL, then play in sync
sheet.sequence.attachAudio({ source: '/music.mp3' }).then(() => {
sheet.sequence.play()
})
// Custom audio graph (pre-existing AudioContext)
const audioContext = new AudioContext()
sheet.sequence.attachAudio({
source: audioBuffer, // AudioBuffer
audioContext,
destinationNode: audioContext.destination,
})
// Modify Theatre.js's built-in gain node
sheet.sequence.attachAudio({ source: '/music.mp3' }).then(({ gainNode, audioContext }) => {
gainNode.disconnect()
const custom = audioContext.createGain()
custom.gain.setValueAtTime(0.1, audioContext.currentTime)
gainNode.connect(custom)
custom.connect(audioContext.destination)
})Options / Props
sequence.attachAudio(opts):
| Name | Type | Description |
|---|---|---|
source | `string \ | AudioBuffer` |
audioContext | AudioContext | Optional existing Web Audio context |
destinationNode | AudioNode | Optional output node (defaults to audioContext.destination) |
Returns Promise resolving to { audioContext, gainNode, ... }.
Notes
- Browsers block audio autoplay until a user gesture occurs; Theatre.js waits for the gesture automatically when using a URL source
- Best practice: show a "Play" button and call
sequence.play()on click to ensure audio starts properly gainNodein the resolved object is Theatre.js's internal volume node — disconnect and replace it to insert custom processing
Related
- Sequences
- Advanced Uses
Sheet Objects
Animatable elements within a Sheet. Can represent THREE.js objects, HTML elements, or any virtual entity. Each object holds typed props whose values Theatre.js animates over time.
Signature / Usage
// Create a sheet object with props
const obj = sheet.object('My Object', {
position: { x: 0, y: 0 },
})
// Read current values
obj.onValuesChange((values) => {
mesh.position.x = values.position.x
})
// Namespaced keys for Studio organization
sheet.object('Basics / Boxes / box-0', { x: 0 })
sheet.object('Basics / Boxes / box-1', { x: 0 })Options / Props
sheet.object(key, props, options?):
| Name | Type | Description |
|---|---|---|
key | string | Unique name; use / separators for namespace grouping |
props | object | Map of prop names to default values or typed definitions |
options.reconfigure | boolean | (v0.5.1+) Replace props on an existing object without page refresh |
Notes
- Calling
sheet.object()with an existing key returns the same instance - With
reconfigure: true, the same reference is returned but old props are removed;obj.value.oldPropbecomesundefined - Use
sheet.detachObject(key)(v0.5.1+) to remove an object while preserving its stored values — recreating with the same key restores them - Forward-slash namespacing (
'Group / Sub / item') creates indented hierarchy in the Studio Outline Panel
Related
- Sheets
- Prop Types
- Sequences
Projects
Top-level organizational unit in Theatre.js. All animations and state belong to a project. Multiple projects can coexist on a single page, though one typically suffices.
Signature / Usage
import { getProject } from '@theatre/core'
import projectState from './state.json'
// Create or retrieve a project
const project = getProject('My Project')
// Load with persisted state
const project = getProject('My Project', { state: projectState })
// Wait for project to be ready (when using Studio)
project.ready.then(() => console.log('Project loaded!'))Options / Props
| Name | Type | Description |
|---|---|---|
state | object | Previously exported JSON state to initialize the project |
Notes
- Calling
getProject()with an existing name returns the same instance (no duplicate) - When Studio is active, state is automatically persisted to
localStorage - Export state as JSON via the Studio UI; load it back via the
stateoption project.readyis a Promise;project.isReadyis a boolean for synchronous checks- Use
project.readyto defer animation start until state is fully loaded
Related
- Sheets
- Sheet Objects
- Assets
Prop Types
Type definitions for Sheet Object props. Types control how Studio renders editing tools and how Theatre.js interpolates keyframe values. Import from @theatre/core.
Signature / Usage
import { types } from '@theatre/core'
const obj = sheet.object('My Object', {
// Shorthand (infers number type)
x: 0,
// Explicit number with options
speed: types.number(1, { range: [0, 10] }),
// Color
color: types.rgba({ r: 1, g: 0, b: 0, a: 1 }),
// Grouped props
rotation: types.compound({ x: 0, y: 0, z: 0 }),
// Enum menu
mode: types.stringLiteral('low', { low: 'Low', high: 'High' }),
// Image asset
texture: types.image('', { label: 'Texture' }),
})Available Types
| Type | Description |
|---|---|
types.number(default, opts?) | Numeric value; opts.range, opts.nudgeMultiplier, opts.nudgeFn |
types.boolean(default, opts?) | Boolean toggle; opts.label for UI label override |
types.string(default, opts?) | Free-form string; opts.label |
types.stringLiteral(default, labels, opts?) | Predefined string options rendered as menu or radio; opts.as: 'switch' for toggle |
types.compound(props, opts?) | Groups related props; shown as expandable section in Details Panel |
types.rgba(default?) | Color value stored as {r, g, b, a}; accepts #RGB, #RGBA, #RRGGBB, #RRGGBBAA |
types.image(default, opts?) | Image asset reference; empty string or undefined means no asset assigned |
Notes
- Explicit type declarations unlock Studio's specialized editors and correct keyframe interpolation
types.compounddoes not itself animate; its child props animate individuallytypes.stringLiteraldoes not interpolate between values (stepped transitions only)types.imageintegrates with the Assets system; useproject.getAssetUrl(value)to resolve URLs- Shorthand defaults (e.g.,
x: 0) are treated astypes.numberautomatically
Related
- Sheet Objects
- Assets
- Sequences
Manual
| Name | Description | Path |
|---|---|---|
| Projects | Top-level organizational unit; state management via getProject() | projects.md |
| Sheets | Animation container; independent instances via project.sheet() | sheets.md |
| Sheet Objects | Animatable elements with typed props; sheet.object() | objects.md |
| Prop Types | Type definitions for props: number, boolean, string, rgba, compound, image | prop-types.md |
| Sequences | Timeline playback control: play(), pause(), position, pointer | sequences.md |
| Assets | File-based prop values (images/textures) with Studio-managed export workflow | assets.md |
| Using Audio | Sync audio to animation via sequence.attachAudio() | audio.md |
| Advanced Uses | Custom rafDrivers for synchronizing with external render loops | advanced.md |
Sequences
Timeline-based animation controller attached to each Sheet. Controls playback, keyframe management, and real-time value observation.
Signature / Usage
// Basic playback
sheet.sequence.play()
// Play with options
sheet.sequence.play({
rate: 2,
range: [1, 4],
iterationCount: Infinity,
direction: 'alternate',
})
// Pause
sheet.sequence.pause()
// Read / set playhead position (seconds)
console.log(sheet.sequence.position)
sheet.sequence.position = 1
// Watch playback state via pointer
import { onChange } from '@theatre/core'
onChange(sheet.sequence.pointer.playing, (isPlaying) => {
console.log(isPlaying ? 'playing' : 'paused')
})API
sequence.play(opts?)
| Option | Type | Description |
|---|---|---|
rate | number | Playback speed multiplier (default 1) |
range | [number, number] | Start and end positions in seconds |
iterationCount | number | Repeat count; use Infinity for looping |
direction | `'normal' \ | 'reverse' \ |
rafDriver | RafDriver | Custom animation frame driver (v0.6.0+) |
Returns Promise<boolean> — resolves true on natural completion, false if interrupted.
sequence.pause()
Halts playback without resetting position.
sequence.position
Read/write number. Current playhead position in seconds.
sequence.pointer
Provides reactive access to sequence state. Useful sub-paths:
| Path | Type | Description |
|---|---|---|
sequence.pointer.position | number | Current position |
sequence.pointer.playing | boolean | Whether sequence is playing |
sequence.pointer.length | number | Total sequence duration |
Use with onChange() or val() from @theatre/core.
Notes
- Keyframes are added/managed via the Studio Sequence Editor UI (right-click a prop → "Sequence")
- Aggregate keyframes on compound props move all child keyframes together
sequence.play()returns a Promise; chain.then()to trigger actions after animation completes- Use
rafDriveroption to synchronize with other libraries (e.g.,@react-three/fiber)
Related
- Sheets
- Using Audio
- Advanced Uses
Sheets
Container that holds one or more Sheet Objects animated together. Each sheet has its own Sequence for independent playback control.
Signature / Usage
// Create or retrieve a sheet
const sheet = project.sheet('My Sheet')
// Sheet instances — multiple independent copies of the same animation
const submitBtn = project.sheet('Button', 'Submit')
const cancelBtn = project.sheet('Button', 'Cancel')
// Play the sheet's animation sequence
sheet.sequence.play()Options / Props
project.sheet(sheetName, instanceId?) accepts two arguments:
| Name | Type | Description |
|---|---|---|
sheetName | string | Unique name for the sheet template |
instanceId | string | Optional identifier for independent instances of the same sheet |
Notes
- Calling
project.sheet()with an existing name returns the existing sheet - Each instance (via
instanceId) maintains independent animation state — playing one does not affect others - Access the animation timeline via
sheet.sequence - Sheet Objects are added to a sheet via
sheet.object()
Related
- Projects
- Sheet Objects
- Sequences
editable (e)
Creates animatable versions of React Three Fiber elements, binding them to Theatre.js for keyframe animation. Typically imported as e for brevity.
Signature / Usage
import { editable as e } from '@theatre/r3f'
// As a JSX tag — built-in r3f element types
<e.mesh theatreKey="Cube">
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</e.mesh>
<e.pointLight theatreKey="Key light" position={[10, 10, 10]} />
<e.group theatreKey="Group" />// As a function — wraps a custom component
import { editable } from '@theatre/r3f'
import { PerspectiveCamera } from '@react-three/drei'
const EditableCamera = editable(PerspectiveCamera, 'perspectiveCamera')Options / Props
| Name | Type | Description |
|---|---|---|
theatreKey | string | Required. Unique identifier for the backing Theatre.js sheet object |
visible | `boolean \ | 'editor'` |
additionalProps | object | Extra Theatre.js properties beyond standard r3f props |
objRef | ref | Ref that receives the backing ISheetObject instance |
editableType | string | THREE.js type hint used with editable.primitive |
Notes
theatreKeymust be unique within its enclosingSheetProvidereditableas a function takes(Component, editableType)—editableTypemust match a valid THREE.js object type string (e.g.,'perspectiveCamera')additionalPropsvalues are observable on theISheetObjectaccessed viaobjRef- All
editableelements must be inside a<SheetProvider>
Related
- SheetProvider.md
- PerspectiveCamera.md
extension
The r3f extension object that registers React Three Fiber support with Theatre Studio. Must be passed to studio.extend() to enable the snapshot editor and r3f-specific tooling in development.
Signature / Usage
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'
if (import.meta.env.DEV) {
studio.initialize()
studio.extend(extension)
}Notes
- Import path is
@theatre/r3f/dist/extension(separate entry point from the main@theatre/r3f) - Should only be called in development; guard with an env check to avoid bundling Studio in production
- Enables the visual snapshot editor panel in Theatre Studio for r3f scenes
Related
- SheetProvider.md
- refreshSnapshot.md
OrthographicCamera
Pre-built editable orthographic camera component for Theatre.js + React Three Fiber. Wraps @react-three/drei's OrthographicCamera with Theatre.js animation support.
Signature / Usage
import { OrthographicCamera } from '@theatre/r3f'
<OrthographicCamera
theatreKey="OrthoCamera"
makeDefault
position={[0, 5, 10]}
/>Options / Props
| Name | Type | Description |
|---|---|---|
theatreKey | string | Required. Unique identifier for the Theatre.js sheet object |
makeDefault | boolean | Sets this camera as the active rendering camera |
position | [number, number, number] | Initial camera position |
lookAt | React.RefObject<THREE.Object3D> | Ref to a Three.js object the camera tracks automatically |
Notes
- Must be inside a
<SheetProvider> - Unlike
PerspectiveCamera, there is nofovprop; usezoominstead (standard drei prop) - See
PerspectiveCamerafor the perspective equivalent
Related
- PerspectiveCamera.md
- SheetProvider.md
PerspectiveCamera
Pre-built editable perspective camera component for Theatre.js + React Three Fiber. Wraps @react-three/drei's PerspectiveCamera with Theatre.js animation support.
Signature / Usage
import { PerspectiveCamera } from '@theatre/r3f'
<PerspectiveCamera
theatreKey="Camera"
makeDefault
position={[5, 5, -5]}
fov={75}
/>Options / Props
| Name | Type | Description |
|---|---|---|
theatreKey | string | Required. Unique identifier for the Theatre.js sheet object |
makeDefault | boolean | Sets this camera as the active rendering camera |
position | [number, number, number] | Initial camera position |
fov | number | Field of view in degrees |
lookAt | React.RefObject<THREE.Object3D> | Ref to a Three.js object the camera tracks automatically |
Notes
- Must be inside a
<SheetProvider> lookAtaccepts a ref to any THREE.js Object3D; the camera updates its target each frame- An
OrthographicCameravariant with the same props (exceptfov) is also available from@theatre/r3f
Related
- SheetProvider.md
- editable.md
r3f
@theatre/r3f and @theatre/react API reference for React Three Fiber integration.
| Name | Description | Path |
|---|---|---|
| SheetProvider | Container component that binds a Theatre.js sheet to descendant editable elements | SheetProvider.md |
| editable (e) | Creates animatable r3f elements or wraps custom components for Theatre.js | editable.md |
| PerspectiveCamera | Editable perspective camera with theatreKey, makeDefault, and lookAt support | PerspectiveCamera.md |
| OrthographicCamera | Editable orthographic camera with theatreKey, makeDefault, and lookAt support | OrthographicCamera.md |
| useCurrentSheet | Hook returning the nearest SheetProvider's ISheet instance (@theatre/r3f) | useCurrentSheet.md |
| refreshSnapshot / RefreshSnapshot | Refresh the snapshot editor after dynamic scene changes | refreshSnapshot.md |
| extension | r3f extension object; pass to studio.extend() to enable Studio tooling | extension.md |
| useVal | Subscribe a React component to a Theatre.js pointer or Dataverse prism (@theatre/react) | useVal.md |
| usePrism | Derive a reactive value from a function and subscribe to it (@theatre/react) | usePrism.md |
| useAtom | Create a Dataverse Atom for shared reactive state (@theatre/react) | useAtom.md |
refreshSnapshot / RefreshSnapshot
Utilities that refresh the snapshot editor state from code. Use when dynamically loaded scene content (e.g., models loaded via Suspense) needs to be reflected in Theatre Studio's snapshot.
Signature / Usage
import { refreshSnapshot, RefreshSnapshot } from '@theatre/r3f'
// Function — call imperatively after dynamic content loads
refreshSnapshot()
// Component — triggers refresh on mount; pair with Suspense
<Suspense fallback={<Fallback />}>
<RefreshSnapshot />
<MyModel />
</Suspense>Notes
refreshSnapshot()is a plain function; call it after any async scene updateRefreshSnapshotis a React component with no props that callsrefreshSnapshot()on mount- Both are no-ops in production (when Theatre Studio is not initialized)
- Primarily relevant during development with
@theatre/studioactive
Related
- SheetProvider.md
- extension.md
SheetProvider
Container component that connects a Theatre.js sheet to all descendant editable elements. Must wrap any r3f elements you want to animate with Theatre.js.
Signature / Usage
import { SheetProvider } from '@theatre/r3f'
import { getProject } from '@theatre/core'
const sheet = getProject('My Project').sheet('Scene')
<Canvas>
<SheetProvider sheet={sheet}>
<e.mesh theatreKey="Box">
<boxGeometry />
<meshStandardMaterial />
</e.mesh>
</SheetProvider>
</Canvas>Options / Props
| Name | Type | Description |
|---|---|---|
sheet | ISheet | The Theatre.js sheet instance to bind descendants to |
Notes
- All
editable(e.*) elements must be descendants of aSheetProvider theatreKeyvalues must be unique within a singleSheetProvider- Typically placed inside
<Canvas>from@react-three/fiber
Related
- editable.md
- useCurrentSheet.md
useAtom
Hook from @theatre/react that creates a Dataverse Atom — a reactive state container similar to useState, but the component does not re-render on atom value changes (use useVal to subscribe).
Signature / Usage
import { useAtom } from '@theatre/react'
function MyScene() {
const atom = useAtom({ ready: false, count: 0 })
// Read a value reactively in another component
const isReady = useVal(atom.pointer.ready)
// Update a specific path
atom.setByPointer(atom.pointer.count, 42)
return null
}Options / Props
| Name | Type | Description |
|---|---|---|
initialValue | T | Initial state object for the atom |
Returns an Atom<T> with:
| Member | Description |
|---|---|
pointer | Type-safe pointer root for accessing sub-paths |
setByPointer(pointer, value) | Updates a specific path in the atom |
get() | Returns the current full value |
Notes
- The component that calls
useAtomdoes not re-render when the atom changes — useuseVal(atom.pointer.someField)in child components to subscribe - Useful for sharing reactive state across a component tree without prop drilling
- Imported from
@theatre/react
Related
- useVal.md
- usePrism.md
useCurrentSheet
Hook that returns the ISheet instance from the nearest ancestor SheetProvider. Useful for triggering playback or accessing the sheet inside a component tree without prop drilling.
Signature / Usage
import { useCurrentSheet } from '@theatre/r3f'
import { useEffect } from 'react'
function SceneController() {
const sheet = useCurrentSheet()
useEffect(() => {
sheet?.project.ready.then(() => {
sheet.sequence.play({ iterationCount: Infinity, range: [0, 2] })
})
}, [sheet])
return null
}Notes
- Returns
ISheet | undefinedif called outside aSheetProvider - Imported from
@theatre/r3f, not@theatre/react - Gives access to
sheet.sequencefor controlling playback (.play(),.pause(),.position)
Related
- SheetProvider.md
- useVal.md
usePrism
Hook from @theatre/react that creates a derived reactive value (prism) from a function and subscribes the component to it, similar to useMemo but reactive to Dataverse state.
Signature / Usage
import { usePrism } from '@theatre/react'
import { val } from '@theatre/dataverse'
const scaledIntensity = usePrism(() => {
return val(lightObj.props.intensity) * 2
}, [lightObj])Options / Props
| Name | Type | Description |
|---|---|---|
fn | () => T | Function executed within a prism context; may call val() on pointers |
deps | unknown[] | Dependency array (same semantics as useMemo) |
Returns T — the current computed value.
Notes
fnruns inside a prism context, so calls toval(pointer)inside it are automatically tracked- Re-renders when any tracked value (or a listed dependency) changes
- Requires a
depsarray; missing deps cause stale closures just likeuseMemo - Imported from
@theatre/react
Related
- useVal.md
- useAtom.md
useVal
Hook from @theatre/react that subscribes a React component to a Theatre.js pointer or Dataverse prism, returning its current value and re-rendering when the value changes.
Signature / Usage
import { useVal } from '@theatre/react'
// Subscribe to a Theatre.js sheet object prop
const brightness = useVal(lightObj.props.intensity)
// Subscribe to an atom pointer path
const isReady = useVal(atom.pointer.ready)
// Subscribe to a prism
const derived = useVal(somePrism)Options / Props
| Name | Type | Description |
|---|---|---|
pointerOrPrism | `Pointer<T> \ | Prism<T>` |
Returns T — the current value at the pointer/prism.
Notes
- Can only be called inside a React component's render function (React hook rules apply)
- Component re-renders whenever the pointed-to value changes
- Works with Theatre.js
ISheetObjectprop pointers (obj.props.someProp) and Dataverse atoms/prisms - Imported from
@theatre/react, not@theatre/r3f
Related
- usePrism.md
- useCurrentSheet.md
Authoring Extensions
Guide to building custom Theatre.js Studio extensions that add toolbar buttons, custom panes, and programmatic scene control.
Overview
Extensions are plain JavaScript objects registered via studio.extend() before studio.initialize(). They can:
- Add buttons and switches to the Global Toolbar
- Open custom draggable/resizable pane windows
- Listen to and modify the current selection
- Persist configuration using the Studio project
Minimal Extension
import studio from '@theatre/studio'
studio.extend({
id: 'my-extension',
toolbars: {
global(set, studio) {
return [
{
type: 'Icon',
title: 'Open My Pane',
svgSource: '🔧',
onClick() {
studio.createPane('my-pane')
},
},
]
},
},
panes: [
{
class: 'my-pane',
mount({ node }) {
node.innerHTML = '<p>Hello from extension!</p>'
return () => { /* cleanup on close */ }
},
},
],
})
studio.initialize()Switch Toolbar Item
let currentMode = 'view'
studio.extend({
id: 'mode-switcher',
toolbars: {
global(set) {
return [
{
type: 'Switch',
value: currentMode,
options: [
{ value: 'view', label: 'View', svgSource: '👁' },
{ value: 'edit', label: 'Edit', svgSource: '✏️' },
],
onChange(value) {
currentMode = value
set(/* re-render by returning updated config */)
},
},
]
},
},
panes: [],
})Reacting to Selection
studio.onSelectionChange((selection) => {
const obj = selection.find((item) => 'props' in item)
if (obj) updateMyEditor(obj)
})Persisting Extension State
import { types } from '@theatre/core'
const configObj = studio.getStudioProject()
.sheet('my-extension')
.object('config', {
theme: types.stringLiteral('light', { light: 'Light', dark: 'Dark' }),
})
configObj.onValuesChange(({ theme }) => applyTheme(theme))Hot Reloading (v0.7.0+)
studio.extend(extension, { __experimental_reconfigure: true })Notes
studio.extend()must be called beforestudio.initialize()- Extension
idmust be globally unique - Render a registered toolbar inside a pane's DOM node:
studio.ui.renderToolset('global', node) - The
mountfunction's return value (cleanup) is called when the pane is closed
Related
- studio.extend
- studio.createPane
- studio.selection
- studio.getStudioProject
Keyboard & Mouse Controls
Keyboard shortcuts and mouse interactions for Theatre.js Studio.
Studio-wide
| Shortcut | Action |
|---|---|
Alt/Option + \ | Toggle Studio visibility |
Space | Play / Pause sequence |
Ctrl/Cmd + Z | Undo |
Ctrl/Cmd + Shift + Z | Redo |
Details Panel
| Interaction | Action |
|---|---|
| Left Mouse drag on number prop | Scrub value |
Alt/Option + Left Mouse drag | Scrub at reduced speed (fine control) |
Sequence Editor (Timeline bar)
| Interaction | Action |
|---|---|
Shift + Left Mouse drag in top bar | Create / adjust focus range |
Dope Sheet
| Interaction | Action |
|---|---|
Shift + Left Mouse drag | Create keyframe selection box |
| Right Mouse click | Context menu (copy, paste, delete) |
| Right Mouse click on playhead | Place marker |
Ctrl/Control + Scroll | Zoom timeline |
| Pinch (trackpad) | Zoom timeline |
Shift + Scroll | Pan timeline horizontally |
| Trackpad scroll left/right | Pan timeline horizontally |
Keyframe Curve Editor
| Shortcut | Action |
|---|---|
← / → / ↑ / ↓ | Navigate easing presets |
Enter | Close and save selected preset |
Escape | Close without saving |
Color Picker
| Shortcut | Action |
|---|---|
← / → / ↑ / ↓ | Adjust color value |
Related
- Studio UI
studio
@theatre/studio package API and UI reference.
| Name | Description | Path |
|---|---|---|
| studio.initialize | Sets up the Studio UI once per app; call before studio.extend() | studio-initialize.md |
| studio.extend | Registers an extension adding toolbar items and custom panes | studio-extend.md |
| studio.transaction | Executes property changes as a single undoable action | studio-transaction.md |
| studio.scrub | Accumulates continuous captures into one undo entry | studio-scrub.md |
| studio.selection | Read/write access to the current sheet/object selection | studio-selection.md |
| studio.createPane | Opens a pane window from an extension-registered class | studio-create-pane.md |
| studio.getStudioProject | Returns the internal Studio project for extension state persistence | studio-get-studio-project.md |
| studio.createContentOfSaveFile | Exports project state as JSON for custom persistence backends | studio-create-content-of-save-file.md |
| Studio UI | Overview of Studio panels: Outline, Details, Sequence Editor, Dope Sheet | studio-ui.md |
| Keyboard & Mouse Controls | All keyboard shortcuts and mouse interactions for Studio | keyboard-mouse-controls.md |
| Authoring Extensions | Guide to building toolbar buttons, panes, and selection listeners | authoring-extensions.md |
studio.createContentOfSaveFile
Generates an exportable JSON object representing the current state of a project. Use this to implement custom persistence (e.g., saving to a server instead of the default localStorage).
Signature / Usage
studio.createContentOfSaveFile(projectId: string): Record<string, unknown>import studio from '@theatre/studio'
async function saveToServer(projectId: string) {
const content = studio.createContentOfSaveFile(projectId)
await fetch('/api/save', {
method: 'POST',
body: JSON.stringify(content),
})
}Options / Props
| Name | Type | Description |
|---|---|---|
projectId | string | The ID of the project to export (matches the ID passed to getProject()) |
Notes
- The returned object is plain JSON-serializable data; store and load it however you like
- To restore state from a custom save file, pass the loaded JSON as the second argument to
getProject(id, { state: savedJSON }) - By default, Theatre.js stores state in
localStorage; this API is the escape hatch for custom backends
Related
- studio.getStudioProject
studio.createPane
Opens a new pane window from an extension-registered pane class.
Signature / Usage
studio.createPane(paneClass: string): voidimport studio from '@theatre/studio'
// Open a pane whose class was registered via studio.extend()
studio.createPane('my-pane')Options / Props
| Name | Type | Description |
|---|---|---|
paneClass | string | The class identifier defined in the extension's panes array |
Notes
- The pane class must be registered via
studio.extend()before callingcreatePane() - Each call opens a new independent pane instance
- The pane's
mountcallback receives{ paneId, node }— usenodeto insert custom HTML, and return a cleanup function to run when the pane closes - Render extension toolbars inside the pane with
studio.ui.renderToolset(toolbarName, node)
Related
- studio.extend
- Studio UI
- Authoring Extensions
studio.extend
Registers an extension to enhance Studio's UI and functionality. Must be called before studio.initialize().
Signature / Usage
studio.extend(extension: IExtension, options?: { __experimental_reconfigure?: boolean }): voidimport studio from '@theatre/studio'
const myExtension = {
id: 'my-extension',
toolbars: {
global(set, studio) {
return [
{
type: 'Icon',
title: 'My Tool',
svgSource: '🔧',
onClick() {
studio.createPane('my-pane')
},
},
]
},
},
panes: [
{
class: 'my-pane',
mount({ paneId, node }) {
node.innerHTML = '<p>Hello from pane</p>'
return () => { /* cleanup */ }
},
},
],
}
studio.extend(myExtension)
studio.initialize()Options / Props
IExtension object
| Name | Type | Description |
|---|---|---|
id | string | Unique identifier for the extension |
toolbars | object | Map of toolbar names to functions returning toolbar items |
panes | array | Pane class definitions with class and mount |
Toolbar item — Button
| Name | Type | Description |
|---|---|---|
type | 'Icon' | Renders as an icon button |
title | string | Tooltip text |
svgSource | string | Icon content (emoji or SVG string) |
onClick | () => void | Click handler |
Toolbar item — Switch
| Name | Type | Description |
|---|---|---|
type | 'Switch' | Renders as a mutually exclusive option group |
value | string | Currently selected option value |
options | array | Items with value, label, and svgSource |
onChange | (value: string) => void | Called when user changes selection |
Pane definition
| Name | Type | Description |
|---|---|---|
class | string | Unique pane class identifier |
mount | `(instance: { paneId: string, node: HTMLElement }) => (() => void) \ | void` |
extend() options
| Name | Type | Description |
|---|---|---|
__experimental_reconfigure | boolean | Enable hot-reloading of the extension (since v0.7.0) |
Notes
studio.extend()must be called beforestudio.initialize()- Open a pane programmatically with
studio.createPane(paneClass) - Render a toolset inside a pane's
nodewithstudio.ui.renderToolset(toolbarName, node) - To persist extension state across reloads, use
studio.getStudioProject().sheet(...).object(...)
Related
- studio.initialize
- studio.createPane
- Authoring Extensions
studio.getStudioProject
Returns the internal Theatre.js IProject that Studio uses for its own sheets and objects. Extensions use this to persist configuration data across reloads.
Signature / Usage
studio.getStudioProject(): IProjectimport studio from '@theatre/studio'
import { types } from '@theatre/core'
const studioProject = studio.getStudioProject()
const configObj = studioProject
.sheet('my-extension')
.object('config', {
mode: types.stringLiteral('default', { default: 'Default', advanced: 'Advanced' }),
})
// React to config changes
configObj.onValuesChange((values) => {
applyMode(values.mode)
})Notes
- The returned project is managed by Studio itself; do not call
project.readychecks or load external state into it - Ideal for storing lightweight per-extension UI state that should survive page reloads (Studio serializes it alongside the user's project state)
- Combine with
studio.onSelectionChangeto build custom inspector panes that update when the user selects different objects
Related
- studio.extend
- Authoring Extensions
studio.initialize
Sets up the Theatre.js Studio UI once per application. Subsequent calls are silently ignored.
Signature / Usage
import studio from '@theatre/studio'
studio.initialize()Call studio.initialize() before anything else in your app's entry point. The studio UI only appears when this is called, giving you control over when the editing interface is available (e.g., development only).
// Conditionally show Studio in development
if (process.env.NODE_ENV === 'development') {
studio.initialize()
}Notes
- Must be called before
studio.extend()to ensure extensions are registered before the UI mounts - Calling it multiple times has no effect; the studio is initialized only once
- The studio UI can be toggled with
Alt/Option + \after initialization
Related
- studio.extend
- Studio UI
studio.scrub
Creates a scrub interface that accumulates multiple property captures into a single undoable action. Useful for continuous interactions like dragging.
Signature / Usage
studio.scrub(): IScrubimport studio from '@theatre/studio'
const scrub = studio.scrub()
// called repeatedly during a drag
scrub.capture(({ set }) => {
set(obj.props.x, currentX)
})
// finalise as one undo entry
scrub.commit()
// or discard all changes
// scrub.discard()
// or revert to the state before the first capture
// scrub.reset()Options / Props
IScrub methods
| Name | Type | Description |
|---|---|---|
capture(fn) | (fn: (api: { set; unset }) => void) => void | Records a set of changes; replaces any previous capture in this scrub |
commit() | () => void | Finalizes all captures as a single undo level |
reset() | () => void | Clears all captured operations, reverting to original state |
discard() | () => void | Destroys the scrub; captured changes are abandoned |
Notes
- Multiple
capture()calls on the same scrub do not create multiple undo entries — onlycommit()creates one reset()reverts values to what they were before the firstcapture()in this scrub- After
commit()ordiscard(), the scrub object should not be reused
Related
- studio.transaction
studio.selection
Provides read and write access to the current selection of sheets and sheet objects in Studio.
Signature / Usage
// Read current selection
const selected = studio.selection // ISheetObject[] | ISheet[]
// Subscribe to selection changes
const unsubscribe = studio.onSelectionChange((newSelection) => {
console.log(newSelection)
})
// Programmatically set selection
studio.setSelection([obj])Options / Props
studio.selection
| Name | Type | Description |
|---|---|---|
studio.selection | `Array<ISheet \ | ISheetObject>` |
studio.onSelectionChange
| Name | Type | Description |
|---|---|---|
callback | `(selection: Array<ISheet \ | ISheetObject>) => void` |
| returns | () => void | Unsubscribe function |
studio.setSelection
| Name | Type | Description |
|---|---|---|
selection | `Array<ISheet \ | ISheetObject>` |
Notes
studio.selectionis a snapshot; useonSelectionChangeto react to changes- Passing an empty array to
setSelection([])clears the selection - Typically used in extensions to sync a custom editor pane with the selected object
Related
- studio.transaction
- Authoring Extensions
studio.transaction
Executes a batch of property changes as a single undoable action. Rolls back automatically if an error is thrown inside the callback.
Signature / Usage
studio.transaction(fn: (api: { set: SetFn; unset: UnsetFn }) => void): voidimport studio from '@theatre/studio'
studio.transaction(({ set, unset }) => {
set(obj.props.x, 10)
set(obj.props.y, 20)
unset(obj.props.z)
})Options / Props
Callback API
| Name | Type | Description |
|---|---|---|
set(pointer, value) | (pointer: Pointer<T>, value: T) => void | Sets the value of a prop by its pointer |
unset(pointer) | (pointer: Pointer<T>) => void | Reverts a prop to its default value |
Notes
- All changes inside one
transaction()call appear as a single undo level in Studio - If an error is thrown inside
fn, all changes within that transaction are rolled back - For incremental changes (e.g., dragging a slider) use
studio.scrub()instead to avoid creating many undo entries
Related
- studio.scrub
- studio.selection
Studio UI
Theatre.js Studio's editor interface, accessible during development. Provides panels for editing scene hierarchy, properties, and animations.
Overview
The Studio is activated by calling studio.initialize() and can be toggled with Alt/Option + \.
┌──────────────────────────────────────────────────────┐
│ Global Toolbar │
├──────────────┬───────────────────────────────────────┤
│ │ │
│ Outline │ Details Panel │
│ Panel │ (selected object's props) │
│ │ │
├──────────────┴───────────────────────────────────────┤
│ Sequence Editor (Dope Sheet / Graph Editor) │
└──────────────────────────────────────────────────────┘Panels
Outline Panel
Displays the scene hierarchy: Projects → Sheets → Namespaces → Sheet Objects. Used to navigate and select items.
Details Panel
Shows all properties of the currently selected Sheet Object. Number props can be scrubbed by dragging (Alt/Option + drag for fine control).
Sequence Editor
Theatre.js' animation sequencer. Shows sequenced properties as keyframe tracks for the active Sheet Object.
- Dope Sheet (right section): Keyframe visualization and editing. Drag to select keyframes; right-click for context menu (copy, paste, delete).
- Graph Editor: Curve editor for easing between keyframes. Navigate presets with arrow keys;
Enterto confirm,Escapeto cancel.
Global Toolbar
Buttons and switches registered by extensions. Provides access to extension-defined actions.
Extension Panes
Draggable, resizable windows opened by extensions via studio.createPane(). Can host custom HTML content and render toolsets via studio.ui.renderToolset().
studio.ui API
studio.ui.hide() // Hide the Studio overlay
studio.ui.restore() // Show the Studio overlay
studio.ui.isHidden // boolean
studio.ui.renderToolset(toolsetId, domNode) // Mount a toolbar into a DOM nodeNotes
- The Studio only appears when
studio.initialize()is called; it has no effect in production if the call is guarded studio.ui.hide()/studio.ui.restore()are useful for integrating with other overlays or entering a preview mode
Related
- studio.initialize
- Keyboard & Mouse Controls
- Authoring Extensions
Audio Sync
Synchronize sequence playback with audio using sequence.attachAudio().
import { getProject } from '@theatre/core'
const project = getProject('Audio Project', { state: projectState })
const sheet = project.sheet('Scene')
// Basic: attach audio from a URL, then play
sheet.sequence.attachAudio({ source: '/music.mp3' }).then(() => {
sheet.sequence.play()
})
// Advanced: control volume via a custom Web Audio graph
sheet.sequence.attachAudio({ source: '/music.mp3' }).then((audioGraph) => {
const { audioContext, gainNode } = audioGraph
// Disconnect the built-in gain node and wire in a lower-volume node
gainNode.disconnect()
const loweredGain = audioContext.createGain()
loweredGain.gain.setValueAtTime(0.1, audioContext.currentTime)
gainNode.connect(loweredGain)
loweredGain.connect(audioContext.destination)
sheet.sequence.play()
})
// Alternative: provide a pre-decoded AudioBuffer
const audioContext = new AudioContext()
const response = await fetch('/music.mp3')
const arrayBuffer = await response.arrayBuffer()
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer)
sheet.sequence.attachAudio({
source: audioBuffer,
audioContext,
destinationNode: audioContext.destination,
}).then(() => {
sheet.sequence.play()
})Notes
attachAudiofetches (or decodes) the audio, sets up a Web Audio API graph, and locks playback to the sequence position- The returned
audioGraphobject exposesaudioContext,gainNode, anddestinationNodefor custom routing - Audio playback is subject to browser autoplay policies; calling
play()inside a user gesture avoids blocking - Providing a pre-created
AudioContextallows reuse of an existing audio graph (e.g., for effects chains)
Basic Animation Setup
Set up a Theatre.js project with getProject → sheet → object → sequence playback.
import { getProject, types } from '@theatre/core'
import studio from '@theatre/studio'
// Initialize Studio (development only)
if (import.meta.env.DEV) {
studio.initialize()
}
// 1. Create a project (acts as a save file)
const project = getProject('My Project')
// 2. Create a sheet to group related objects
const sheet = project.sheet('Animated Scene')
// 3. Define an object with animatable props
const obj = sheet.object('Box', {
position: types.compound({
x: types.number(0, { range: [-5, 5] }),
y: types.number(0, { range: [-5, 5] }),
}),
opacity: types.number(1, { range: [0, 1] }),
})
// 4. React to value changes
obj.onValuesChange((values) => {
mesh.position.x = values.position.x
mesh.position.y = values.position.y
mesh.material.opacity = values.opacity
})
// 5. Play the sequence after project is ready
project.ready.then(() => {
sheet.sequence.play({ iterationCount: Infinity, range: [0, 3] })
})Notes
getProjectreturns the same instance if a project with that name already exists- A sheet groups objects that animate together; one scene typically uses one sheet
onValuesChangefires on every frame during playback and on manual edits in Studio- Use
{ state: projectState }ingetProjectto load exported animation JSON in production
HTML DOM Animation
Animate HTML/SVG elements by connecting Theatre.js prop values to DOM style properties.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<style>
body { margin: 0; background: black; color: white; font-family: sans-serif; }
</style>
</head>
<body>
<h1 id="heading" style="text-align: center">Welcome</h1>
<script type="module">
import 'https://cdn.jsdelivr.net/npm/@theatre/browser-bundles@0.5.0-insiders.88df1ef/dist/core-and-studio.js'
const { core, studio } = Theatre
studio.initialize()
const project = core.getProject('HTML Animation')
const sheet = project.sheet('Sheet 1')
const obj = sheet.object('Heading', {
y: 0,
opacity: core.types.number(1, { range: [0, 1] }),
})
const el = document.getElementById('heading')
obj.onValuesChange(({ y, opacity }) => {
el.style.transform = `translateY(${y}px)`
el.style.opacity = opacity
})
// Play after export: replace core-and-studio.js with core-only.min.js
// and pass { state: projectState } to getProject
project.ready.then(() => {
sheet.sequence.play({ iterationCount: Infinity, range: [0, 6] })
})
</script>
</body>
</html>Notes
- The
core-and-studioCDN bundle includes both@theatre/coreand@theatre/studio; swap it forcore-only.min.jsin production onValuesChangereceives the current value of every prop on each animation frame, making direct DOM style mutation straightforward- Any CSS-animatable property (transform, opacity, color, etc.) can be driven this way — no framework required
rangeinsequence.playlimits playback to a subsection of the timeline (in seconds)
React Three Fiber Integration
Animate R3F scene objects with @theatre/r3f's editable wrapper and SheetProvider.
import { createRoot } from 'react-dom/client'
import React, { useEffect } from 'react'
import { Canvas } from '@react-three/fiber'
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'
import { SheetProvider, editable as e, PerspectiveCamera } from '@theatre/r3f'
import { getProject } from '@theatre/core'
import demoProjectState from './state.json'
// Initialize Studio with the R3F extension
studio.initialize()
studio.extend(extension)
// Create project with saved state
const sheet = getProject('Demo Project', { state: demoProjectState }).sheet('Demo Sheet')
const App = () => {
useEffect(() => {
sheet.project.ready.then(() =>
sheet.sequence.play({ iterationCount: Infinity, range: [0, 1] })
)
}, [])
return (
<Canvas>
<SheetProvider sheet={sheet}>
{/* Camera controlled by Theatre.js */}
<PerspectiveCamera theatreKey="Camera" makeDefault position={[5, 5, -5]} fov={75} />
<ambientLight />
{/* Wrap R3F primitives with editable() */}
<e.pointLight theatreKey="Light" position={[10, 10, 10]} />
<e.mesh theatreKey="Cube">
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial color="orange" />
</e.mesh>
</SheetProvider>
</Canvas>
)
}
createRoot(document.getElementById('root')!).render(<App />)Notes
studio.extend(extension)adds the R3F viewport overlay to Studio for in-scene editing- The
editable as eprefix wraps any R3F primitive (e.g.,e.mesh,e.pointLight) to make it animatable theatreKeyis required and must be unique within the sheet; it links the R3F element to its Theatre.js objectSheetProviderscopes alleditablechildren to the given sheet- Export animation from Studio to
state.json, then pass it togetProjectfor production builds
samples
| Name | Description | Path |
|---|---|---|
| Basic Animation Setup | Set up a Theatre.js project with getProject → sheet → object → sequence playback. | basic-animation-setup.md |
| React Three Fiber Integration | Animate R3F scene objects with @theatre/r3f's editable wrapper and SheetProvider. | react-three-fiber-integration.md |
| Audio Sync | Synchronize sequence playback with audio using sequence.attachAudio(). | audio-sync.md |
| Studio Editing Workflow | Use Theatre.js Studio to interactively edit animations during development and export state for production. | studio-editing-workflow.md |
| HTML DOM Animation | Animate HTML/SVG elements by connecting Theatre.js prop values to DOM style properties. | html-dom-animation.md |
Studio Editing Workflow
Use Theatre.js Studio to interactively edit animations during development and export state for production.
import { getProject, types } from '@theatre/core'
import studio from '@theatre/studio'
// --- Development: initialize Studio ---
studio.initialize()
const project = getProject('My Project')
const sheet = project.sheet('Scene')
const obj = sheet.object('Torus Knot', {
rotation: types.compound({
x: types.number(0, { range: [-2, 2] }),
y: types.number(0, { range: [-2, 2] }),
z: types.number(0, { range: [-2, 2] }),
}),
color: types.rgba({ r: 1, g: 0.5, b: 0.2, a: 1 }),
})
obj.onValuesChange(({ rotation, color }) => {
mesh.rotation.set(
rotation.x * Math.PI,
rotation.y * Math.PI,
rotation.z * Math.PI,
)
mesh.material.color.setRGB(color.r, color.g, color.b)
})
// --- Production: load exported state, omit studio.initialize() ---
// import projectState from './state.json'
// const project = getProject('My Project', { state: projectState })
// project.ready.then(() =>
// sheet.sequence.play({ iterationCount: Infinity })
// )Notes
- Right-click a prop name in the Details Panel and choose "Sequence" to enable keyframe editing for that prop
- After authoring keyframes, export state via the Outline Panel → click project name → "Export [Project Name] to JSON"
- Pass the downloaded
state.jsonas the second argument{ state }togetProjectin production - Remove
studio.initialize()in production builds; tree-shaking (Vite/webpack) strips the studio bundle automatically when not imported - Toggle Studio visibility at runtime with Alt/Option + \\ (backslash)
Animation
シーケンス(アニメーション)の再生・制御コードスニペット集。
シーケンスの再生
sheet.sequence.play()無限ループ再生
project.ready.then(() => sheet.sequence.play({ iterationCount: Infinity }))シーケンスの一時停止
sheet.sequence.pause()現在の再生位置の取得
const position = sheet.sequence.positionオーディオのアタッチ(URL 指定)
sheet.sequence.attachAudio({
source: 'http://localhost:3000/audio.mp3',
}).then(() => {
console.log('Audio loaded!')
})オーディオのアタッチ(Web Audio API カスタムグラフ)
const audioContext = new AudioContext()
sheet.sequence.attachAudio({
source: audioBuffer,
audioContext,
destinationNode: audioContext.destination,
})オーディオのゲイン調整(カスタムルーティング)
sheet.sequence.attachAudio({
source: '/music.mp3',
}).then((audioGraph) => {
const { audioContext, gainNode } = audioGraph
gainNode.disconnect()
const loweredGain = audioContext.createGain()
loweredGain.gain.setValueAtTime(0.1, audioContext.currentTime)
gainNode.connect(loweredGain)
loweredGain.connect(audioContext.destination)
})Install
Theatre.js パッケージのインストールコマンド集。
@theatre/core と @theatre/studio のインストール(npm)
npm install --save @theatre/core @theatre/studio@theatre/core と @theatre/studio のインストール(yarn)
yarn add @theatre/core @theatre/studioバージョン固定インストール — 0.5 系(npm)
npm install --save @theatre/core@0.5 @theatre/studio@0.5バージョン固定インストール — 0.5 系(yarn)
yarn add @theatre/core@0.5 @theatre/studio@0.5React Three Fiber 統合パッケージのインストール(npm)
npm install --save react three @react-three/fiber
npm install --save @theatre/core@0.5 @theatre/studio@0.5 @theatre/r3f@0.5
npm install --save-dev @types/threeReact Three Fiber 統合パッケージのインストール(yarn)
yarn add react three @react-three/fiber
yarn add @theatre/core@0.5 @theatre/studio@0.5 @theatre/r3f@0.5
yarn add --dev @types/threetheatric(React 向けコントロール UI)のインストール
npm install theatricCDN 経由での読み込み(バンドラー不使用・開発用)
<script type="module">
import 'https://cdn.jsdelivr.net/npm/@theatre/browser-bundles@0.5.0-insiders.88df1ef/dist/core-and-studio.js'
</script>CDN 経由での読み込み(バンドラー不使用・本番用)
<script type="module">
import 'https://cdn.jsdelivr.net/npm/@theatre/browser-bundles@0.5.0-insiders.88df1ef/dist/core-only.min.js'
</script>core-only.min.js は Studio UI を含まない本番向けビルド。アニメーション再生のみ必要な場合に使用する。
Three.js スターターリポジトリのクローンと起動
git clone https://github.com/fulopkovacs/vanilla-threejs-project
cd vanilla-threejs-project
npm install
npm run devR3F
@theatre/r3f(React Three Fiber 統合)のコードスニペット集。
SheetProvider によるシートのバインド
import { Canvas } from '@react-three/fiber'
import { SheetProvider } from '@theatre/r3f'
import { getProject } from '@theatre/core'
const demoSheet = getProject('Demo Project').sheet('Demo Sheet')
function App() {
return (
<Canvas>
<SheetProvider sheet={demoSheet}>
{/* editable コンポーネントを配置 */}
</SheetProvider>
</Canvas>
)
}editable コンポーネントの使用
import { editable as e } from '@theatre/r3f'
<e.pointLight theatreKey="Key light" />
<e.group theatreKey="My group" />
<e.mesh theatreKey="Marker" visible="editor">
<boxBufferGeometry />
<meshBasicMaterial color="yellow" />
</e.mesh>theatreKey は Theatre.js オブジェクトの識別子として必須。
カスタムコンポーネントの editable ラップ
import { editable } from '@theatre/r3f'
import { PerspectiveCamera } from '@react-three/drei'
const EditableCamera = editable(PerspectiveCamera, 'perspectiveCamera')PerspectiveCamera の配置
import { PerspectiveCamera } from '@theatre/r3f'
<PerspectiveCamera theatreKey="Camera" makeDefault position={[0, 0, 16]} fov={75} />lookAt でオブジェクト追尾も可能:
<PerspectiveCamera theatreKey="Camera" makeDefault position={[0, 0, 16]} fov={75} lookAt={ref} />useCurrentSheet によるシート参照の取得
import { useCurrentSheet } from '@theatre/r3f'
function MyComponent() {
const sheet = useCurrentSheet()
// sheet.sequence.play() 等が利用可能
}RefreshSnapshot による Suspense 対応
import { RefreshSnapshot } from '@theatre/r3f'
<Suspense fallback={null}>
<RefreshSnapshot />
<MyModel />
</Suspense>手動スナップショット更新
import { refreshSnapshot } from '@theatre/r3f'
refreshSnapshot()Studio + R3F 拡張の初期化(開発環境・Vite)
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'
import { getProject } from '@theatre/core'
if (import.meta.env.DEV) {
studio.initialize()
studio.extend(extension)
}
const demoSheet = getProject('Demo Project').sheet('Demo Sheet')scripts
| Name | Description | Path |
|---|---|---|
| Animation | Code snippets for playback and control of sequences (animations) | animation.md |
| Install | Package installation commands for Theatre.js | install.md |
| R3F | Code snippets for @theatre/r3f (React Three Fiber integration) | r3f.md |
| Setup | Code snippets for project, sheet, and object initialization | setup.md |
| Studio | Code snippets for Theatre.js Studio operation and programmatic control | studio.md |
| Theatric | Code snippets for theatric package (React control UI) | theatric.md |
Setup
プロジェクト・シート・オブジェクトの初期化コードスニペット集。
プロジェクトの作成
import { getProject } from '@theatre/core'
const project = getProject('My Project')getProject は同名プロジェクトが既に存在する場合、それを返す。
保存済み状態の読み込み
import { getProject } from '@theatre/core'
import projectState from './state.json'
const project = getProject('My Project', { state: projectState })プロジェクトの読み込み完了を待機
project.ready.then(() => console.log('Project loaded!'))シートの作成
const sheet = project.sheet('Animated scene')シートオブジェクトの作成
const obj = sheet.object('My Object', {
position: { x: 0, y: 0 },
})Prop タイプを明示したオブジェクト定義
import { getProject, types } from '@theatre/core'
const obj = sheet.object('Torus Knot', {
rotation: types.compound({
x: types.number(0, { range: [-2, 2] }),
y: types.number(0, { range: [-2, 2] }),
z: types.number(0, { range: [-2, 2] }),
}),
})値変化の購読
const unsubscribe = obj.onValuesChange((values) => {
mesh.rotation.x = values.rotation.x * Math.PI
})
// 購読解除
unsubscribe()onChange / val によるポインター購読
import { onChange, val } from '@theatre/core'
onChange(obj.props.x, (newValue) => console.log(newValue))
console.log(val(obj.props.position.x))オブジェクトの名前空間(階層化)
const box0 = sheet.object('Basics / Boxes / box-0', { x: 0 })
const box1 = sheet.object('Basics / Boxes / box-1', { x: 0 })オブジェクトの再構成
const obj = sheet.object('obj', { foo: 0 })
const obj2 = sheet.object('obj', { bar: 0 }, { reconfigure: true })reconfigure: true を指定すると既存オブジェクトの Props が置き換わる。
オブジェクトのデタッチ
sheet.detachObject('obj')Studio の初期化(開発環境限定・Vite)
import studio from '@theatre/studio'
if (import.meta.env.DEV) {
studio.initialize()
}Studio の初期化(@theatre/r3f 拡張付き)
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'
if (import.meta.env.DEV) {
studio.initialize()
studio.extend(extension)
}theatric による React コントロール初期化
import { initialize } from 'theatric'
initialize({
assets: { baseUrl: '/theatric-assets' },
})Studio
Theatre.js Studio の操作・プログラム制御コードスニペット集。
Studio の表示・非表示
import studio from '@theatre/studio'
studio.ui.hide()
studio.ui.restore()アニメーション状態の JSON 取得(プログラムによる保存)
const stateJSON = studio.createContentOfSaveFile('My Project')
console.log(JSON.stringify(stateJSON))createContentOfSaveFile は Studio UI の "Export" ボタン相当の動作をプログラムから実行する。
undoable トランザクションによる Prop 値の変更
studio.transaction(({ set, unset }) => {
set(obj.props.x, 10)
unset(obj.props.y)
})スクラブによる Prop 値の変更(undo レベルをまとめる)
const scrub = studio.scrub()
scrub.capture(({ set }) => {
set(obj.props.x, 10)
})
// 変更を確定
scrub.commit()
// または変更を破棄
// scrub.discard()選択状態の設定
studio.setSelection([sheet, obj])選択変化の購読
studio.onSelectionChange((selection) => {
console.log(selection)
})拡張機能の登録
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'
studio.extend(extension)Studio プロジェクトの取得
const studioProject = studio.getStudioProject()ツールセットの DOM レンダリング
studio.ui.renderToolset('my-toolset', document.getElementById('toolbar'))Theatric
theatric パッケージ(React 向けコントロール UI)のコードスニペット集。
基本的な useControls の使用
import { useControls } from 'theatric'
function MyComponent() {
const { name, age } = useControls({ name: 'Andrew', age: 28 })
return <div>{name} is {age}</div>
}数値の範囲・ステップ指定
import { useControls, types } from 'theatric'
const { age } = useControls({
age: types.number(28, { range: [0, 150], nudgeMultiplier: 0.1 }),
})フォルダーによるグループ化
const { x } = useControls({ x: 0 }, { folder: 'Position' })ボタンコントロールの追加
import { useControls, button } from 'theatric'
useControls({
reset: button(() => {
console.log('reset clicked')
}),
})アセット URL の取得
import { useControls, types, getAssetUrl } from 'theatric'
const { image } = useControls({
image: types.image('', {}),
})
const url = getAssetUrl(image)命令型アクセス($get / $set)
const { $get, $set } = useControls({ age: 28 })
// 現在値の取得
const currentAge = $get((values) => values.age)
// 値の設定
$set((values) => values.age, 30)初期化設定(アセットベース URL 指定)
import { initialize } from 'theatric'
initialize({
assets: { baseUrl: '/theatric-assets' },
})