Animation components and utilities for Remotion video projects. Use when building Remotion compositions with text animations, gradient transitions, particle effects, 3D scenes, or staggered motion effects. Provides example bits (complete compositions) and reusable components that can be installed via jsrepo.
Animation components and utilities for Remotion video projects. Use when building Remotion compositions with text animations, gradient transitions, particle effects, 3D scenes, or staggered motion effects. Provides example bits (complete compositions) and reusable components that can be installed via jsrepo.
Remotion Bits
Animation components and utilities for building Remotion videos. The library's most powerful feature is Scene3D - a camera-based 3D presentation system (like impress.js) that enables cinematic multi-section compositions with flying camera moves, step-aware element animations, and Transform3D position management.
When building any non-trivial composition, prefer Scene3D as your foundation. It handles camera movement, timing, element positioning, and responsive layout all in one system. Individual components (AnimatedText, Particles, etc.) work best as content placed inside Scene3D steps.
Most animation properties accept : a static number OR an array of keyframes interpolated over the animation's duration.
AnimatedValue
opacity: 1 // Static
opacity: [0, 1] // Animate 0→1
opacity: [0, 1, 0.5, 0] // Multi-keyframe: 0→1→0.5→0 evenly spaced
scale: [0.8, 1] // Scale from 80% to 100%
y: [30, 0] // Slide up from 30px offset
Keyframes are evenly distributed across the duration. With 4 keyframes over 60 frames: frame 0→20→40→60.
2. Responsive Sizing with useViewportRect
Never hardcode pixel values. Always use viewport-relative units:
const rect = useViewportRect();
// rect.width - composition width (e.g. 1920)
// rect.height - composition height (e.g. 1080)
// rect.vw - 1% of width (19.2)
// rect.vh - 1% of height (10.8)
// rect.vmin - min(vw, vh) - USE THIS for most sizing
// rect.vmax - max(vw, vh)
// rect.cx, cy - center coordinates
const { vmin } = rect;
Use vmin for font sizes, element dimensions, spacing, and padding. This ensures compositions render identically at 1920×1080, 1080×1920, 3840×2160, etc.
Understanding vmin at Common Resolutions
vmin = min(width, height) / 100. Always compute what vmin equals in pixels for your composition size before choosing multipliers.
Composition Size
Aspect Ratio
vmin (px)
vmin * 8
vmin * 10
vmin * 5
1920×1080
16:9 landscape
10.8
86px
108px
54px
1080×1920
9:16 portrait
10.8
86px
108px
54px
1080×1080
1:1 square
10.8
86px
108px
54px
3840×2160
16:9 4K
21.6
173px
216px
108px
1280×720
16:9 720p
7.2
58px
72px
36px
Font Size Reference (at 1920×1080, vmin = 10.8px)
Use these proven multipliers from production bits. Err on the side of LARGER, not smaller.
Role
Multiplier
Pixels at 1080p
Example
Hero / main title
vmin * 10–15
108–162px
Full-screen headline
Section heading
vmin * 6–8
65–86px
Scene3D step titles
Subheading
vmin * 4–5
43–54px
Card titles, counters
Body / card label
vmin * 2.5–3
27–32px
Feature labels, descriptions
Code / small text
vmin * 1.5–2
16–22px
Code blocks, captions
Fine print
vmin * 1–1.2
11–13px
Code font in dense blocks
Common mistake: using vmin * 3 for headings. At 1080p that's only 32px - fine for body text but too small for any heading. For prominent headings, start at vmin * 8 minimum.
Aspect Ratio Awareness
The aspect ratio determines which dimension is the "min" for vmin:
Landscape (16:9, 1920×1080):vmin is based on height (1080/100 = 10.8). Horizontal space is abundant; vertical space is limited.
Portrait (9:16, 1080×1920):vmin is based on width (1080/100 = 10.8). Vertical space is abundant; horizontal space is limited.
Most common: "easeOutCubic" for entries, "easeInOutCubic" for camera moves.
5. Layout & Display Configuration
CRITICAL: Bits are pre-wrapped with a layout container when displayed in the docs. The wrapper (withShowcaseFill) provides an AbsoluteFill with default styling. When building standalone compositions, you must provide this layout yourself.
The Layout Wrapper
Bits displayed in docs are automatically wrapped with:
Bits use CSS variables for consistent theming. These are available when rendering within the docs but must be defined or replaced with literal values in standalone projects:
Variable
Default Value
Usage
--color-primary
#ec8b49
Accent orange
--color-primary-hover
#fcc192
Light orange / text color
--color-background-dark
#100f0f
Dark background
--color-surface-dark
#1c1b1a
Surface/card background
--color-surface-light
#343331
Lighter surface
--color-border-dark
#100f0f
Dark borders
--color-border-light
#1c1b1a
Light borders
In standalone projects: Replace var(--color-*) with literal hex values, or define these variables in your HTML/CSS.
Common Layout Mistakes
Missing background: Content renders on transparent/white background. Always set backgroundColor on the outermost container.
Missing AbsoluteFill: Scene3D and full-viewport compositions need <AbsoluteFill> from remotion as their root.
Using CSS vars without defining them:var(--color-primary) resolves to nothing in a bare Remotion project. Use literal colors.
Hardcoded pixel sizes: Use vmin-based sizing from useViewportRect() instead.
Missing font settings: Set fontSize, fontWeight, fontFamily, and color explicitly - there are no inherited defaults in Remotion.
Three Layout Patterns
Simple (centered content, uses wrapper defaults):
// Relies on the outer wrapper for background, centering, font
export const Component = () => (
<AnimatedText transition={{ opacity: [0, 1] }}>Hello</AnimatedText>
);
Transform3D represents a 3D transformation (position + rotation + scale) using Three.js internals. It is immutable by convention - every method returns a new instance.
import { Transform3D, Vector3 } from "remotion-bits";
Creating Transforms
const base = Transform3D.identity(); // Origin: position(0,0,0), rotation(0,0,0), scale(1,1,1)
Chaining Transforms
Every method returns a new Transform3D. Chain freely:
const cardPosition = base
.translate(vmin * 50, vmin * -20, 0) // Move right and up
.rotateY(-15) // Rotate around Y axis (degrees)
.scaleBy(1.5); // Scale uniformly by 1.5x
Available Methods
// Position
transform.translate(x, y, z) // Add to position
transform.translate(vector3) // Add Vector3 to position
// Rotation (angles in DEGREES)
transform.rotateX(degrees) // Rotate around X axis
transform.rotateY(degrees) // Rotate around Y axis
transform.rotateZ(degrees) // Rotate around Z axis
transform.rotateAround(origin, axis, degrees) // Rotate around arbitrary point+axis
// Scale
transform.scaleBy(uniform) // Scale all axes equally
transform.scaleBy(sx, sy, sz) // Scale per-axis
// Composition
transform.multiply(other) // Matrix multiplication
transform.inverse() // Invert transform
transform.lerp(target, alpha) // Linear interpolation (0-1)
transform.clone() // Deep copy
// Randomization (deterministic via seed)
transform.randomTranslate([minX, maxX], [minY, maxY], [minZ, maxZ], seed)
transform.randomRotateX([minDeg, maxDeg], seed)
transform.randomRotateY([minDeg, maxDeg], seed)
transform.randomRotateZ([minDeg, maxDeg], seed)
// Conversion
transform.toProps() // → { x, y, z, rotateX, rotateY, rotateZ, scaleX, scaleY, scaleZ, rotateOrder }
transform.toCSSMatrix3D() // → "matrix3d(...)" CSS string
transform.toMatrix4() // → Three.js Matrix4
Using Transform3D with Step/Element3D
The .toProps() method converts a Transform3D to props that Step and Element3D accept:
const position = base.translate(vmin * 50, 0, 0).rotateY(-15);
// Spread directly into Step or Element3D
<Step id="my-step" {...position.toProps()} />
This is equivalent to manually specifying x={...} y={...} z={...} rotateX={...} rotateY={...} rotateZ={...}.
Using Transform3D as Keyframes
Pass Transform3D[] arrays as the transform property for smooth 3D interpolation between positions:
Scene3D creates camera-based 3D presentations (like impress.js). The camera flies between Steps; content is placed in 3D space.
Architecture
Scene3D (perspective, timing)
├── Step (camera target 1) - children visible during this step
├── Step (camera target 2) - children visible during this step
├── ...
├── StepResponsive - element that animates differently per step
├── StepResponsive - another step-aware element
└── (any other children - always rendered)
Scene3D Container
<Scene3D
perspective={1000} // CSS perspective in px (depth effect)
stepDuration={60} // Default frames per step
transitionDuration={60} // Frames for camera transitions between steps
easing="easeInOutCubic" // Camera transition easing
>
{/* Steps and content */}
</Scene3D>
Steps (Camera Targets)
Steps define where the camera flies to. They execute sequentially. Content inside a Step is visible when that step is active.
<Step
id="intro" // Unique identifier (used by StepResponsive)
{...position.toProps()} // Camera target position/rotation/scale
duration={120} // Override stepDuration for this step (optional)
transition={{ // Animate children on step ENTRY (optional)
opacity: [0, 1],
blur: [10, 0],
duration: 20,
}}
exitTransition={{ // Animate children on step EXIT (optional)
opacity: [1, 0],
blur: [0, 10],
}}
>
{/* Content shown during this step */}
<FloatingCard>...</FloatingCard>
</Step>
stepDuration (on Scene3D): default duration each step is active
duration (on Step): override for specific step
transitionDuration (on Scene3D): how long the camera takes to move between steps
Total composition should be ≥ sum of all step durations
Element3D (3D Positioned Content)
Places content at a specific 3D position, independent of camera:
<Element3D
centered // Center-align the element (transform-origin: center)
x={vmin * 50} y={0} z={0} // Position in 3D space
style={{ width: vmin * 60 }}
transition={{ // Animate on mount (optional)
delay: 20,
opacity: [0, 1],
duration: 35,
transform: [startTransform, endTransform], // 3D keyframes
easing: "easeInOutCubic",
}}
>
<div>Content positioned in 3D space</div>
</Element3D>
StepResponsive (Step-Aware Animations)
The key to complex scenes. Elements define how they should look/position at each step, and animate between states when the camera moves:
<StepResponsive
centered // Center the child
style={{ position: 'absolute', fontSize }}
steps={{
// Key = step ID, Value = properties at that step
'intro': {
transform: [base, shiftedPosition], // Transform3D keyframes
opacity: [0, 1],
},
'elements': {
transform: [elementPosition], // Hold at this position
},
'outro': {
transform: [outroStart, outroEnd],
opacity: [1, 1, 1, 0], // Hold visible, then fade
duration: "step", // Match step duration
easing: "easeInOutCubic",
},
}}
>
<h1>Title That Follows Camera</h1>
</StepResponsive>
StepResponsive key behaviors:
Properties accumulate/inherit: if step "elements" doesn't set opacity, it keeps the value from the last step that set it
Arrays flatten to their final value when moving to the next step (no re-animation of past keyframes)
transform accepts Transform3D[] arrays - the primary way to position elements in 3D
duration: "step" makes the animation last the entire step duration
You can map the same props to multiple step IDs to hold position across steps
Mapping same props to multiple steps (common pattern):
This is the recommended approach for any non-trivial composition. Scene3D provides camera management, step-based timing, 3D element positioning, and step-responsive animations - eliminating the need to manually manage useCurrentFrame(), <Sequence>, or CSS transforms.\n\n### Step 1: Plan the Scene Structure
Decide on the major sections (acts) and what the camera shows in each:
intro → elements → element-particles → element-text → ... → transitions → scenes → outro
Each section = one Step. Steps execute sequentially.
Step 2: Pre-compute All Positions
Use useMemo to build a position tree. This is THE critical architectural pattern: