home / skills / noklip-io / agent-skills / theatre-js

theatre-js skill

/skills/theatre-js

This skill helps you implement and manage Theatre.js animations in web projects, enabling studio timelines, r3f integration, and keyframe workflows.

npx playbooks add skill noklip-io/agent-skills --skill theatre-js

Review the files below or copy the command above to add this skill to your agents.

Files (8)
SKILL.md
5.9 KB
---
name: theatre-js
description: Use when implementing motion design, timeline animations, visual animation editors, animating Three.js/R3F scenes, creating keyframe animations, or using Theatre.js, @theatre/core, @theatre/studio, @theatre/r3f, theatric, or building animation tooling for the web.
---

# Theatre.js Best Practices

Motion design editor and animation library for the web. Provides a visual timeline editor (Studio) with programmatic control.

## Installation

```bash
# Core + Studio (development)
npm install @theatre/[email protected] @theatre/[email protected]

# React Three Fiber integration
npm install @theatre/[email protected]

# React utilities
npm install @theatre/react

# Theatric (simplified controls)
npm install theatric
```

## Quick Start

```tsx
import * as core from '@theatre/core'
import studio from '@theatre/studio'

// Initialize Studio (dev only)
if (import.meta.env.DEV) {
  studio.initialize()
}

// Create project → sheet → object
const project = core.getProject('My Project')
const sheet = project.sheet('Main')
const obj = sheet.object('Box', {
  position: { x: 0, y: 0 },
  opacity: 1
})

// Read values
console.log(obj.value.position.x)

// Listen to changes
obj.onValuesChange((values) => {
  element.style.opacity = values.opacity
  element.style.transform = `translate(${values.position.x}px, ${values.position.y}px)`
})

// Play animation
sheet.sequence.play({ iterationCount: Infinity })
```

## Core Concepts

| Concept | Description |
|---------|-------------|
| **Project** | Container for all animation data; maps to exported JSON state |
| **Sheet** | A scene or component; contains objects and one sequence |
| **Object** | Animatable entity with typed props |
| **Sequence** | Timeline with keyframes; controls playback |
| **Props** | Typed values (number, compound, rgba, etc.) |

## Reference Index

| Reference | Use When |
|-----------|----------|
| `references/01-core.md` | Project setup, sheets, objects, sequences, playback control |
| `references/02-prop-types.md` | Defining props, custom types, compound props, constraints |
| `references/03-studio.md` | Studio UI, keyboard shortcuts, extensions, panels |
| `references/04-react-integration.md` | useVal, usePrism, @theatre/react hooks |
| `references/05-r3f-integration.md` | React Three Fiber, editable components, SheetProvider |
| `references/06-production.md` | Export state, assets, deployment, tree-shaking |

## Common Patterns

### Animate DOM Element

```tsx
const obj = sheet.object('Card', {
  x: 0,
  y: 0,
  rotation: 0,
  scale: 1,
  opacity: 1
})

obj.onValuesChange(({ x, y, rotation, scale, opacity }) => {
  element.style.transform = `translate(${x}px, ${y}px) rotate(${rotation}deg) scale(${scale})`
  element.style.opacity = opacity
})
```

### Sequence Playback Control

```tsx
const seq = sheet.sequence

// Play once
seq.play()

// Play with options
seq.play({
  iterationCount: Infinity,  // loop forever
  range: [0, 2],             // play seconds 0-2
  rate: 1.5,                 // 1.5x speed
  direction: 'alternate'     // ping-pong
})

// Pause and seek
seq.pause()
seq.position = 1.5  // jump to 1.5s

// Await completion
await seq.play({ iterationCount: 1 })
console.log('Animation complete')
```

### React Three Fiber Scene

```tsx
import { Canvas } from '@react-three/fiber'
import { editable as e, SheetProvider } from '@theatre/r3f'
import { getProject } from '@theatre/core'
import studio from '@theatre/studio'
import extension from '@theatre/r3f/dist/extension'

// Dev setup
if (import.meta.env.DEV) {
  studio.initialize()
  studio.extend(extension)
}

const sheet = getProject('R3F Demo').sheet('Scene')

function App() {
  return (
    <Canvas>
      <SheetProvider sheet={sheet}>
        <e.mesh theatreKey="Cube">
          <boxGeometry />
          <meshStandardMaterial color="orange" />
        </e.mesh>
        <e.pointLight theatreKey="Light" position={[10, 10, 10]} />
      </SheetProvider>
    </Canvas>
  )
}
```

