Communitygithub.com

cxb2523/gsb-828-9f38b84355494457

Crie vídeos animados "renderizados por código" onde um programa desenha cada quadro (Canvas 2D + Web Audio procedural), renderizado headless para MP4 em 16:9 / 9:16 / 4:5 a partir de uma única linha do tempo. Use para explicadores animados, walkthroughs técnicos, diagramas em movimento e som ou música procedural, onde a saída deve ser código editável e não pixels gerados.

O que é gsb-828-9f38b84355494457?

Esta skill permite que um agente de IA crie vídeos animados inteiramente através de código JavaScript, onde cada quadro e cada som são gerados proceduralmente, sem depender de pixels pré-renderizados ou bibliotecas externas. A saída é um arquivo `index.html` autocontido que, quando executado em um navegador headless (Chrome/Chromium), produz um vídeo MP4 em formatos como 16:9, 9:16, 4:5 ou 1:1. O coração do sistema é um contrato rigoroso: o código deve expor funções como `seek(t)` (para desenhar um quadro em um instante exato), `renderAudio()` (para gerar áudio procedural) e uma estrutura de dados `__anim` que permite verificação e ajustes finos. A skill é inspirada em projetos como riso-windowseat, HeyGen HyperFrames e Remotion, e foi projetada para ser usada com ferramentas como Claude Code, Cursor ou Codex. Ela inclui um harness completo com comandos para verificar, renderizar, auditar (lint) e garantir qualidade (QA), assegurando que o vídeo final seja deterministicamente reproduzível, livre de bugs e atenda a padrões de acessibilidade e sincronia audiovisual.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/cxb2523/gsb-828-9f38b84355494457/tree/HEAD

Perguntar na sua IA favorita

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

Documentação

Animações renderizadas por código

A ideia (Addy Osmani, LinkedIn 2026-09-24): um modelo de codificação lê uma fonte (um artigo, uma especificação) e escreve JavaScript que desenha cada quadro, mais o som. O entregável é um programa, então qualquer momento pode ser editado, re-temporizado, re-renderizado ou reformatado. Osmani não publicou seu prompt ou ferramentas. O design desta skill vem do projeto open-source riso-windowseat (MIT, apenas referência, não copie suas instruções): https://github.com/sevenevesai/riso-windowseat . Também pega emprestado ideias do HeyGen HyperFrames (https://hyperframes.heygen.com/guides/pipeline) e Remotion.

Regras básicas: revise quadro a quadro a 4+ amostras/segundo, nunca reivindique uma verificação que não foi executada e corrija defeitos cirurgicamente.

O contrato (tudo depende disso)

Cada trabalho é um index.html autocontido com um <canvas> que lê ?w=&h= da URL e expõe:

window.__anim = {
  duration, fps, width, height, canvas,
  ready,            // false até que fontes/assets sejam carregados e seek(0) tenha desenhado
  seek(t),          // desenha exatamente o quadro no tempo t, síncrono
  renderAudio(),    // Promise<base64 WAV>, 48 kHz estéreo, exatamente com duração `duration`
  marks,            // tempos de eventos visíveis (pops, cortes) - o som deve cair neles
  shots,            // [{id,start,end}] - a verificação confirma os limites
};

Invariantes:

  • seek(t) é uma função pura de t. Cada valor na tela é computado a partir de t: prog(t,a,b), easing, pop(t,at). Nenhum estado é carregado entre quadros, sem temporização requestAnimationFrame, sem Date, sem simulação física a menos que seja recalculada do zero ou em forma fechada.
  • Nunca chame Math.random() durante a renderização. Use rngFor('stable-key') (mulberry32 com semente) e crie layouts aleatórios uma vez no carregamento a partir de chaves fixas. Alterar uma chave altera a saída, então congele as chaves após aprovação.
  • Um objeto de linha do tempo (TL) contém todos os intervalos de cena e tempos de eventos. A imagem e a partitura leem dele. É assim que a sincronia funciona: o som é agendado nos mesmos números que a imagem aparece.
  • Espere pelas fontes antes de ready=true (document.fonts.load('700 40px "DejaVu Sans"')). Caso contrário, os primeiros quadros renderizam com uma fonte alternativa. Verifique as fontes instaladas com fc-list : family (Linux/macOS) ou C:\Windows\Fonts (Windows; Segoe UI, Bahnschrift, Consolas e Georgia estão sempre lá, DejaVu não). Se o design precisar de uma fonte específica, incorpore-a como um @font-face em base64 e carregue da mesma forma.
  • Nenhuma chamada de rede no trabalho. Incorpore quaisquer ativos como URIs de dados.

