⚙️ Built by David Cannan — DevOps Engineer & AI Solutions Architect
💡 Self-made | #devopsdad | #tripletdad | #hacktheplanet
NodeVideo’s Expression system allows you to create dynamic animations using MiniScript code to control any property (position, opacity, scale, blur, etc.) without manual keyframes. This guide covers cinematic text animations, 3D camera movements, and coordinated layer effects.
- Basic Concepts
- Cinematic Text Animations
- 3D Camera and Object Animation
- Advanced Coordinated Effects
- Best Practices
// Basic time calculation
t = time - thisLayer.startTime
progress = clamp(t / duration, 0, 1)
easeT = ease(progress, 0, 1)
Use index
to create staggered animations across multiple layers:
delay = (index - 1) * 0.2
t = time - (thisLayer.startTime + delay)
Create smooth, professional text reveals with combined opacity and scale effects:
// Configuration
fadeInDuration = 1.5
delay = (index - 1) * 0.2
t = time - (thisLayer.startTime + delay)
progress = clamp(t / fadeInDuration, 0, 1)
easeT = ease(progress, 0, 1)
// Opacity
opacity = easeT
// Scale (subtle zoom-in effect)
startScale = 85
endScale = 100
scale = linear(easeT, startScale, endScale)
// Apply to properties:
// Scale: [scale, scale]
// Opacity: [opacity]
Add vertical movement and subtle drift for dynamic text:
// Position animation
startY = 0.3 + index * 0.05
endY = 0.0
y = linear(easeT, startY, endY)
z = -0.1 * index // Layer depth
// Subtle drift after fade-in
driftT = max(0, t - fadeInDuration)
driftAmt = clamp(driftT / 8.0, 0, 1)
y += driftAmt * -0.02
x = driftAmt * 0.015
// Apply to Position: [x, y, z]
- Main Text Layer: Primary text (white, bold, centered)
- Glow Layer: Duplicate with Gaussian blur/glow effect
- Group: Combine for synchronized animation
fadeDuration = 0.7
t = clamp((time - thisLayer.startTime)/fadeDuration, 0, 1)
ease(t, 0, 0.6) // Max 60% opacity for subtlety
Complete lifecycle animation with entrance and exit:
fadeInDuration = 0.5
fadeOutDuration = 0.5
startFadeOut = thisLayer.outPoint - fadeOutDuration
if (time < thisLayer.startTime) then
0
else if (time < thisLayer.startTime + fadeInDuration) then
t = (time - thisLayer.startTime) / fadeInDuration
ease(t, 0, 1)
else if (time > startFadeOut) then
t = (time - startFadeOut) / fadeOutDuration
ease(1-t, 0, 1)
else
1
end if
Create synchronized animations across multiple properties:
// Shared time base (use in all related expressions)
panDuration = 15.0
loop = true
t0 = thisLayer.startTime
t = time - t0
progress = (t / panDuration)
if loop then
progress = (t % panDuration) / panDuration
end if
theta = ease(progress, 0.0, 1.0)
// Pan left to right
maxPan = 0.02
x = sin(theta * pi) * maxPan
// Apply to Position: [x, 0, 0]
// Rotation swing
maxYRot = 0.6
yRot = sin(theta * pi) * maxYRot
// Apply to Rotation: [0, yRot, 0]
One-time FOV change for cinematic effect:
duration = 3.0
startTime = thisLayer.startTime
t = clamp((time - startTime) / duration, 0, 1)
easeT = ease(t, 0, 1)
startFOV = 35
endFOV = 49
linear(easeT, startFOV, endFOV)
Coordinate multiple layers with shared timing and individual fall animations:
// === Shared pan time control ===
panDuration = 15.0
loop = true
t0 = thisLayer.startTime
t = time - t0
progress = t / panDuration
if loop then
progress = (t % panDuration) / panDuration
end if
theta = ease(progress, 0.0, 1.0)
// === Panning range ===
xStart = -0.6
xEnd = 0.6
zStart = 0.0
zEnd = -0.3
x = ease(theta, xStart, xEnd)
z = ease(theta, zStart, zEnd)
// === Fall away Y motion (staggered by layer index) ===
fallDuration = 1.0
staggerDelay = 0.3
fallDistance = -0.5
fallT = t - (index - 1) * staggerDelay
fallProgress = clamp(fallT / fallDuration, 0, 1)
yStart = 0.6
yEnd = yStart + fallDistance
y = ease(fallProgress, yStart, yEnd)
// Apply to Position: [x, y, z]
// Use same shared time base as position
startRot = 0.6
endRot = -0.6
yRot = ease(theta, startRot, endRot)
// Apply to Rotation: [0.0, yRot, 0.0]
- Use
clamp()
to prevent values from exceeding intended ranges - Minimize complex calculations in expressions
- Cache frequently used values in variables
- Focus Pull Effect: Use Gaussian blur transitioning from 100% to 0% on text entrance
- Letter Tracking: Animate letter spacing in sync with position easing
- Staggered Animation: Use layer index to create cascading effects instead of simultaneous animations
- Text fade-in: 0.5-1.5 seconds for readability
- Scale effects: Keep subtle (85-100% range)
- Stagger delays: 0.1-0.3 seconds between layers
- Drift effects: Use longer durations (8+ seconds) for subtle movement
- Sync text animations with camera movements using shared time variables
- Match FOV changes with text scaling for cohesive visual flow
- Use consistent easing functions across related animations
Property | Expression Return Format | Example |
---|---|---|
Position | [x, y, z] |
[0.5, -0.2, 0.1] |
Rotation | [x, y, z] |
[0, 0.6, 0] |
Scale | [x, y] |
[1.1, 1.1] |
Opacity | value |
0.8 |
FOV | value |
45 |
// Continuous back-and-forth motion
progress = (time % duration) / duration
value = sin(progress * 2 * pi) * amplitude
// Wait before starting animation
startDelay = 2.0
t = max(0, time - thisLayer.startTime - startDelay)
// Overshoot and settle
bounceAmount = 0.1
if (progress > 0.7) then
bounce = sin((progress - 0.7) * 10 * pi) * bounceAmount * (1 - progress)
value = targetValue + bounce
else
value = ease(progress / 0.7, startValue, targetValue)
end if
This guide provides the foundation for creating professional motion graphics in NodeVideo using expressions. Experiment with these examples and combine techniques to achieve your desired cinematic effects.