Communitygithub.com

Kreesten-hsh/video

Esta habilidade deve ser usada quando o usuário solicitar "adicionar legendas animadas", "fazer legendas para TikTok", "legendas de karaokê", "legendas palavra por palavra", "legendas automáticas", "destacar a palavra ativa", "gravar legendas em um vídeo", "sincronizar legendas com uma narração/locução" ou "transformar um SRT/transcrição em texto animado". Abrange temporização de nível de palavra do Whisper, pop-in/scale-in por palavra, destaque da palavra ativa, posicionamento seguro em formato 9:16, tipografia legível e gravação interna versus SRT/VTT externo.

O que é video?

Este skill especializa-se em criar legendas animadas com temporização palavra por palavra, populares em plataformas como TikTok, Reels e Shorts. Ele transforma transcrições (como as geradas pelo Whisper) ou arquivos SRT em legendas visuais onde cada palavra surge com um efeito pop-in e a palavra atualmente sendo falada é destacada, sincronizando perfeitamente com a narração. Suporta tanto a gravação permanente das legendas no vídeo (burn-in) quanto a geração de arquivos SRT/VTT separados para acessibilidade. O skill utiliza ferramentas como Remotion para animação ou CSS para players web, e impõe regras de design focadas em legibilidade em dispositivos móveis, como uso de fontes sans-serif em negrito, traçado/sombra, e posicionamento seguro em áreas centrais do vídeo no formato 9:16. A saída final inclui um vídeo MP4 com legendas gravadas e os arquivos de legenda externos, garantindo que a animação seja baseada em quadros (frame-driven) e não em temporizadores imprevisíveis.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Kreesten-hsh/video/tree/HEAD/.agents/skills/caption-animation

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Animação de Legendas

Transforme uma transcrição ou narração em legendas com temporização de palavra, que param a rolagem: cada palavra surge na sílaba, a palavra ativa é destacada e o texto permanece colado à narração. Crie o tipo de legenda que aumenta o tempo de exibição no TikTok, Reels e Shorts — legível, no ritmo e dentro da área segura.

Quando usar

  • Legendas palavra por palavra ou de karaokê em vídeos verticais de formato curto (9:16).
  • Gravar legendas abertas em uma faixa de narração/locução a partir de uma transcrição.
  • Transformar uma transcrição do Whisper/SRT em texto animado no Remotion ou na web.
  • Reestilizar legendas geradas automaticamente em uma revelação com marca, estilo "Hormozi".

O pipeline

EtapaTarefaFerramenta
TranscreverÁudio → carimbos de tempo de palavraWhisper (@remotion/install-whisper-cpp), AssemblyAI
Normalizar→ tokens { text, startMs, endMs }[]Tipo Caption do @remotion/captions
PaginarAgrupar palavras em "páginas" de 1–4 palavrascreateTikTokStyleCaptions()
AnimaçãoPop-in por palavra + destaque da palavra ativaspring() do Remotion / CSS
PosicionarÁrea segura, tipo legívelregras de layout abaixo
ExportarGravação interna (MP4) e/ou SRT/VTT externoRenderização Remotion / emissão de arquivo

Duas regras inegociáveis

  1. Temporização de palavra, não de linha. O karaokê só parece mágico quando cada palavra cai na sílaba. Sempre transcreva para carimbos de tempo de palavra; nunca os invente dividindo uma linha uniformemente por sua duração — o desvio é instantaneamente visível.
  2. Legibilidade supera estilo. Legendas são lidas em um telefone, sob luz solar, no mudo. Sans-serif em negrito, traçado ou sombra pesados, alto contraste primeiro; decoração depois. Uma legenda que ninguém consegue ler é decoração, não legenda.

Temporização de palavra a partir de uma transcrição

O Whisper emite carimbos de tempo de palavra diretamente. Normalize cada fonte em uma forma de token única para que o renderizador nunca se importe de onde as palavras vieram:

// Token de legenda — a forma única que tudo downstream consome
type Token = { text: string; startMs: number; endMs: number };

// Analisar um bloco de cue SRT "00:00:01,200 --> 00:00:01,640" para ms
const toMs = (t: string) => {
  const [h, m, rest] = t.split(":");
  const [s, ms] = rest.split(",");
  return ((+h * 60 + +m) * 60 + +s) * 1000 + +ms;
};