Elementos editáveis (para correção de filme)

review/film-fix.html permite que um revisor mova e escale elementos diretamente e escreva o resultado de volta no index.html. Filmes que desejam isso (o exemplo faz; copie de lá) adicionam:

  • const TUNE = /*TUNE*/{}/*END-TUNE*/; em uma linha, chaveado por formato e depois id do elemento: {"16x9":{"title":{"dx":0,"dy":0,"s":1}}}. É chaveado por formato porque cada proporção tem seu próprio layout. dx/dy são pixels do canvas. A ferramenta reescreve apenas o texto entre os dois marcadores.
  • E(id, [x,y,w,h], draw) em torno de cada elemento principal. Ele procura TUNE[fmt][id], então salva, translada por dx/dy, escala por s em torno do centro da caixa, chama draw() e restaura. Também insere {id, box} em __anim.elements, onde box é a caixa transformada em pixels do canvas (de ctx.getTransform(), então movimentos de câmera são respeitados). seek(t) limpa essa lista no início de cada quadro.
  • __anim.elements, __anim.format (16x9, 9x16, 4x5, 1x1) e __anim.tune (getter/setter para o objeto TUNE ativo, usado para a pré-visualização ao vivo da ferramenta).
  • Regras: ids são únicos em todo o filme; sem aleatoriedade; com TUNE vazio, E não aplica transformação alguma, então os quadros permanecem idênticos byte a byte. Prove com um hash de quadro antes e depois de adicionar E, então execute verify.mjs.

Filmes sem E ainda funcionam no film-fix: seu modo "Nudge" delimita uma região e transforma um arrasto ou escala em uma correção numérica para a IA.

Configuração do projeto

Requer Node 18+, ffmpeg (libx264, aac, blackdetect, freezedetect, ebur128) e um Chrome/Chromium local. O harness encontra o Chrome nos locais de instalação habituais no Windows, macOS e Linux; defina a variável de ambiente CHROME para substituir. qa.sh precisa de bash (Git Bash no Windows) e escolhe uma fonte do sistema para o timecode; defina QA_FONT para substituir. Nada é instalado globalmente; puppeteer-core é a única dependência npm.

SKILL_DIR=<caminho para esta pasta de skill>
mkdir -p anim && cp -r "$SKILL_DIR/harness" anim/harness
cd anim/harness && npm install && cd ..
mkdir -p works/<nome>
cp "$SKILL_DIR/example/browser-10s/index.html" works/<nome>/index.html   # comece a partir do exemplo funcional
# execute todos os comandos do harness a partir de anim/; as saídas vão para out/

Comandos do harness

ComandoO que fazSinal de aprovação
node harness/verify.mjs works/<n>/index.html --format 16x9Avança, retrocede e faz saltos frios em amostras + limites de cena, depois compara hashes de pixelsverify: OK, saída 0
node harness/shoot.mjs works/<n>/index.html --format 9x16 --times 1.5,3.4 ou --range 3:4:0.1 --out out/stillsPNGs nativos do canvas em tempos exatosOlhe para eles
node harness/audio.mjs works/<n>/index.html --twiceRenderiza apenas a partitura (segundos), verifica o comprimento exato e determinismo entre execuçõesNenhum FAIL. O Web Audio do Chrome pode diferir por 1 LSB entre execuções; tolerância é 1
node harness/render.mjs works/<n>/index.html --format 16x9 --fps 30 --out out/<n>-16x9.mp4Busca cada quadro e canaliza PNGs para ffmpeg (libx264 crf18, yuv420p, +faststart), depois multiplexa renderAudio como AACImprime quadros e tempo. Saída 2 em erros de página. --from/--to ou --silent renderiza um intervalo sem áudio
node harness/lint.mjs works/<n>/index.html --all-formatsPortão de qualidade na própria página (16:9 + 9:16; --format para um): tamanho do texto, área segura, sobreposição de texto, alongamento estático, quadros vazios, lista de cenas, som nos marcos, palavras na telalint: OK (0 FAIL), saída 0
node harness/review.mjs works/<n>/index.html --format 16x9 --out out/review-16x9, depois --check out/review-16x9/REVIEW.mdUma folha de contato por cena (primeiro, 6 quadros uniformemente espaçados e último, com timecode gravado), o JSON do lint e REVIEW.md com perguntas fixas por cena. --check falha em uma resposta vazia ou TODO, uma pontuação abaixo de 4, um FAIL no lint, uma seção de cena removida ou um filme alterado desde que as folhas foram feitasreview check: OK, saída 0
bash harness/qa.sh out/<n>-16x9.mp4 4ffprobe, blackdetect, freezedetect, EBU R128 loudness, comprimento de áudio/vídeo, folhas de contato em N amostras/segundo com timecode gravadoVeja QA abaixo

