@kolbo/kolbo-code-linux-arm64-musl 2.0.8 → 2.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/kolbo
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: remotion-best-practices
|
|
3
|
-
description: Best practices for Remotion - Video creation in React
|
|
3
|
+
description: Best practices for Remotion - Video creation in React. Use for announcement videos, product reveals, multi-scene videos, cinematic effects, camera shake, whip pan, beat-driven timing, brand videos, and launch videos.
|
|
4
4
|
metadata:
|
|
5
|
-
tags: remotion, video, react, animation, composition
|
|
5
|
+
tags: remotion, video, react, animation, composition, motion-design, cinematic, camera, beat-driven, whip-pan, announcement, product-reveal
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
## When to use
|
|
9
9
|
|
|
10
|
-
Use this
|
|
10
|
+
Use this skill whenever you are dealing with Remotion code to obtain the domain-specific knowledge. Also use for any cinematic, beat-driven, or production-quality video work.
|
|
11
11
|
|
|
12
12
|
## Captions
|
|
13
13
|
|
|
@@ -59,3 +59,4 @@ Read individual rule files for detailed explanations and code examples:
|
|
|
59
59
|
- [rules/parameters.md](rules/parameters.md) - Make a video parametrizable by adding a Zod schema
|
|
60
60
|
- [rules/maps.md](rules/maps.md) - Add a map using Mapbox and animate it
|
|
61
61
|
- [rules/voiceover.md](rules/voiceover.md) - Adding AI-generated voiceover to Remotion compositions using ElevenLabs TTS
|
|
62
|
+
- [rules/motion-design.md](rules/motion-design.md) - Cinematic motion design: beat-driven timeline, camera rig, whip pan, masked word reveal, impact flash, narrative rules
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Motion Design & Video Production
|
|
3
|
+
impact: HIGH
|
|
4
|
+
impactDescription: transforms generic animations into professional motion graphics with cinematic camera work, beat-driven timing, and production-quality transitions
|
|
5
|
+
tags: motion-design, announcement-video, cinematic, camera, beat, whip-pan, word-reveal, production
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Beat-Driven Absolute Timeline
|
|
9
|
+
|
|
10
|
+
**Never** nest `<Sequence>` components for multi-scene videos. Define a single `T` object with absolute frame positions — the whole timeline becomes readable and easy to adjust.
|
|
11
|
+
|
|
12
|
+
**Incorrect (fragile nested sequences):**
|
|
13
|
+
|
|
14
|
+
```tsx
|
|
15
|
+
<Sequence from={0} durationInFrames={60}>
|
|
16
|
+
<SceneA />
|
|
17
|
+
</Sequence>
|
|
18
|
+
<Sequence from={60} durationInFrames={90}>
|
|
19
|
+
<SceneB />
|
|
20
|
+
</Sequence>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Correct (absolute beat timeline):**
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
const T = {
|
|
27
|
+
intro: 0,
|
|
28
|
+
codeScene: 58,
|
|
29
|
+
terminal: 98,
|
|
30
|
+
terminalEnd: 248,
|
|
31
|
+
shipScene: 252,
|
|
32
|
+
cta: 450,
|
|
33
|
+
TOTAL: 560,
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
// Components self-hide outside their window:
|
|
37
|
+
if (frame < at || frame >= exit) return null;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Camera Rig — Handheld + 3D Tilt + Z Dolly
|
|
43
|
+
|
|
44
|
+
Every production video needs a camera layer. Wire it once, all content gets it.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
// Slow Z dolly into each section
|
|
48
|
+
const camZ = frame < T.terminal
|
|
49
|
+
? interpolate(frame, [0, T.terminal], [-120, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.quad) })
|
|
50
|
+
: 0;
|
|
51
|
+
|
|
52
|
+
// Organic breathing tilt (barely perceptible — felt not seen)
|
|
53
|
+
const camTiltY = Math.sin(frame / 180) * 1.2;
|
|
54
|
+
const camTiltX = Math.cos(frame / 240) * 0.8;
|
|
55
|
+
|
|
56
|
+
// Ultra-slow handheld drift
|
|
57
|
+
const driftX = Math.sin(frame * 0.028) * 3.0 + Math.sin(frame * 0.061) * 1.5;
|
|
58
|
+
const driftY = Math.cos(frame * 0.035) * 2.5 + Math.cos(frame * 0.073) * 1.2;
|
|
59
|
+
|
|
60
|
+
// Beat-synced camera kick
|
|
61
|
+
const impactAmt = ALL_BEATS.reduce((acc, bf) => {
|
|
62
|
+
const d = frame - bf;
|
|
63
|
+
if (d >= 0 && d < 22) {
|
|
64
|
+
const peak = BIG_BEATS.has(bf) ? 22 : 13;
|
|
65
|
+
return acc + interpolate(d, [0, 22], [peak, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic) });
|
|
66
|
+
}
|
|
67
|
+
return acc;
|
|
68
|
+
}, 0);
|
|
69
|
+
|
|
70
|
+
const shakeX = driftX + Math.sin(frame * 0.71) * impactAmt * 0.65;
|
|
71
|
+
const shakeY = driftY + Math.cos(frame * 0.83) * impactAmt * 0.35;
|
|
72
|
+
const shakeRot = Math.sin(frame * 0.022) * 0.25 + Math.sin(frame * 0.57) * impactAmt * 0.025;
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
JSX structure — outer shake, inner 3D perspective:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
<div style={{
|
|
79
|
+
position: 'absolute', inset: 0,
|
|
80
|
+
transform: `translateX(${shakeX + whipX}px) translateY(${shakeY}px) rotate(${shakeRot}deg)`,
|
|
81
|
+
filter: whipBlur > 0 ? `blur(${whipBlur}px)` : undefined,
|
|
82
|
+
}}>
|
|
83
|
+
<div style={{ position: 'absolute', inset: 0, perspective: '1400px', perspectiveOrigin: '50% 50%' }}>
|
|
84
|
+
<div style={{
|
|
85
|
+
position: 'absolute', inset: 0,
|
|
86
|
+
transform: `rotateX(${camTiltX}deg) rotateY(${camTiltY}deg) translateZ(${camZ}px)`,
|
|
87
|
+
transformStyle: 'preserve-3d',
|
|
88
|
+
}}>
|
|
89
|
+
{/* All scene content here */}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Whip Pan Between Sections
|
|
98
|
+
|
|
99
|
+
Fast lateral translateX + motion blur. Apply to the whole content wrapper at section cut frames.
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
const WHIP_FRAMES = [T.terminal, T.shipScene, T.cta]; // all major cuts
|
|
103
|
+
const WHIP_HALF = 4; // frames each side = 8 total = 0.27s
|
|
104
|
+
|
|
105
|
+
const { whipX, whipBlur } = WHIP_FRAMES.reduce(
|
|
106
|
+
(acc, tf) => {
|
|
107
|
+
const d = frame - tf;
|
|
108
|
+
if (d >= -WHIP_HALF && d < WHIP_HALF) {
|
|
109
|
+
const x = d < 0
|
|
110
|
+
? interpolate(d, [-WHIP_HALF, 0], [0, -420], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.in(Easing.cubic) })
|
|
111
|
+
: interpolate(d, [0, WHIP_HALF], [420, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic) });
|
|
112
|
+
const blur = interpolate(Math.abs(d), [0, WHIP_HALF], [22, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' });
|
|
113
|
+
return { whipX: acc.whipX + x, whipBlur: Math.max(acc.whipBlur, blur) };
|
|
114
|
+
}
|
|
115
|
+
return acc;
|
|
116
|
+
},
|
|
117
|
+
{ whipX: 0, whipBlur: 0 },
|
|
118
|
+
);
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
No overlay effects needed. The displacement + blur IS the transition.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Masked Word Reveal — Soft Gradient Window
|
|
126
|
+
|
|
127
|
+
**Never use `overflow: hidden`** for word reveals — causes hard cuts at container edges.
|
|
128
|
+
Use CSS `mask-image` gradient instead. Words slide through a soft dissolve zone.
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
const maskGrad = [
|
|
132
|
+
'transparent calc(50% - 185px)',
|
|
133
|
+
'black calc(50% - 105px)',
|
|
134
|
+
'black calc(50% + 105px)',
|
|
135
|
+
'transparent calc(50% + 185px)',
|
|
136
|
+
].join(', ');
|
|
137
|
+
|
|
138
|
+
<div style={{
|
|
139
|
+
position: 'absolute', inset: 0,
|
|
140
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
141
|
+
maskImage: `linear-gradient(to bottom, ${maskGrad})`,
|
|
142
|
+
WebkitMaskImage: `linear-gradient(to bottom, ${maskGrad})`,
|
|
143
|
+
}}>
|
|
144
|
+
{words.map((word, i) => {
|
|
145
|
+
const wordAt = at + i * stagger; // stagger: 6-10 frames per word
|
|
146
|
+
const enterY = interpolate(frame, [wordAt, wordAt + enterDur], [215, 0], {
|
|
147
|
+
extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.out(Easing.cubic),
|
|
148
|
+
});
|
|
149
|
+
const exitY = interpolate(frame, [exit - exitDur, exit], [0, -215], {
|
|
150
|
+
extrapolateLeft: 'clamp', extrapolateRight: 'clamp', easing: Easing.in(Easing.cubic),
|
|
151
|
+
});
|
|
152
|
+
return (
|
|
153
|
+
<span key={i} style={{ transform: `translateY(${enterY + exitY}%)`, display: 'inline-block', lineHeight: 1 }}>
|
|
154
|
+
{word}
|
|
155
|
+
</span>
|
|
156
|
+
);
|
|
157
|
+
})}
|
|
158
|
+
</div>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Font size guide:**
|
|
162
|
+
- Brand / hero reveal: `fontSize: 192, fontWeight: 900, letterSpacing: '-8px'`
|
|
163
|
+
- Section headlines: `fontSize: 128, fontWeight: 800, letterSpacing: '-4px'`
|
|
164
|
+
- Never drop below 128px for main titles — inconsistency reads as a bug
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Impact Flash System
|
|
169
|
+
|
|
170
|
+
Beat-synced white flash — screen-space overlay, outside the camera rig.
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
const ALL_BEATS = [T.intro, T.codeScene, T.shipScene, T.cta];
|
|
174
|
+
const BIG_BEATS = new Set([T.intro, T.cta]);
|
|
175
|
+
|
|
176
|
+
const flashOpacity = ALL_BEATS.reduce((acc, bf) => {
|
|
177
|
+
const d = frame - bf;
|
|
178
|
+
if (d >= 0 && d < 8) {
|
|
179
|
+
const peak = BIG_BEATS.has(bf) ? 0.14 : 0.07;
|
|
180
|
+
return Math.max(acc, interpolate(d, [0, 8], [peak, 0], { extrapolateLeft: 'clamp', extrapolateRight: 'clamp' }));
|
|
181
|
+
}
|
|
182
|
+
return acc;
|
|
183
|
+
}, 0);
|
|
184
|
+
|
|
185
|
+
{flashOpacity > 0.005 && (
|
|
186
|
+
<div style={{ position: 'absolute', inset: 0, background: 'white', opacity: flashOpacity, pointerEvents: 'none' }} />
|
|
187
|
+
)}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Only add flash beats for major section cuts — not for UI elements fading in within a scene.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Narrative Order — Promise Before Proof
|
|
195
|
+
|
|
196
|
+
Text headlines BEFORE recordings/demos, not after.
|
|
197
|
+
|
|
198
|
+
- ✅ `"Code anything."` → terminal recording → `"Ship anything."` → desktop recording
|
|
199
|
+
- ❌ terminal recording → `"Code anything."` (that's a caption, not a headline)
|
|
200
|
+
|
|
201
|
+
## Cold Open — No Warmup
|
|
202
|
+
|
|
203
|
+
Frame 0 = the brand name or strongest visual. No intro slides, no warmup.
|
|
204
|
+
If you have a brand name, open on it at 192px. Let the first word be the hook.
|
|
205
|
+
|
|
206
|
+
## Avoid Copy Redundancy
|
|
207
|
+
|
|
208
|
+
Adjacent phrases must not repeat words or contradict each other.
|
|
209
|
+
- ❌ `"Beyond code."` immediately followed by a `"Code."` beat
|
|
210
|
+
- ❌ `"Code anything."` then `"Create anything."` (same structure, same word)
|
|
211
|
+
- ✅ `"Code anything."` → `"Ship anything."` (different verb, completes a story arc)
|
|
212
|
+
|
|
213
|
+
## One Logo Rule
|
|
214
|
+
|
|
215
|
+
Never show the product logo twice. If it appears in a mid-video reveal (e.g. orbit converge scene), remove it from the CTA. Use text only at the end.
|