Se existirem apenas dicas de nível de frase, retranscreva para temporização de palavra — não interpole. O medium.en do Whisper é ~2x mais rápido que o large e preciso o suficiente para áudio de voz limpo.

Pop-in por palavra (Remotion)

Dirija a escala e opacidade de cada palavra com uma mola (spring) ancorada ao seu próprio startMs. O micro-excesso é o que faz parecer "vivo".

import { useCurrentFrame, useVideoConfig, spring, interpolate } from "remotion";

const Word: React.FC<{ token: Token; active: boolean }> = ({ token, active }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const enter = (token.startMs / 1000) * fps;            // quadro de entrada da própria palavra
  const p = spring({ frame: frame - enter, fps, config: { damping: 12, mass: 0.6 } });
  const scale = interpolate(p, [0, 1], [0.6, 1]);        // pop de 60% → 100% (excede)
  return (
    <span style={{
      display: "inline-block",
      transform: `scale(${scale})`,
      opacity: interpolate(p, [0, 1], [0, 1]),
      color: active ? "#FFE45E" : "#FFFFFF",             // destaque da palavra ativa
      transition: "color 80ms linear",
    }}>{token.text}&nbsp;</span>
  );
};

Calcule active testando frame contra [startMs, endMs] de cada token. Mantenha o destaque como uma única cor de acento ou uma "caixa" preenchida atrás da palavra viva — nunca anime a cor de cada palavra de uma vez.

Páginas, não paredes de texto

Mostre de 1 a 4 palavras por vez. Use createTikTokStyleCaptions() do Remotion (ou agrupe manualmente) e ajuste combineTokensWithinMilliseconds: ~200–500ms para verdadeiro palavra por palavra, ~1000–1500ms para frases curtas legíveis.

import { createTikTokStyleCaptions } from "@remotion/captions";
const { pages } = createTikTokStyleCaptions({
  captions, combineTokensWithinMilliseconds: 1200,       // ~2–3 palavras por página
});

Pop em Web/CSS (sem Remotion)

Para um player DOM/ao vivo, dê a cada palavra seu próprio animation-delay igual a startMs:

