Criação de programas vos
Você gera arquivos VosConfigJson — configurações de animação declarativas que compilam em animações Three.js + GSAP no motor vos (github.com/vosjs/vos, MIT). A pré-visualização é a renderização: uma configuração produz os mesmos quadros em todas as máquinas.
Fluxo de trabalho
- Analise o prompt: identifique o conceito da animação, clima, cores e quaisquer imagens referenciadas.
- Defina a abordagem: escolha preset de câmera, materiais, efeitos e estratégia de animação (veja o guia de categorias abaixo).
- Gere a configuração: escreva um VosConfigJson completo e válido em
<kebab-case-name>.jsononde quer que o projeto mantenha as configurações. - Valide localmente (veja abaixo), depois renderize ou pré-visualize.
Se o usuário fornecer uma imagem, analise seu estilo visual (cores, clima, composição, iluminação) e traduza essas qualidades para a cena.
Validar e renderizar localmente
npm i @vosjs/core # compilador + lints
node -e "import('@vosjs/core/compiler').then(async m => { \
const cfg = JSON.parse(require('fs').readFileSync('my-config.json','utf8')); \
m.compileVosConfig(cfg); console.error('compila limpo') })"
compileVosConfig(config) não deve lançar exceção — é o mesmo compilador que
todo player e servidor de renderização executa. Para verificações visuais, a CLI
(npm i -D @vosjs/cli):
vos render my-config.json out.webm # renderização de vídeo determinística
vos still my-config.json out.webp --time 2.5
vos preview my-config.json # página de reprodução local
vos info my-config.json
Compartilhar para pré-visualização (sem necessidade de conta)
A renderização local é o padrão — nada sai da máquina a menos que o usuário peça compartilhamento ou hospedagem. Quando ele quiser ver hospedado (link reproduzível, ajustes finos no estúdio vos.so) e nenhuma chave de API estiver configurada, use o push reivindicável:
vos push my-config.json --claimable --title "…"
# → claim: https://vos.so/claim/… expira: <72h a partir de agora>
# Sem CLI, ou uma versão antiga? A mesma coisa via HTTP puro:
curl -s -X POST https://vos.so/api/claim \
-H 'content-type: application/json' \
-d '{"title": "…", "config": <o VosConfigJson>}'
# → { "claimUrl": "https://vos.so/claim/…", "expiresAt": "…" }
Entregue claimUrl ao usuário e a mais ninguém — é a única referência e
a única credencial. Dura 72 horas; trabalhos não reivindicados são excluídos
após esse período (re-push se expirar). Reivindicar move o vos para a
biblioteca do usuário. Com uma chave configurada (VOS_API_KEY ou vos login), prefira
vos push — pushes autenticados não expiram. Programas apenas, config ≤200KB,
5 pushes por dia por rede.
Estrutura VosConfigJson
{
"version": 2,
"duration": 8,
"scene": { "background": "#0a0a1a", "fog": { "type": "exp2", "color": 0, "density": 0.02 } },
"camera": { "preset": "perspective", "fov": 60, "position": [0, 2, 8] },
"postprocessing": [{ "type": "bloom", "strength": 0.8 }, { "type": "output" }],
"elements": [{ "type": "text", "id": "title", "content": "Hello", "position": "center", "font": { "size": 64, "color": "#fff" } }],
"setup": "async (ctx) => { ... return data }",
"createContent": "(ctx, setupData?) => { ... return { objects, refs, dispose } }",
"createTimeline": "(ctx, content, duration) => { ... return tl }"
}
Referência completa de campos: references/schema-reference.md. Exemplos
funcionais de cada tipo principal: references/examples.md.
Regras críticas
FAÇA
- Funções devem ser strings JavaScript válidas — NADA de TypeScript (sem
as, sem anotações de tipo) - Acesse Three.js via
ctx.THREE, dialeto GSAP viactx.gsap, cena viactx.scene createContentDEVE retornar{ objects: [...], refs: {...}, dispose: () => {...} }createTimelineDEVE retornarctx.gsap.timeline({ paused: true })- Sempre inclua
dispose()que limpe geometrias, materiais, texturas - Use
ctx.resolution.drawingBufferWidth/drawingBufferHeightpara shaderiResolution - Defina
mesh.frustumCulled = falsepara quads de shader em tela cheia - Defina
depthWrite: false, depthTest: falseem ShaderMaterial de tela cheia - Sempre inclua
{ "type": "output" }como a última entrada ao usar pós-processamento - Adicione todos os objetos da cena tanto à
scenequanto ao arrayobjectsretornado - Interpole uniformes de shader diretamente:
tl.to(uniforms.iTime, { value: duration, duration, ease: 'none' }) - Para lógica por quadro (atualizações de InstancedMesh, matemática de câmera), use callbacks
onUpdate
NÃO FAÇA
- NADA de template literals
${}em strings de função — eles são avaliados em tempo de compilação. Use concatenação de strings. - NADA de crases escapadas — escreva shaders como strings inline, ou use concatenação em linha única
- NUNCA
repeat: -1em tweens individuais — o loop é tratado pelo player; isso quebra o rastreamento de duração - NADA de objetos CSS-style
styleoutextContentem elementos — elementos são planos WebGL - NADA de imports externos — tudo está em
ctx(THREE, gsap, loaders, utils) - NADA de
requestAnimationFrameousetTimeout— use callbacksonUpdate - NADA de
Date.now()/Math.random()em caminhos de quadro — determinismo é o contrato; derive tudo do tempo da timeline ectx.data - NADA de
onFramepara novas configurações — coloque toda lógica por quadro emonUpdate
Parâmetros e Looks (torne remixável)
Declare 2–4 knobs via config.params — editores os renderizam como controles
ao vivo, e outros agentes podem reajustar o programa sem tocar no código. Cada
parâmetro documenta uma chave ctx.data que o programa lê:
"params": [{ "key": "hue", "label": "Matiz", "kind": "number",
"min": 0, "max": 1, "default": 0.6,
"hint": "Desloca todas as cores ao redor do círculo cromático",
"unit": "°", "group": "Cor", "order": 1 }]
- kinds:
number(min/max/step) |color|select(options) |toggle hint: UMA frase sobre o que muda visivelmente — escreva- o programa o lê onde anima:
const d = ctx.data || {}; if (typeof d.hue === 'number') u.uHue.value = d.hue - Nomeie knobs pela INTENÇÃO (
mood,drama,pace), nunca pela implementação (blurRadius). Cada knob deve alterar visivelmente a saída — um knob morto é pior que nenhum knob.
Forneça Looks via config.presets — conjuntos nomeados de parâmetros (2–3 quando o
programa tiver 4+ parâmetros):
"presets": [{ "name": "Vívido", "values": { "hue": 0.9, "speed": 2 } },
{ "name": "Calmo", "values": { "hue": 0.3, "speed": 0.6 } }]
Valores devem referenciar chaves de parâmetros declarados com tipos correspondentes.
Cuidado com renderização no servidor
Configurações enviadas para vos.so são pré-renderizadas em uma frota de software-GL: nada de
THREE.DoubleSide em materiais de transmissão (travamento), nada de dispersion
(muito lento), e qualquer asset buscado deve estar acessível e com CORS aberto. Estes são
válidos para renderizações puramente locais em uma GPU real.
Princípios de design
- Impacto visual: bloom para brilho (mesmo 0.3–0.5 eleva); materiais emissivos + bloom = emissores de luz; fog exp2 (0.01–0.05) para profundidade; fundos escuros fazem as cores se destacarem; vidro (transmission + ambiente HDR) transmite sofisticação; movimento sutil de câmera adiciona vida.
- Qualidade da animação: loops de 4–10s, narrativas de 10–30s;
ease: 'none'para movimento constante;power2.inOut/sine.inOutpara transições; stagger para revelações sequenciais; camadas de tweens em tempos de início diferentes. - Cor: no máximo 2–3 cores complementares; chave quente + borda fria; tons escuros
(
0x0a0a1a,0x1a1a2e) para fundos. - Desempenho:
InstancedMeshpara 10+ objetos idênticos; 5k–20k Points, 100–500 InstancedMesh; limpe tudo.
Guia de seleção de categorias
| Conceito | Câmera | Técnica |
|---|---|---|
| Arte shader / fractais | fullscreen | ShaderMaterial + tweens de uniformes |
| Objetos 3D | perspective | Mesh + Material + luzes |
| Isométrico/chato | orthographic | InstancedMesh + posições computadas |
| Vitrine de texto | perspective ou fullscreen | TextGeometry (3D) ou elementos (2D) |
| Vidro/refração | perspective | MeshPhysicalMaterial + mapa de ambiente HDR |
| Partículas | perspective | Points + shader ou InstancedMesh |
| Mídia mista | perspective | Cena 3D + array de elementos |