Propósito
Esta habilidade fornece ao Claude Code e agentes de IA de codificação um contexto abrangente para criar vídeos prontos para produção usando Remotion. Inclui princípios de design, modelos selecionados e melhores práticas.
Quando usar
Use esta habilidade quando:
- Criar conteúdo de vídeo programaticamente com React
- Construir audiogramas de podcast ou visualizações de áudio
- Adicionar legendas animadas a vídeos (estilo TikTok/Reels)
- Criar demonstrações de produtos em 3D
- Construir um SaaS de geração de vídeos
- Fazer vídeos de visualização de dados
- Gerar conteúdo para redes sociais em escala
Princípios de Design de Vídeo
1. Ritmo e Cadência
- Cortes rápidos: 1-2 segundos para energia (redes sociais)
- Ritmo médio: 3-5 segundos para compreensão (tutoriais)
- Ritmo lento: 5-10 segundos para ênfase (cinematográfico)
- Sincronizar com áudio: Transições alinhadas a batidas/fala
2. Hierarquia Visual
- Um ponto focal por quadro
- Use movimento para guiar a atenção
- Contraste em elementos importantes
- Mantenha espaçamento consistente
3. Regras para Texto em Vídeo
- Máximo de 7 palavras por quadro
- Alto contraste (branco sobre escuro, ou com contorno)
- Deixe o texto na tela tempo suficiente para ler 2x
- Posicione no terço inferior ou centro
- Evite bordas (zona segura: margem de 10%)
4. Temporização de Animação
| Elemento | Duração | Suavização (Easing) |
|---|---|---|
| Aparecimento de texto | 8-15 quadros | easeOut |
| Saída de texto | 6-10 quadros | easeIn |
| Transição de cena | 15-30 quadros | easeInOut |
| Revelação de logotipo | 30-60 quadros | spring |
| Corte rápido | 0-3 quadros | linear |
5. Taxas de Quadros
- 30fps: Vídeo padrão, redes sociais
- 60fps: Movimento suave, conteúdo de jogos
- 24fps: Estilo cinematográfico
- 25fps: Transmissão europeia
6. Proporções de Tela
| Plataforma | Proporção | Resolução |
|---|---|---|
| YouTube | 16:9 | 1920x1080 |
| TikTok/Reels | 9:16 | 1080x1920 |
| Feed do Instagram | 1:1 | 1080x1080 |
| Story do Instagram | 9:16 | 1080x1920 |
| 16:9 | 1280x720 | |
| 16:9 | 1920x1080 |
Início Rápido
Instalação
# Novo projeto com seleção de modelo
npx create-video@latest
# Configuração manual
mkdir my-video && cd my-video
npm init -y
npm install remotion @remotion/cli react react-dom
npm install -D typescript @types/react
Estrutura do Projeto
src/
├── index.ts # registerRoot
├── Root.tsx # Definições de Composition
├── compositions/
│ ├── Main.tsx # Vídeo principal
│ └── Intro.tsx # Sequência de introdução
├── components/
│ ├── Text.tsx # Texto animado
│ └── Logo.tsx # Revelação de logotipo
└── assets/
├── fonts/
├── images/
└── audio/
Template Root.tsx
import { Composition } from 'remotion';
import { Main } from './compositions/Main';
export const RemotionRoot = () => {
return (
<>
<Composition
id="Main"
component={Main}
durationInFrames={300}
fps={30}
width={1920}
height={1080}
defaultProps={{
title: "My Video"
}}
/>
</>
);
};
Padrões Principais
Fundamentos de Animação
import { useCurrentFrame, useVideoConfig, interpolate, spring } from 'remotion';
const frame = useCurrentFrame();
const { fps, width, height, durationInFrames } = useVideoConfig();
// Interpolação linear (limitada)
const opacity = interpolate(frame, [0, 30], [0, 1], {
extrapolateRight: 'clamp'
});
// Animação spring (elástica)
const scale = spring({
frame,
fps,
config: { damping: 12, stiffness: 200 }
});
// Spring com atraso
const delayedScale = spring({
frame: frame - 15, // Atraso de 15 quadros
fps,
config: { damping: 10 }
});
Sequenciamento
import { Sequence, AbsoluteFill } from 'remotion';
export const MyVideo = () => (
<AbsoluteFill>
<Sequence from={0} durationInFrames={60}>
<Intro />
</Sequence>
<Sequence from={60} durationInFrames={120}>
<MainContent />
</Sequence>
<Sequence from={180}>
<Outro />
</Sequence>
</AbsoluteFill>
);
Animação de Texto
const TextReveal = ({ text }: { text: string }) => {
const frame = useCurrentFrame();
const words = text.split(' ');
return (
<div style={{ display: 'flex', gap: 10, justifyContent: 'center' }}>
{words.map((word, i) => {
const delay = i * 5;
const opacity = interpolate(frame - delay, [0, 10], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
});
const y = interpolate(frame - delay, [0, 10], [20, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp'
});
return (
<span key={i} style={{ opacity, transform: `translateY(${y}px)` }}>
{word}
</span>
);
})}
</div>
);
};
Sincronização de Áudio
import { Audio, useCurrentFrame, interpolate } from 'remotion';
import { getAudioDurationInSeconds } from '@remotion/media-utils';
// Envelope de volume
const frame = useCurrentFrame();
const volume = interpolate(frame, [0, 30, 270, 300], [0, 1, 1, 0]);
<Audio src={staticFile('music.mp3')} volume={volume} />
Catálogo de Modelos
Legendas e Subtítulos
remotion-subtitles (17 Estilos)
npm install @ahgsql/remotion-subtitles
import { Subtitle } from '@ahgsql/remotion-subtitles';
// Estilos disponíveis: bounce, fire, glitch, neon, typewriter,
// 3d, lightning, glowing, explosive, wave, slide, fade,
// zoom, rotate, flip, shake, pulse
<Subtitle
src={staticFile('captions.srt')}
style="bounce"
fontSize={48}
color="#ffffff"
strokeColor="#000000"
strokeWidth={2}
/>
Legendas Estilo TikTok
const Caption = ({ text, highlight }: { text: string; highlight: number }) => {
const words = text.split(' ');
return (
<div style={{
position: 'absolute',
bottom: '20%',
width: '100%',
textAlign: 'center',
fontSize: 64,
fontWeight: 900,
textTransform: 'uppercase'
}}>
{words.map((word, i) => (
<span
key={i}
style={{
color: i === highlight ? '#FFE135' : '#FFFFFF',
textShadow: '4px 4px 0 #000, -4px -4px 0 #000, 4px -4px 0 #000, -4px 4px 0 #000'
}}
>
{word}{' '}
</span>
))}
</div>
);
};
Visualização de Áudio
Visualizador de Forma de Onda
import { useAudioData, visualizeAudio } from '@remotion/media-utils';
const Waveform = ({ src }: { src: string }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const audioData = useAudioData(src);
if (!audioData) return null;
const visualization = visualizeAudio({
fps,
frame,
audioData,
numberOfSamples: 64
});
return (
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
{visualization.map((amplitude, i) => (
<div
key={i}
style={{
width: 8,
height: amplitude * 200,
backgroundColor: '#667eea',
borderRadius: 4
}}
/>
))}
</div>
);
};
Visualizador de Áudio Circular
const CircularVisualizer = ({ audioData, frame, fps }) => {
const visualization = visualizeAudio({ fps, frame, audioData, numberOfSamples: 32 });
const radius = 150;
return (
<svg width={400} height={400} viewBox="0 0 400 400">
{visualization.map((amp, i) => {
const angle = (i / visualization.length) * Math.PI * 2;
const x1 = 200 + Math.cos(angle) * radius;
const y1 = 200 + Math.sin(angle) * radius;
const x2 = 200 + Math.cos(angle) * (radius + amp * 100);
const y2 = 200 + Math.sin(angle) * (radius + amp * 100);
return <line key={i} x1={x1} y1={y1} x2={x2} y2={y2} stroke="#667eea" strokeWidth={4} />;
})}
</svg>
);
};
3D com Three.js
Configuração
npm install @remotion/three three @react-three/fiber @react-three/drei
Maquete de Telefone
import { ThreeCanvas } from '@remotion/three';
import { useFrame } from '@react-three/fiber';
const PhoneMockup = ({ videoSrc }: { videoSrc: string }) => {
const frame = useCurrentFrame();
const rotation = interpolate(frame, [0, 150], [0, Math.PI * 2]);
return (
<ThreeCanvas>
<ambientLight intensity={0.5} />
<pointLight position={[10, 10, 10]} />
<mesh rotation={[0, rotation, 0]}>
<boxGeometry args={[2, 4, 0.2]} />
<meshStandardMaterial color="#1a1a2e" />
</mesh>
</ThreeCanvas>
);
};
Modelos para Redes Sociais
Introdução para Reels do Instagram
const ReelIntro = ({ username, avatar }: Props) => {
const frame = useCurrentFrame();
const scale = spring({ frame, fps: 30, config: { damping: 12 } });
return (
<AbsoluteFill style={{ background: 'linear-gradient(135deg, #667eea, #764ba2)' }}>
<div style={{
transform: `scale(${scale})`,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100%'
}}>
<img
src={avatar}
style={{ width: 120, height: 120, borderRadius: '50%', border: '4px solid white' }}
/>
<h1 style={{ color: 'white', fontSize: 48, marginTop: 20 }}>@{username}</h1>
</div>
</AbsoluteFill>
);
};
CTA de Inscrição no YouTube
const SubscribeCTA = () => {
const frame = useCurrentFrame();
const slideIn = interpolate(frame, [0, 20], [100, 0], { extrapolateRight: 'clamp' });
const bellWiggle = Math.sin(frame * 0.5) * 10;
return (
<div style={{
position: 'absolute',
bottom: 50,
right: 50,
transform: `translateX(${slideIn}%)`,
display: 'flex',
alignItems: 'center',
gap: 20,
background: 'rgba(0,0,0,0.8)',
padding: '20px 30px',
borderRadius: 50
}}>
<div style={{
background: '#FF0000',
padding: '12px 24px',
borderRadius: 4,
color: 'white',
fontWeight: 'bold'
}}>
INSCREVA-SE
</div>
<div style={{ transform: `rotate(${bellWiggle}deg)`, fontSize: 32 }}>
🔔
</div>
</div>
);
};
Biblioteca de Efeitos
Efeito Glitch
const GlitchText = ({ text }: { text: string }) => {
const frame = useCurrentFrame();
const glitchOffset = Math.random() > 0.9 ? Math.random() * 10 - 5 : 0;
return (
<div style={{ position: 'relative' }}>
<span style={{
position: 'absolute',
left: glitchOffset,
color: 'cyan',
clipPath: 'inset(0 0 50% 0)'
}}>
{text}
</span>
<span style={{
position: 'absolute',
left: -glitchOffset,
color: 'magenta',
clipPath: 'inset(50% 0 0 0)'
}}>
{text}
</span>
<span style={{ color: 'white' }}>{text}</span>
</div>
);
};
Campo de Partículas
const Particles = ({ count = 50 }: { count?: number }) => {
const frame = useCurrentFrame();
const particles = useMemo(() =>
Array.from({ length: count }, () => ({
x: Math.random() * 100,
y: Math.random() * 100,
size: Math.random() * 4 + 2,
speed: Math.random() * 0.5 + 0.2
})),
[]
);
return (
<AbsoluteFill style={{ overflow: 'hidden' }}>
{particles.map((p, i) => (
<div
key={i}
style={{
position: 'absolute',
left: `${p.x}%`,
top: `${(p.y + frame * p.speed) % 100}%`,
width: p.size,
height: p.size,
borderRadius: '50%',
background: 'rgba(255,255,255,0.5)'
}}
/>
))}
</AbsoluteFill>
);
};
Fundo Gradiente
const AnimatedGradient = () => {
const frame = useCurrentFrame();
const hue = frame * 2 % 360;
return (
<AbsoluteFill style={{
background: `linear-gradient(135deg, hsl(${hue}, 70%, 50%), hsl(${(hue + 60) % 360}, 70%, 50%))`
}} />
);
};
Integrações de IA
Geração de Vídeo com IA
npm install openai
import OpenAI from 'openai';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function generateVoiceover(script: string) {
const mp3 = await openai.audio.speech.create({
model: 'tts-1',
voice: 'alloy',
input: script
});
const buffer = Buffer.from(await mp3.arrayBuffer());
return buffer;
}
Geração de Legendas com Whisper
npm install @remotion/media-utils
async function generateCaptions(audioUrl: string) {
const response = await openai.audio.transcriptions.create({
file: audioUrl,
model: 'whisper-1',
response_format: 'srt'
});
return response;
}
Dicas de Desempenho
- Pré-renderizar componentes estáticos usando
useMemo - Evitar re-renderizações movendo estilos estáticos para fora do componente
- Usar
staticFile()para assets em vez de imports - Limitar operações por quadro para manter a taxa de quadros
- Pré-carregar fontes no
useEffect - Escolher a codec correta:
h264: Melhor compatibilidadeh265: Melhor qualidade com tamanho menor (dispositivos mais novos)vp8/vp9: Web, Chromeprores: Pós-produção (arquivos grandes)
- Usar
@remotion/rendererpara renderização programática