In this guide, you'll learn how to animate a 3D scene by integrating Theatre.js into a THREE.js project. Theatre.js can be used with THREE.js to animate things like the camera, lights, material colors, and more.

Prerequisites

We'll start by cloning or downloading the code in the THREE.js + bundler project repository.


# using git
git clone https://github.com/fulopkovacs/vanilla-threejs-project

Alternatively, you can download the ZIP archive of the code and extract it to a folder of your choice.

This code will set up a basic THREE.js scene with a torus know geometry, basic lighting, and a render loop.

This project uses Vite, but you really just need a basic web project set up with a bundler of your choice with some code to set up a basic THREE.js scene.

Once you have your project cloned, navigate to the project folder in your terminal.


# open the repository in your terminal
cd vanilla-threejs-project

And use a package manager of your choice (e.g., npm or yarn) to install dependencies to the ./node_modules folder.


# install the dependencies:
npm install
# and start the dev server:
npm run dev

Or with yarn:


# install the dependencies:
yarn install
# and start the dev server:
yarn run dev

Now, if you open http://localhost:3000 in the browser, you should see something like this:

The sample project screen open in the browser.

Add Theatre.js packages

Now that you have a THREE.js codebase you want to add Theatre.js to, let's install Theatre.js's packages.


# with npm
npm install --save @theatre/core @theatre/studio
# or with yarn
yarn add @theatre/core @theatre/studio

Create an animation

Theatre.js has two essential packages that we need to use in this project. @theatre/studio is the editor GUI that we use to create animations, and @theatre/core plays the animations we've created. If you're using the started project, naviage to the main.ts file and let's import the studio package and initialize the editor:


/* ... */
import * as THREE from 'three'
import studio from '@theatre/studio'
/**
* Theatre.js
*/
studio.initialize()
/* ... */

The editor UI after the studio has been initialized.

Animate the rotation of the TorusKnot

So far, we cannot edit anything using the UI; we need to hook up our THREE.js objects to Theatre.js first. There are a lot of things we could animate in the starting scene, but let's focus on the rotation of the torusKnot for now.

First, create a Theatre.js project using the code below. A Project in Theatre.js is like a save file. Projects are stored in the browser's localStorage, so you don't lose your progress if you close and reopen Theatre.js.


import * as THREE from 'three'
import { getProject } from '@theatre/core'
// Initialize the studio
studio.initialize()
// Create a project for the animation
const project = getProject('THREE.js x Theatre.js')

Now we'll add a Sheet. Sheets are a collection of objects which can be animated together.


/* ... */
// Create a project for the animation
const project = getProject('THREE.js x Theatre.js')
// Create a sheet
const sheet = project.sheet('Animated scene')

Next, we'll create a Sheet Object with the props that you want to animate. Sheets contain one or more objects, that can be animated together. You can customize how these props look and behave in the UI, e.g. you can set minimum and maximum values of a number prop by modifying its range.


import * as THREE from 'three'
import { getProject, types } from '@theatre/core'
/* ... */
scene.add(mesh)
// Create a Theatre.js object with the props you want to
// animate
const torusKnotObj = sheet.object('Torus Knot', {
// Note that the rotation is in radians
// (full rotation: 2 * Math.PI)
rotation: types.compound({
x: types.number(mesh.rotation.x, { range: [-2, 2] }),
y: types.number(mesh.rotation.y, { range: [-2, 2] }),
z: types.number(mesh.rotation.z, { range: [-2, 2] }),
}),
})

The last thing to do is to rotate the torusKnot's mesh based on the values of torusKnotObj. This can be done by listening to the changes of the torusKnotObj and updating the rotation of the torusKnot.


const torusKnotObj = sheet.object('Torus Knot', {
/* ... */
})
torusKnotObj.onValuesChange((values) => {
const { x, y, z } = values.rotation
mesh.rotation.set(x * Math.PI, y * Math.PI, z * Math.PI)
})

Now you're set up to use the Studio to edit and animate the torusKnot's rotation!

Animating objects

To start animating, we first have to sequence the properties of an object we want to animate. To sequence properties: select an object, right-click on a property (or group of properties) in the property editor panel, and click "sequence". After clicking "sequence", the sequence editor will open. Here, we'll sequence the rotation of the torus knot.

