Three.js effect
threeEffect connects Motion’s animation engine to Three.js objects, materials, and uniforms. Register it once, then use the same animate() API you use for DOM elements and plain objects.
import { animate } from '@humanspeak/svelte-motion'
import { threeEffect } from '@humanspeak/svelte-motion/three'
animate.addEffect(threeEffect)
animate(mesh, { x: 1.5, rotateY: 360 }, { type: 'spring' })import { animate } from '@humanspeak/svelte-motion'
import { threeEffect } from '@humanspeak/svelte-motion/three'
animate.addEffect(threeEffect)
animate(mesh, { x: 1.5, rotateY: 360 }, { type: 'spring' })Register at module scope rather than from a component. The effect registry is global and does not reference-count, so removing threeEffect when one component unmounts would unregister it for every other component still animating a mesh. See custom effects for the full rule.
Click Move again to move back, scrub the progress slider in either direction to apply or undo the ripple, or click Reset to restore the initial mesh and shader state immediately.
Supported subjects and values
The adapter claims the following Three.js-shaped subjects without importing three itself:
| Subject | Animated values |
|---|---|
Object3D (meshes, cameras, lights) | x, y, z, rotateX, rotateY, rotateZ, scaleX, scaleY, scaleZ, and scale |
| Materials | Direct properties and colour keys such as color, emissive, and specular |
| Uniforms objects | Any { key: { value } } entry, including vector components such as offsetX |
| TSL uniform nodes | Uniform-node values assigned to slots such as material.colorNode |
Rotation shorthands — rotateX, rotateY, and rotateZ — are in degrees. They are converted to radians on write, like DOM rotate, so a full turn is 360, not Math.PI * 2.
Shader uniforms
Bind a MotionValue directly to a uniforms object when you want to control it independently from the mesh:
import { animate, motionValue } from '@humanspeak/svelte-motion'
import { threeEffect } from '@humanspeak/svelte-motion/three'
const progress = motionValue(0)
const uniforms = { progress: { value: 0 } }
const unbind = threeEffect(uniforms, { progress })
animate(progress, 1, { duration: 1 })
// When the integration is no longer active:
unbind()
progress.destroy()import { animate, motionValue } from '@humanspeak/svelte-motion'
import { threeEffect } from '@humanspeak/svelte-motion/three'
const progress = motionValue(0)
const uniforms = { progress: { value: 0 } }
const unbind = threeEffect(uniforms, { progress })
animate(progress, 1, { duration: 1 })
// When the integration is no longer active:
unbind()
progress.destroy()Frame timing
threeEffect writes values in frame.preRender. Schedule the Three.js render loop with frame.render so every object transform and uniform update lands before the frame is drawn.
import { cancelFrame, frame } from '@humanspeak/svelte-motion'
const render = () => renderer.render(scene, camera)
frame.render(render, true)
// On cleanup, stop the render loop and release any values you bound.
// Leave the effect registered: the registry is global and shared.
cancelFrame(render)
unbind()import { cancelFrame, frame } from '@humanspeak/svelte-motion'
const render = () => renderer.render(scene, camera)
frame.render(render, true)
// On cleanup, stop the render loop and release any values you bound.
// Leave the effect registered: the registry is global and shared.
cancelFrame(render)
unbind()Related
- Custom effects — create and register adapters for other non-DOM subjects
- Three.js effect example — spring a mesh and ripple its shader with complete source
Based on Motion 13.2’s motion/three.