Formatos: 16x9 1920x1080, 9x16 1080x1920, 4x5 1080x1350, 1x1 1080x1080. Os quadros vão para ffmpeg como JPEG q95 por padrão, cerca de 16 fps em 1080p, então uma peça de 40 s leva cerca de 60-75 s por formato. --png é sem perdas, mas cerca de 4x mais lento.

Renderizações longas: uma chamada de shell do agente pode expirar (frequentemente em 120 s) e levar seus processos filhos. Aumente o tempo limite da chamada ou execute as renderizações completas de forma destacada e verifique o log (Linux/macOS mostrado; no Windows use um shell em segundo plano ou Start-Process):

setsid nohup bash -c 'node harness/render.mjs works/<n>/index.html --format 16x9 --out out/<n>-16x9.mp4; echo RENDER_DONE' > out/render.log 2>&1 < /dev/null & disown
tr '\r' '\n' < out/render.log | tail -2

Mantenha também heredocs que escrevem um arquivo de trabalho separados dos comandos que o executam, para que um tempo limite não possa perder o arquivo.

Definição de pronto

Os comandos exatos estão em AGENTS.md (a fonte única, compartilhada com outras ferramentas de codificação): verifique em cada formato, audio --twice, lint --all-formats com 0 FAIL, review.mjs então review.mjs --check aprovando, renderizar, qa. Nunca reivindique pronto com um portão com falha, e nunca enfraqueça um portão ou edite limites do harness para passar em um.

Fluxo de trabalho

  1. Briefing. O que a fonte diz, o público, o comprimento, os formatos, narração ou não. Extraia as principais afirmações do artigo fonte. Cada cena deve mostrar o que está sendo dito. História antes da correção. O harness torna um filme correto. Não o torna assistível. Antes do storyboard, responda a quatro perguntas no briefing:
    • Quem é o personagem e o que eles sentem em cada cena?
    • Que objeto do cotidiano representa cada ideia? (O original usava uma carta com selo de cera, uma caixa de areia e uma cidade de casas.)
    • Que HUD persistente une as cenas? Geralmente um dial de capítulo e um título digitado.
    • Como o último quadro volta ao primeiro? Um filme sem nenhum desses passa em todos os portões e ainda assim é passado adiante.
  2. STORYBOARD.md antes do código. Uma linha por cena: intervalo de tempo | conteúdo na tela | ação | sugestão de áudio. Liste cada tempo de evento (estes se tornam TL e marks). Planeje um corte ou um novo movimento a cada poucos segundos e sem alongamento estático. Planeje transições como um empurrão ou sobreposição, nunca um mergulho para um quadro vazio. Sem fade-to-black no final, a menos que aprovado.
  3. Módulo de linha do tempo, depois uma função sceneN(t) por cena (pura em t), depois um compositor que desenha o fundo, as cenas ativas e as transições.
  4. Quadro mais difícil primeiro. Construa o momento mais denso, shoot em cada formato, olhe para ele, corrija-o. Depois construa o resto.
  5. verify em cada formato, audio --twice, depois render cada formato.
  6. QA (abaixo), corrija cirurgicamente, re-renderize apenas o necessário, re-QA.
  7. Entregue os MP4s como anexos com os resultados do QA e as fraquezas conhecidas.

Com narração

Primeiro TTS, depois timestamps no nível da palavra (transcreva o WAV com qualquer ferramenta que retorne tempos por palavra), depois defina TL a partir das palavras. Cenas, pops e destaques de legendas karaokê dependem todos dos tempos das palavras. Regras: legendas estilo karaokê em uma caixa, apenas uma faixa de legenda, números na tela quando falados, verifique nomes próprios de ouvido. Incorpore o WAV da narração como uma URI de dados e misture dentro de renderAudio() (decodifique com ctx.decodeAudioData, agende em 0), ou multiplexe com ffmpeg e abaixe a partitura sob ele. Normalize o volume da narração em todo o vídeo.