To animate the knot's rotation, we'll create two sets of keyframes by clicking on the yellow diamond next to the rotation prop in the Details Panel, one with the playhead at 0s, and one at 3s. We'll then set the first set of keyframes to 0, and the second set to 1.

You can of course experiment with adding more keyframes.

Once we have some keyframes in the sequence editor, we can play our animation by pressing Space.

Tip: If the Studio UI gets in the way, you can hide it by pressing Alt/Option + \.

To learn more about creating animations, see Working with Sequences.

The animated torusKnot

Getting ready for production

All the keyframes you created are saved in your browser's localStorage so that your animation will be remembered between page refreshes. However, now you may want a way to save, share/publish, and programmatically play your animation.

To distribute your animation as a part of your website, export your Theatre.js Project by clicking on "THREE.js x Theatre.js" in the outline menu in the top left of the UI.

Save your animation: step 1

Then click on the "Export THREE.js x Theatre.js to JSON" button on the right.

Save your animation: step 2

This will download a JSON file state.json. Now, we can move state.json to the folder containing our web project, and import the JSON file:


import projectState from './state.json'

Then replace our code from before:


const project = getProject('THREE.js x Theatre.js')

With this code:


const project = getProject('THREE.js x Theatre.js', { state: projectState })

We are now passing the saved animation state to getProject. By doing this,the Theatre.js project will be initialized with the saved animation from state.json instead of the animation saved in localStorage. Don't worry; any changes you make to your animation in Studio will still be saved to localStorage after you do this (your edits will still survive page refreshes).

The last thing left is programmatically playing your animation.

To play an animation, we need to get a reference to its sequence and call the play method on it. Sequence.play accepts a number of options. Here, we are going to instruct Theatre.js to play the animation forever.


// Play the animation on repeat
project.ready.then(() => sheet.sequence.play({ iterationCount: Infinity }))

In summary, your code will now look like the following GitHub repo.

To check what our page looks like without the Studio, we can press Alt/Option + \ to hide it. Alternatively, we can comment out studio.initialize().

Deploying to production

When we are done and ready to deploy our webpage to production, we only need to do two things.

  1. Make sure that we have the latest project state exported to a JSON file and passed to getProject.
  2. Remove studio.initialize and studio.extend.

We can also achieve the last step without manually editing the code every time by using environment-checks and relying on our bundler's tree-shaking feature:


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


// create-react-app
if (process.env.NODE_ENV === 'development') {
studio.initialize()
studio.extend(extension)
}

Next steps

Want to learn more? Take a look at some more in-depth topics from our manual:

Projects

This guide covers creating projects, managing their states, saving and loading their states, and more.

Sheets

This guide covers Sheets in Theatre.js

Sheet Objects

This guide covers Sheet Objects in Theatre.js.

Prop types

Learn how to customize the props you want to animate with Theatre.js. When creating a Sheet Object, we define the props that we want to animate on it. Props can have different types which can be imported from "@theatre/core".

Working with Sequences

In this guide, we'll explore the tools that Theatre.js offers for creating animations.

Assets

Learn about assets in Theatre.js

Using Audio

Learn how to load and synchronize music or narration audio to an animation.

Studio

Learn the different parts of the Studio.

Authoring extensions

The Theatre.js Studio API enables you to define extensions that extend the Studio's UI and/or extend the functionality of Studio.

Keyboard & Mouse Controls

A catalog of controls in Theatre.js Studio.

Advanced uses

Or check out another getting started guide:

With React Three Fiber

Animate a React Three Fiber project using Theatre.js's r3f extension, "@theatre/r3f". This guide assumes that you have a web project with a bundler set up.

With HTML/SVG

How to get started animating HTML elements directly with Theatre.js. This tutorial doesn't require any knowledge beyond HTML + JavaScript.


Was this article helpful to you?

Last edited on February 01, 2024.
Edit this page

Theatre.js
Theatre.js is a design tool in the making. We aim to blur the line between designer/developer, author/consumer, and artist/scientist.
© 2022 Theatre.js Oy – Helsinki.