Skip to content

Instantly share code, notes, and snippets.

@Cdaprod
Last active June 11, 2025 17:36
Show Gist options
  • Save Cdaprod/a1c386307773f5776821798a7cdb8687 to your computer and use it in GitHub Desktop.
Save Cdaprod/a1c386307773f5776821798a7cdb8687 to your computer and use it in GitHub Desktop.
Mini script - Motion Expression Fx for NodeVideo

Simple Cinematic Example for Text

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
startScale = 85
endScale = 100
scale = linear(easeT, startScale, endScale)

// Position
startY = 0.3 + index * 0.05
endY = 0.0
y = linear(easeT, startY, endY)
z = -0.1 * index  // Depth

// Subtle drift
driftT = max(0, t - fadeInDuration)
driftAmt = clamp(driftT / 8.0, 0, 1)
y += driftAmt * -0.02
x = driftAmt * 0.015

[scale, scale]        // for Scale
[opacity]             // for Opacity
[x, y, z]             // for Position

💡 Bonus Tips • Use Gaussian blur at 100% → 0% on entrance for “focus pull” feel • Try letter tracking keyframes in sync with position ease (subtle spacing that tightens) • Don’t animate all text at once — use layer index to stagger


In NodeVideo, the feature we're using is typically called Expressions or the Expression Editor—this lets you write MiniScript code to control almost any property (position, opacity, scale, blur, etc.) dynamically, like in After Effects but with their own flavor of scripting. You can use expressions to animate text overlays (or anything else) without manual keyframes.

How to Create Cinematic Text Animations Using NodeVideo Expressions

Here’s how you can recreate that smooth, cinematic overlay look using NodeVideo’s expression system:

A. Set Up Layers

•	Layer 1: Main text (white, bold, centered)
•	Layer 2: Duplicate of main text, placed under main text
•	Apply a Gaussian Blur or Glow (adjust for softness)
•	Lower opacity if needed

Group them if you want to animate together.

B. Smooth Fade & Scale In Expression

On the Main Text Layer’s Opacity:

// Fade in over 0.5s
fadeDuration = 0.5
t = clamp((time - thisLayer.startTime)/fadeDuration, 0, 1)
ease(t, 0, 1)
•	This fades the text from 0 to 100% opacity over half a second.

On the Main Text Layer’s Scale:

// Scale from 110% to 100% for a gentle pop
scaleDuration = 0.7
t = clamp((time - thisLayer.startTime)/scaleDuration, 0, 1)
s = ease(t, 1.1, 1.0)
[s, s]
•	Gives a subtle zoom-in as it appears, matching modern YT shorts style.

On the Blur/Glow Layer’s Opacity:

•	Copy the same expression as main text opacity to sync fade-in.
•	You can add a longer duration for a soft linger:
fadeDuration = 0.7
t = clamp((time - thisLayer.startTime)/fadeDuration, 0, 1)
ease(t, 0, 0.6) // Max out at 60% opacity for subtlety

C. Advanced: Layer Out Animation (Fade/Slide Out)

On Main Text Layer’s Opacity for Out Animation:

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
•	Fades in, stays, then fades out at the end.

D. Combine With FOV/Pullback Sync

If your text should match the camera pullback, use the same t or easeT as your FOV expression, so it animates in sync with camera movement.

Summary

•	Expressions in NodeVideo = mini scripts for animation control (powerful, reusable, no keyframes).
•	Animate opacity and scale for elegant, modern text.
•	Use duplicate blurred text as a glow, also animated with expressions.
•	Sync with your FOV or other video motion for pro results.

Want a full template example with all code blocks? Or do you want suggestions for even more stylized cinematic text reveals (like tracking, letter spacing, etc.)?

Animating 3D Camera + Object Properties

Positon Expression

// Shared time base
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)

// Position pans left ↔ right
maxPan = 0.02
[sin(theta * pi) * maxPan, 0, 0]

Rotation Expression

// Shared time base
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)

// Rotation swings left ↔ right
maxYRot = 0.6
[0, sin(theta * pi) * maxYRot, 0]

One-Time FOV Expression

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)

In this doc—we’ll now combine your shared pan logic (used in position and rotation) with a layer-index-based fall-away effect — all within one elegant expression system that keeps everything coordinated.

🎯 Composite Behavior:

1.	Shared panning movement (position and rotation)
2.	Optional falling away effect (position.y) staggered by index
3.	Clean, reusable structure for both position and rotation expressions

🧩 Shared Base Time Expression (for all expressions)

Put this at the top of both position and rotation expressions:

// === Shared pan time control ===
panDuration = 15.0  // time for one full pan direction
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)

Now theta is your global normalized value (0–1) for smooth transitions.

🧭 POSITION EXPRESSION (X pan + Y fall + Z depth)

// === Panning range (adjust as needed) ===
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) ===
fallDuration = 1.0
staggerDelay = 0.3
fallDistance = -0.5

// Layer-specific fall progress
fallT = t - (index - 1) * staggerDelay
fallProgress = clamp(fallT / fallDuration, 0, 1)
yStart = 0.6
yEnd = yStart + fallDistance

y = ease(fallProgress, yStart, yEnd)

[x, y, z]

🔄 ROTATION EXPRESSION (Y-axis pan based on shared time)

// Same shared time block from above...

startRot = 0.6
endRot = -0.6

yRot = ease(theta, startRot, endRot)
[0.0, yRot, 0.0]

✅ How It Works

Feature Logic

x & z pan Uses theta to ease left → right y fall Staggered via index with time rotation Eased pan back and forth All synced Unified shared time base (theta)

🧪 Optional Tweaks:

•	Want to pause before returning? Wrap theta in yoyo or apply a hold zone.
•	Want individual layer control (e.g. layer("Title").index)? Replace index as needed.

Hope you enjoy—Use the comments to... Let me know if you want:

•	Bounce instead of linear ease on the fall
•	A separate “reveal” trigger (reveal = true)
•	More dynamic camera angles or zoom per layer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment