Skip to content

Transform3D

The Transform3D class provides a mathematically correct way to compose 3D transformations using matrix operations and quaternions. This eliminates gimbal lock and provides smooth interpolation for 3D animations.

import { Transform3D, Vector3, Quaternion } from "remotion-bits";

Creates a new transform from position, rotation, and scale.

const transform = new Transform3D({
position: new Vector3(10, 20, 30),
rotation: new Quaternion(0, 0, 0, 1),
scale: new Vector3(1, 1, 1),
});

Creates an identity transform (no transformation).

const identity = Transform3D.identity();

Transform3D.fromEuler(x, y, z, position?, scale?, order?)

Section titled “Transform3D.fromEuler(x, y, z, position?, scale?, order?)”

Creates a transform from Euler angles (in radians).

const transform = Transform3D.fromEuler(
Math.PI / 2, // 90° X rotation
0,
0,
new Vector3(10, 20, 30), // position
new Vector3(1, 1, 1), // scale
'XYZ' // rotation order
);

Parameters:

  • x, y, z: Rotation in radians around each axis
  • position: Optional Vector3 for position (default: origin)
  • scale: Optional Vector3 for scale (default: 1,1,1)
  • order: Rotation order (‘XYZ’, ‘XZY’, ‘YXZ’, ‘YZX’, ‘ZXY’, ‘ZYX’)

Creates a transform by decomposing a Matrix4.

import { Matrix4 } from "remotion-bits";
const matrix = new Matrix4();
const transform = Transform3D.fromMatrix(matrix);

All operations return a new transform, leaving the original unchanged.

Adds a translation offset.

const moved = transform.translate(10, 20, 30);

Applies a rotation quaternion.

import { Quaternion, Vector3 } from "remotion-bits";
const q = new Quaternion().setFromAxisAngle(
new Vector3(0, 1, 0), // Y axis
Math.PI / 4 // 45° rotation
);
const rotated = transform.rotate(q);

rotateX(radians), rotateY(radians), rotateZ(radians)

Section titled “rotateX(radians), rotateY(radians), rotateZ(radians)”

Rotates around a specific axis.

const rotated = transform
.rotateX(Math.PI / 4)
.rotateY(Math.PI / 6)
.rotateZ(Math.PI / 8);

Multiplies the scale.

const scaled = transform.scaleBy(2, 2, 2);

Combines two transforms (matrix multiplication).

const parent = Transform3D.fromEuler(0, Math.PI / 4, 0);
const child = Transform3D.identity().translate(10, 0, 0);
// Combine transforms
const world = parent.multiply(child);

Returns the inverse transformation.

const inverted = transform.inverse();
const identity = transform.multiply(inverted);

Interpolates between two transforms with proper quaternion SLERP.

const from = Transform3D.identity();
const to = Transform3D.fromEuler(Math.PI, 0, 0);
const halfway = from.lerp(to, 0.5);

Benefits:

  • Smooth rotation paths (spherical interpolation)
  • No gimbal lock artifacts
  • Mathematically correct scaling

interpolateTransform(from, to, progress, easing?)

Section titled “interpolateTransform(from, to, progress, easing?)”

Helper function for transform interpolation with optional easing.

import { interpolateTransform } from "remotion-bits";
const interpolated = interpolateTransform(
transformA,
transformB,
frame / 100,
(t) => t * t // easing function
);

interpolateTransformKeyframes(keyframes, progress, easing?)

Section titled “interpolateTransformKeyframes(keyframes, progress, easing?)”

Interpolates through multiple keyframe transforms.

import { interpolateTransformKeyframes } from "remotion-bits";
const keyframes = [
Transform3D.identity(),
Transform3D.fromEuler(Math.PI / 2, 0, 0),
Transform3D.fromEuler(Math.PI, Math.PI / 2, 0),
];
const current = interpolateTransformKeyframes(
keyframes,
frame / 200
);

Transforms a Vector3 point.

import { Vector3 } from "remotion-bits";
const point = new Vector3(10, 0, 0);
const transformed = transform.apply(point);

Converts to a three.js Matrix4.

const matrix = transform.toMatrix4();

Converts to a CSS matrix3d() string.

const css = transform.toCSSMatrix3D();
// "matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,10,20,30,1)"
element.style.transform = css;

Converts rotation to Euler angles.

const euler = transform.toEuler('XYZ');
console.log(euler.x, euler.y, euler.z); // radians

Returns the transform components.

const { position, rotation, scale } = transform.decompose();
  • position: Vector3 - The translation component
  • rotation: Quaternion - The rotation component
  • scale: Vector3 - The scale component

While Scene3D components accept numeric props, you can use Transform3D for advanced use cases:

import { Scene3D, Element3D, Transform3D } from "remotion-bits";
// Build transform with chainable API
const transform = Transform3D.identity()
.translate(100, 50, -200)
.rotateY(Math.PI / 4)
.scaleBy(1.5, 1.5, 1.5);
// Apply to element
<Element3D>
<div style={{
transform: transform.toCSSMatrix3D(),
transformStyle: "preserve-3d"
}}>
Content
</div>
</Element3D>
// Parent transform
const parent = Transform3D.fromEuler(0, Math.PI / 4, 0)
.translate(100, 0, 0);
// Child relative to parent
const child = Transform3D.identity()
.translate(50, 0, 0);
// Combine for world position
const worldTransform = parent.multiply(child);
const orbitTransform = (angle: number, radius: number) => {
const x = Math.cos(angle) * radius;
const z = Math.sin(angle) * radius;
return Transform3D.identity()
.translate(x, 0, z)
.rotateY(angle + Math.PI / 2);
};
// Use in animation
const angle = (frame / 60) * Math.PI * 2;
const transform = orbitTransform(angle, 200);
// Define camera keyframes
const keyframes = [
Transform3D.identity(),
Transform3D.fromEuler(0, Math.PI / 2, 0, new Vector3(200, 0, 0)),
Transform3D.fromEuler(0, Math.PI, 0, new Vector3(200, 0, 200)),
];
// Interpolate smoothly
const cameraTransform = interpolateTransformKeyframes(
keyframes,
frame / durationInFrames
);
  1. No Gimbal Lock: Quaternion-based rotation eliminates gimbal lock artifacts
  2. Smooth Interpolation: Natural spherical interpolation paths
  3. Composable: Proper matrix multiplication for hierarchical transforms
  4. Mathematically Correct: Single matrix operation vs. multiple CSS transforms
  5. Type-Safe: Full TypeScript support with three.js types
  6. Chainable: Build complex transforms step-by-step