### Theatric Controls (Simple)

```tsx
import { useControls, types } from 'theatric'

function Component() {
  const { color, intensity, position } = useControls({
    color: '#ff0000',
    intensity: types.number(1, { range: [0, 2] }),
    position: { x: 0, y: 0, z: 0 }
  })

  return <mesh position={[position.x, position.y, position.z]} />
}
```

## Critical Mistakes to Avoid

### 1. Studio in Production
```tsx
// ❌ Includes studio in bundle
import studio from '@theatre/studio'
studio.initialize()

// ✅ Dev-only initialization
if (import.meta.env.DEV) {
  studio.initialize()
}
```

### 2. Missing State in Production
```tsx
// ❌ No animations without state
const project = core.getProject('My Project')

// ✅ Load exported state
import state from './state.json'
const project = core.getProject('My Project', { state })
```

### 3. Object Key Collisions
```tsx
// ❌ Same key = same object (shared state)
sheet.object('Box', { x: 0 })
sheet.object('Box', { y: 0 })  // Overwrites!

// ✅ Unique keys per object
sheet.object('Box1', { x: 0 })
sheet.object('Box2', { y: 0 })
```

### 4. Missing R3F Extension
```tsx
// ❌ No 3D controls in Studio
studio.initialize()

// ✅ Extend with R3F extension
import extension from '@theatre/r3f/dist/extension'
studio.initialize()
studio.extend(extension)
```

### 5. Forgetting theatreKey
```tsx
// ❌ Not editable
<e.mesh>

// ✅ Requires theatreKey
<e.mesh theatreKey="MyCube">
```

## Quick Reference

| Task | Solution |
|------|----------|
| Create project | `getProject('Name', { state? })` |
| Create sheet | `project.sheet('Name')` |
| Create object | `sheet.object('Key', { props })` |
| Listen to values | `obj.onValuesChange(cb)` |
| Read value | `obj.value.propName` |
| Play animation | `sheet.sequence.play(opts?)` |
| Pause animation | `sheet.sequence.pause()` |
| Seek position | `sheet.sequence.position = 1.5` |
| R3F editable | `<e.mesh theatreKey="Key">` |
| React value hook | `useVal(obj.props.x)` |
| Export state | Studio → Project → Export (JSON) |

Overview

This skill helps implement motion design, timeline animations, and visual animation editors using Theatre.js and related packages. It covers project/sheet/object setup, sequence playback, React and R3F integration, and export/production considerations. Use it to build interactive keyframe workflows, animate DOM or Three.js scenes, and wire Studio-based tooling into web apps.

How this skill works

The skill inspects and guides setup for Theatre.js concepts: projects, sheets, objects (props), and sequences. It shows how to initialize Studio in dev, create typed props, listen to value changes, control playback, and export runtime state for production. It also explains integrations with @theatre/r3f, @theatre/react, and theatric for simplified controls.

When to use it

  • Building keyframed UI or Three.js animations with a visual timeline editor
  • Integrating Theatre Studio into a React or R3F project for iterative motion design
  • Creating animation tooling that exports JSON state for production playback
  • Animating DOM elements or hooking animated values into custom render loops
  • Prototyping motion with a visual timeline and then shipping without Studio

Best practices

  • Initialize studio only in development (guard with import.meta.env.DEV) to avoid shipping dev UI
  • Export and load project state JSON in production so animations work without Studio
  • Give each animatable object a unique key to avoid accidental state collisions
  • Extend Studio with the R3F extension when using React Three Fiber to enable 3D controls
  • Always add theatreKey to editable R3F components so they are visible/editable in Studio

Example use cases

  • Animate a card component by mapping sheet.object props to element transform and opacity
  • Drive a 3D scene in R3F: make mesh and light editable with <e.mesh theatreKey="Cube">
  • Create looping background motion with sheet.sequence.play({ iterationCount: Infinity })
  • Build a lightweight control panel using theatric to expose color, intensity, and position
  • Export project state to JSON and load it at runtime to preserve finalized animations

FAQ

How do I avoid shipping Studio to production?

Only call studio.initialize() inside a dev-only conditional (e.g., import.meta.env.DEV) and exclude @theatre/studio from production bundles.

What if my animations vanish in production?

Load the exported project state JSON when creating the project: getProject('Name', { state }) so the runtime has keyframes without Studio.

How do I make R3F objects editable in Studio?

Initialize the R3F extension (studio.extend(extension)) in dev, wrap scene with SheetProvider, and use editable components with theatreKey attributes.