.word { display:inline-block; opacity:0; animation: pop .26s cubic-bezier(.34,1.56,.64,1) forwards; }
@keyframes pop { from { opacity:0; transform:translateY(.18em) scale(.7) } to { opacity:1; transform:none } }
.word.active { color:#FFE45E; }
words.forEach(w => { const el = mk(w.text); el.style.animationDelay = `${w.startMs}ms`; track.append(el); });

Tipo legível e posicionamento em área segura (9:16, 1080×1920)

PropriedadeValorPorquê
FonteSans-serif Bold/ExtraBold (Montserrat, Inter, Helvetica)Lê-se em telas pequenas e ocupadas
Tamanho56–80px (≈8% da altura do quadro), mínimo 45pxLegível no mudo em um telefone
TraçadoContorno preto sólido de 2–6pxSobrevive a qualquer fundo
SombraSombra projetada suave como backup do traçadoSeparação em quadros claros
CaixaMaiúsculas ou sentença; preenchimento de alto contrasteImpacto + escaneabilidade
Posição verticalFaixa central, ~62–70% para baixoAcima da UI, abaixo da ação
Seguro inferiorManter longe dos ~280px / 15% inferioresEvita UI de legenda/CTA/áudio
Seguro lateralManter dentro dos 80% centrais da larguraEvita ícones da barra lateral direita

Coloque as legendas na faixa central — não fixadas no fundo, onde a UI da plataforma (nome de usuário, áudio, botões) reside. Preencha o texto com no máximo ~2 linhas.

Sincronizar com uma narração / locução

A transcrição já carrega a temporização do áudio exato. Coloque esse mesmo áudio sob a composição e mantenha os carimbos de tempo na base de tempo do áudio — legendas e voz permanecem travados sem ajuste manual. Se a locução for regravada, retranscreva; nunca mude os deslocamentos manualmente.

Gravação interna vs SRT/VTT externo

Gravação interna (aberto)SRT/VTT externo (fechado)
OndePixels no MP4Arquivo .srt/.vtt separado
Social (TikTok/Reels)Obrigatório — garantido, estilizávelFrequentemente ignorado pela plataforma
Acessibilidade (ADA/WCAG)Não satisfaz sozinhoObrigatório (alternável)
Melhor práticaGrave legendas animadas para socialTambém envie um sidecar para web/SEO

Emita ambos: grave as legendas animadas para social e escreva um SRT/VTT simples a partir dos mesmos tokens para reprodução acessível/SEO.

Lista de verificação de saída

  • Carimbos de tempo de palavra (reais, da transcrição) — sem falsificação de divisão uniforme.
  • Pop-in por palavra com micro-excesso; uma cor de acento para a palavra ativa.
  • 1–4 palavras por página; nunca uma parede de texto.
  • Sans-serif em negrito, 56–80px, traçado de 2–6px + sombra, alto contraste.
  • Faixa central, limpa dos ~280px inferiores e barras laterais.
  • Áudio colocado sob a composição; carimbos de tempo na base de tempo do áudio.
  • Envie gravação interna para social e um SRT/VTT externo para acessibilidade.

Entregar e verificar (imagens renderizadas → MP4)

Pacote auxiliar (scripts/): organize suas imagens com scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png, depois verifique a codificação com scripts/probe-mp4.sh out.mp4 [WxH] [fps]. Veja scripts/README.md.

As legendas são enviadas como uma composição do Remotion (<Composition> + schema zod + defaultProps) — todo movimento de palavra é orientado por quadro usando useCurrentFrame(), nunca Date.now() / Math.random() / timers. Entregável = out/*.mp4 (gravado) + o projeto + o SRT/VTT externo. O padrão é vertical 9:16 (1080×1920).

Loop de verificação — renderizar imagens → inspecionar → codificar. A temporização de palavra é o que quebra; verifique-a em quadros exatos antes de gastar tempo em uma codificação.

# Imagens no início / um quadro de palavra ativa amostrado / fim — COM PROPRIEDADES ENVIADAS (tokens reais + áudio)
npx remotion still Captions out/f-start.png --frame=0   --props='{"captionsSrc":"vo.json"}'
npx remotion still Captions out/f-mid.png   --frame=90  --props='{"captionsSrc":"vo.json"}'
npx remotion still Captions out/f-end.png   --frame=N   --props='{"captionsSrc":"vo.json"}'  # N = durationInFrames-1

# Inspecione cada PNG:
#  - a palavra destacada no quadro 90 é a palavra cujo [startMs,endMs] contém 90/fps (sem desvio)
#  - gravação interna legível: sans-serif em negrito, traçado+sombra segura, sem corte
#  - área segura 9:16: legenda na faixa central, limpa do topo ~12% e fundo ~20-35% (legendas/CTA/UI de áudio) e da barra de ação direita

npx remotion render Captions out/captions.mp4 --props='{"captionsSrc":"vo.json"}'   # codificar uma vez que as imagens estejam corretas
npx remotion render Captions out/demo.gif --codec=gif                                # clipe de prova README

Use npx remotion compositions para ler durationInFrames/fps e escolher os quadros de palavra ativa e fim.

Antes de finalizar:

  1. As imagens renderizam limpas no quadro 0, um quadro intermediário de palavra ativa e no último — sem fonte/áudio faltando.
  2. A palavra correta está destacada no quadro amostrado (frame/fps cai dentro de seu token); nenhuma falsificação de divisão uniforme.
  3. A gravação interna é legível (traçado+sombra) e a legenda está completamente dentro da área segura 9:16 em todos os quadros verificados.
  4. Orientado apenas por quadro — sem Date.now() / Math.random() / timers.
  5. As propriedades enviadas (tokens reais, não apenas defaultProps) renderizam corretamente; MP4 + SRT/VTT externo emitidos, GIF opcional.

Arquivos de referência

  • references/word-timed-captions.md — construção ponta a ponta: transcrição Whisper e o tipo Caption, um parser SRT completo, componente Remotion com temporização de palavra e destaque ativo, paginação manual, um emissor SRT/VTT, mapas de área segura por plataforma e uma folha de especificações de tipo legível.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Habilidades Relacionadas