Three.js

Matt Perry

In this tutorial, we're going to build the Three.js example step-by-step.

This example is rated advanced difficulty, which means we assume you're already quite familiar with Motion (and JavaScript in general).

Here's a live demo of the example we're going to be creating:

Loading...

Introduction

The Three.js example shows how to integrate Motion with Three.js to create smooth 3D animations. We'll build a rotating cube that spins continuously in 3D space.

This tutorial uses two key Motion APIs: animate for creating the rotation animation, and frame for managing Three.js's render loop efficiently.

Get started

Let's start by setting up the basic HTML structure and Three.js scene:

<div id="three-container"></div>

<script type="module">
    import * as THREE from "https://cdn.jsdelivr.net/npm/three@v0.149.0/build/three.module.js"

    const main = document.getElementById("three-container")
    const scene = new THREE.Scene({ alpha: true })
    const camera = new THREE.PerspectiveCamera(
        25,
        main.offsetWidth / main.offsetHeight,
        0.1,
        1000
    )
    const renderer = new THREE.WebGLRenderer({ antialias: true })
    renderer.setSize(main.offsetWidth, main.offsetHeight)
    main.appendChild(renderer.domElement)

    const geometry = new THREE.BoxGeometry()
    const material = new THREE.MeshPhongMaterial({ color: 0x4ff0b7 })
    const cube = new THREE.Mesh(geometry, material)
    renderer.setClearColor(0xffffff, 0)
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
    directionalLight.position.set(2, 2, 2)
    const light = new THREE.AmbientLight(0x404040)
    scene.add(light)
    scene.add(directionalLight)
    scene.add(cube)

    camera.position.z = 5
</script>

<style>
    #three-container {
        width: 300px;
        height: 200px;
    }
<

This creates a basic Three.js scene with a cube, lights, and camera. The cube is green (0x4ff0b7), and we've added both directional and ambient lighting to make it visible.

Let's animate!

Import from Motion

Add Motion to your imports:

import * as THREE from "https://cdn.jsdelivr.net/npm/three@v0.149.0/build/three.module.js"
import { animate, frame } from "motion"

Set up the render loop

Three.js needs to continuously render frames to display animations. Motion's frame.render provides an optimized way to do this:

frame.render(() => renderer.render(scene, camera), true)

The frame.render function takes two arguments: a callback to run on each frame, and a boolean indicating whether to keep the loop running. Setting it to true creates a persistent render loop that calls renderer.render(scene, camera) on every frame.

This pairs better with Motion because it ensures all animations and updates are run before rendering the latest frame.

Add a utility function

Create a helper to convert degrees to radians, which Three.js uses for rotations:

function rad(degrees) {
    return degrees * (Math.PI / 180)
}

Animate the cube

Now we can use Motion's animate function to rotate the cube:

animate(
    cube.rotation,
    { y: rad(360), z: rad(360) },
    { duration: 10, repeat: Infinity, ease: "linear" }
)

The animate function directly animates the cube.rotation object, rotating it 360 degrees on both the Y and Z axes. By setting repeat: Infinity, the animation loops forever. The linear easing ensures the rotation speed stays constant throughout the animation.

What makes this powerful is that Motion can animate any JavaScript object - not just DOM elements. Since Three.js uses regular JavaScript objects for properties like rotation, position, and scale, Motion can animate them natively.

Conclusion

We've built a 3D rotating cube by combining Three.js with Motion. Motion's animate function works with any JavaScript object, making it perfect for animating Three.js properties. The frame API provides an efficient render loop that coordinates with Motion's animation system, ensuring smooth performance.

Motion is supported by the best in the industry.