Remotion keyframes
Skill phamthanhnghia/remotion-agent-skills/skills/remotion-keyframes
Keyframe animation engine for Remotion. Use for precise frame-level animation, interpolate curves, stagger patterns, entrance/exit choreography, and text reveal animations.From its SKILL.md
npx -y skills add phamthanhnghia/remotion-agent-skills --skill remotion-keyframesAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.0 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
Remotion Keyframes
Keyframes are a pose contract: visible states, continuous subject identity, frame-deterministic, verified pixels.
Use remotion-animation for broad scene recipes. Use remotion-cli for full command docs.
Procedure
- Identify the animated subject, visible states, final state, and animation primitive (
springorinterpolate). - Choose the smallest mechanism that proves the prompt.
- Author frame-deterministic keyframes. All values derive from
useCurrentFrame(). - Verify with
npx remotion studio— scrub the full timeline, check first frame, all poses, and final frame. - If proof fails, fix the source keyframes and re-verify before rendering.
Contract
- Name the moving subject.
- Name the poses needed to prove the intended motion, including the final state.
- Keyframe visible channels, not hidden helper state.
- Preserve object identity when continuity matters.
- Hold readable or semantic states long enough to see (minimum 10 frames for text, 6 frames for icon).
- Final frame is part of the animation, not cleanup.
- Do not reset to rest unless requested.
- Do not end on black unless requested.
Runtime Rules
interpolate() and spring():
- all values computed synchronously from
frame— no async inside interpolate/spring calls extrapolateLeft: 'clamp'andextrapolateRight: 'clamp'to prevent value leaking outside intended rangespring({frame, fps, config})— frame must be the RemotionuseCurrentFrame()value, not a derivedDate.now()derivative- For entrances, offset spring:
spring({frame: frame - delay, fps, config}); useMath.max(0, frame - delay)to avoid negative frames driving spring
Never use for render-critical motion:
Date.now()orperformance.now()- unseeded
Math.random()(use Remotion'srandom(seed)instead) - CSS transitions or CSS animations (they don't seek)
requestAnimationFramesetTimeout/setIntervalin render pathuseState/useEffectfor visual-property values
Spring Config Reference
import {spring, useCurrentFrame, useVideoConfig} from 'remotion';
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
// Snappy entrance (default feel)
const scale = spring({frame, fps, config: {damping: 200}});
// Bouncy entrance
const bounce = spring({frame, fps, config: {damping: 8, stiffness: 300}});
// Very smooth, slow
const smooth = spring({frame, fps, config: {damping: 30, stiffness: 80, mass: 2}});
// Sharp exit (reverse spring — animate from 1 to 0)
const {durationInFrames} = useVideoConfig();
const exitFrame = durationInFrames - frame;
const exitScale = spring({frame: exitFrame, fps, config: {damping: 200}});
const scaleValue = 1 - (1 - exitScale) ; // goes from 1 down to 0
Interpolate Recipes
import {interpolate} from 'remotion';
// Fade in over 20 frames
const opacity = interpolate(frame, [0, 20], [0, 1], {extrapolateRight: 'clamp'});
// Slide in from left
const x = interpolate(frame, [0, 30], [-300, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// Multi-stop color shift
const r = interpolate(frame, [0, 30, 60], [255, 100, 0], {extrapolateRight: 'clamp'});
const g = interpolate(frame, [0, 30, 60], [0, 150, 255], {extrapolateRight: 'clamp'});
// Scale pop
const scale = interpolate(frame, [0, 8, 12, 18], [0, 1.15, 0.95, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
Channels
Prefer compositor/visual channels: transform: translateX/Y/Z, transform: scale/rotate/skew, opacity, borderRadius, CSS vars, clipPath, SVG strokeDashoffset.
Avoid layout channels: top/left/right/bottom, width/height, margin/padding — they trigger reflow, are slow, and produce incorrect results when the renderer samples frames in parallel.
Stagger Pattern
const ITEM_COUNT = 5;
const STAGGER_FRAMES = 8; // delay between each item
const items = Array.from({length: ITEM_COUNT}, (_, i) => {
const delayedFrame = Math.max(0, frame - i * STAGGER_FRAMES);
const itemSpring = spring({frame: delayedFrame, fps, config: {damping: 200}});
return {opacity: itemSpring, y: (1 - itemSpring) * 40};
});
Text Animation Pattern
const words = 'Hello Remotion World'.split(' ');
const WordReveal = () => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
return (
<div style={{display: 'flex', gap: 12}}>
{words.map((word, i) => {
const delayedFrame = Math.max(0, frame - i * 6);
const progress = spring({frame: delayedFrame, fps, config: {damping: 200}});
return (
<span
key={i}
style={{
opacity: progress,
transform: `translateY(${(1 - progress) * 20}px)`,
display: 'inline-block',
}}
>
{word}
</span>
);
})}
</div>
);
};
SVG Stroke Draw
// SVG stroke-dasharray draw-on effect
const pathLength = 500; // measure with path.getTotalLength() in development
const drawProgress = interpolate(frame, [0, 60], [0, 1], {extrapolateRight: 'clamp'});
<path
d="M 0 0 L 300 200"
stroke="white"
strokeWidth={4}
fill="none"
strokeDasharray={pathLength}
strokeDashoffset={pathLength * (1 - drawProgress)}
/>
3D Depth (CSS Perspective)
// CSS perspective 3D scene
<div style={{perspective: 800, perspectiveOrigin: '50% 50%'}}>
<div
style={{
transformStyle: 'preserve-3d',
transform: `rotateY(${interpolate(frame, [0, 60], [45, 0], {extrapolateRight: 'clamp'})}deg)`,
}}
>
{/* 3D content */}
</div>
</div>
Mechanism Choice
| Need | Mechanism |
|---|---|
| Entrance with natural deceleration | spring() |
| Timed value transition | interpolate() |
| Multi-stop sequence | interpolate() with multiple input/output stops |
| Many items with ordered entrance | stagger: indexed frame - i * delay in spring() |
| Text reveal (word by word) | stagger spring per word index |
| Stroke grows or traces | strokeDashoffset via interpolate() |
| Shape reveal boundary | clipPath via interpolate() |
| 3D depth scene | CSS perspective + rotateX/Y/Z via interpolate() |
Error Handling
| Failure | Fix |
|---|---|
| Values jump at certain frames | Check for negative frame passed to spring() — clamp with Math.max(0, frame - delay) |
| Animation doesn't seek correctly | Confirm no useState/CSS animation driving the value |
| Black frames at start or end | Check extrapolateLeft: 'clamp' on entrance, extrapolateRight: 'clamp' on exit |
| Text blinking / inconsistent | Ensure random() seed is a stable string, not Math.random() |
| Spring overshoots off-screen | Increase damping (200 = no overshoot, 8 = lots of bounce) |
Done
Verify in Studio: scrub first frame, all visible poses, final frame. Confirm subject-owned motion, no debug overlays, no flash on first frame.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.