Multi-formato a partir de uma linha do tempo (não cortes)

  • Mesma TL, mesmas cenas, um layout diferente. Compute portrait = H > W, S = min(W,H)/1080 (escala de tipo e forma), M = 0.06*min(W,H) (margem segura). Layout por aspecto: linhas em 16:9 tornam-se colunas em 9:16, árvores obtêm distribuições mais estreitas, e assim por diante.
  • Texto: encolher para caber (loop measureText contra W-2M) para que nada seja cortado na borda. Os tamanhos vêm de S. Em retrato, use tipo maior e preencha a altura.
  • Não trate "retrato" como um único caso. 4:5 (1080x1350) é muito mais curto que 9:16. O layout em colunas 9:16 do exemplo executado em 4:5 empurra a grade de pixels e a tagline para fora do fundo e um sobre o outro. Ramifique na proporção de aspecto (H/W: cerca de 0.56, 1.25, 1.78) ou faça o layout a partir da altura disponível, e shoot cada formato que você entregar.
  • Lembre-se do overshoot de pop (easeOutBack ~1.1x) e das escalas "quentes" ao verificar margens. Verifique o quadro mais denso de cada formato com shoot.

Som procedural (OfflineAudioContext)

  • new OfflineAudioContext(2, 48000*duration, 48000), construa o grafo, await ctx.startRendering(), codifique para PCM WAV de 16 bits, retorne base64 (veja wavB64 no exemplo).
  • Padrão do exemplo: um acorde pad por cena (pares seno/triângulo desafinados, ataque lento, sobrepondo nos cortes), um pluck (triângulo + 2 harmônicos, ataque de 5 ms, decaimento exponencial) em cada evento marks, cliques de ruído seedado para digitação, um baque de queda de pitch para um impacto, e um DynamicsCompressor no master.
  • Ruído vem do RNG seedado, nunca Math.random.
  • Loudness: aponte para cerca de -14 LUFS integrado e pico abaixo de -1 dBFS para feeds sociais. A postagem original mediu -14.3 LUFS; o exemplo incluído é mais silencioso, a -17.2 LUFS, pico -1.4. Ajuste isso com ganho master na página para que permaneça no código.
  • Para áudio bit-exato, sintetize amostras em JS puro em um Float32Array (soma de osciladores com envelopes de forma fechada) em vez de nós Web Audio.
  • Tone.js Tone.Offline (https://tonejs.github.io/docs/14.9.17/functions/Offline.html) é uma opção se uma API de música mais rica for necessária. Deve ser incorporada inline, pois o trabalho não tem acesso à rede.

QA (a revisão é nossa, não do espectador)

Portões automatizados, todos no MP4 final:

  • verify OK em todos os formatos. Erros de página = falha.
  • ffprobe: tamanho esperado, 30/1 fps, nb_frames = duração x fps, duração do áudio = duração do vídeo.
  • blackdetect: nenhum (a menos que haja uma batida preta dramática aprovada). freezedetect (>=2 s estático): nenhum.
  • ebur128: loudness integrado e pico verdadeiro dentro do alvo. Consistente entre episódios.

Lint (portão de qualidade)

node harness/lint.mjs works/<n>/index.html --all-formats [--json out/lint.json] busca o trabalho a cada 0.1 s (--step) e envolve fillText/strokeText de fora, então funciona em qualquer trabalho que siga o contrato sem alterações. Ele falha em:

  • text-size: texto em repouso abaixo de 2.6% do min(W,H) (28 px em 1080); abaixo de 3.3% (36 px) é um AVISO. Texto encolhido por um zoom-out abaixo da escala 0.5 é tratado como um mapa geral e apenas avisado.
  • safe-area: texto em repouso a menos de 4% de uma borda; 4-6% é um AVISO.
  • text-overlap: duas strings visíveis diferentes compartilhando mais de 15% da caixa menor por 0.2 s ou mais. Passagens mais curtas são AVISOS.
  • static-stretch: mais de 3 s sem mudança visível (2 s é um AVISO). empty-frame: qualquer quadro quase uniforme, primeiro e último incluídos.
  • shots-coverage: lacunas ou sobreposições em shots, ou uma cena com menos de 1 s. audio-sync: abaixo de 70% dos marks com um início de som dentro de ±40 ms (90% para PASS); um pico acima de -1 dBFS é um AVISO. density: mais de 18 palavras na tela em retrato ou 30 em paisagem (apenas AVISO).

Um trabalho não está pronto até que o lint passe com 0 FAIL em cada formato que você entregar. Ele vê apenas texto desenhado com fillText/strokeText no canvas do próprio trabalho, então colisões de forma e texto desenhado em canvases fora da tela ainda precisam de revisão visual. example/browser-10s passa em todos os três formatos; example/browsers-40s foi feito antes do portão e falha nele (rótulos em 17-28 px, um rótulo cortado em 9:16, texto

… [truncado para tradução]

Habilidades Relacionadas