Vos 프로그램 작성
VosConfigJson 파일을 생성합니다 — 선언적 애니메이션 구성으로, vos 엔진(github.com/vosjs/vos, MIT)에서 Three.js + GSAP-방언 애니메이션으로 컴파일됩니다. 미리보기가 곧 렌더링입니다: 구성이 모든 머신에서 동일한 프레임을 생성합니다.
워크플로우
- 프롬프트 분석: 애니메이션 개념, 분위기, 색상 및 참조 이미지를 식별합니다.
- 접근법 설계: 카메라 프리셋, 재질, 효과 및 애니메이션 전략을 선택합니다 (아래 카테고리 가이드 참조).
- 구성 생성: 완전하고 유효한 VosConfigJson을
<kebab-case-name>.json파일로 프로젝트의 구성 위치에 작성합니다. - 로컬 검증 (아래 참조) 후 렌더링 또는 미리봅니다.
사용자가 이미지를 제공하면, 시각적 스타일(색상, 분위기, 구성, 조명)을 분석하여 해당 특성을 씬으로 변환합니다.
로컬 검증 및 렌더링
npm i @vosjs/core # 컴파일러 + 린트
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('compiles clean') })"
compileVosConfig(config)는 예외를 발생시키지 않아야 합니다 — 모든 플레이어와 렌더 서버가 실행하는 동일한 컴파일러입니다. 시각적 확인을 위한 CLI (npm i -D @vosjs/cli):
vos render my-config.json out.webm # 결정적 비디오 렌더링
vos still my-config.json out.webp --time 2.5
vos preview my-config.json # 로컬 재생 페이지
vos info my-config.json
미리보기 공유 (계정 불필요)
로컬 렌더링이 기본값입니다 — 사용자가 공유 또는 호스팅을 요청하지 않는 한 머신 밖으로 나가는 데이터는 없습니다. 사용자가 호스팅된 링크(vos.so 스튜디오에서 미세 조정 가능)를 원하고 API 키가 설정되지 않은 경우, 청구 가능 푸시를 사용하세요:
vos push my-config.json --claimable --title "…"
# → claim: https://vos.so/claim/… 만료: <지금부터 72시간>
# CLI가 없거나 구버전인 경우? 평범한 HTTP로 동일한 작업:
curl -s -X POST https://vos.so/api/claim \
-H 'content-type: application/json' \
-d '{"title": "…", "config": <VosConfigJson>}'
# → { "claimUrl": "https://vos.so/claim/…", "expiresAt": "…" }
claimUrl을 사용자에게만 전달하고 다른 곳에는 전달하지 마세요 — 이것이 유일한 참조이자 유일한 자격 증명입니다. 72시간 동안 유효하며, 청구되지 않은 작업은 이후 삭제됩니다 (만료 시 다시 푸시). 청구하면 vos가 사용자의 라이브러리로 이동합니다. 키가 설정된 경우 (VOS_API_KEY 또는 vos login), vos push를 선호하세요 — 키가 있는 푸시는 만료되지 않습니다. 프로그램만 가능, 구성 ≤200KB, 네트워크당 하루 5회 푸시.
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 }"
}
전체 필드 참조: references/schema-reference.md. 모든 주요 유형의 실제 예제: references/examples.md.
중요 규칙
DO
- 함수는 유효한 JavaScript 문자열이어야 함 — TypeScript 사용 금지 (
as, 타입 애너테이션 금지) - Three.js는
ctx.THREE, GSAP-방언은ctx.gsap, 씬은ctx.scene으로 접근 createContent는 반드시{ objects: [...], refs: {...}, dispose: () => {...} }를 반환createTimeline은 반드시ctx.gsap.timeline({ paused: true })를 반환- 항상 지오메트리, 재질, 텍스처를 정리하는
dispose()포함 - 셰이더
iResolution에ctx.resolution.drawingBufferWidth/drawingBufferHeight사용 - 전체화면 셰이더 쿼드에
mesh.frustumCulled = false설정 - 전체화면 ShaderMaterial에
depthWrite: false, depthTest: false설정 - 후처리 사용 시 항상 마지막 항목으로
{ "type": "output" }포함 - 모든 씬 오브젝트를
scene과 반환된objects배열 모두에 추가 - 셰이더 유니폼 직접 트윈:
tl.to(uniforms.iTime, { value: duration, duration, ease: 'none' }) - 프레임별 로직(InstancedMesh 업데이트, 카메라 수학)은 트윈의
onUpdate콜백 사용
DON'T
- 함수 문자열에서
${}템플릿 리터럴 사용 금지 — 컴파일 시 평가됨. 문자열 연결 사용. - 이스케이프된 백틱 금지 — 셰이더는 인라인 문자열 또는 한 줄 연결 사용
- 개별 트윈에
repeat: -1절대 사용 금지 — 반복은 플레이어가 처리하며, 지속 시간 추적이 깨짐 - CSS 스타일
style객체나textContent를 요소에 사용 금지 — 요소는 WebGL 평면 - 외부 임포트 금지 — 모든 것은
ctx에 있음 (THREE, gsap, 로더, 유틸) requestAnimationFrame또는setTimeout사용 금지 —onUpdate콜백 사용- 프레임 경로에서
Date.now()/Math.random()사용 금지 — 결정성이 계약 조건; 모든 것을 타임라인 시간과ctx.data에서 파생 - 새 구성에
onFrame사용 금지 — 모든 프레임별 로직은onUpdate에 배치
파라미터와 룩 (리믹스 가능하게 만들기)
config.params를 통해 2-4개의 노브를 선언하세요 — 편집기에서 실시간 컨트롤로 렌더링하며, 다른 에이전트가 코드를 건드리지 않고 프로그램을 재조정할 수 있습니다. 각 파라미터는 프로그램이 읽는 ctx.data 키를 문서화합니다:
"params": [{ "key": "hue", "label": "Hue", "kind": "number",
"min": 0, "max": 1, "default": 0.6,
"hint": "모든 색상을 휠 주위로 이동합니다",
"unit": "°", "group": "Color", "order": 1 }]
- 종류:
number(min/max/step) |color|select(options) |toggle hint: 시각적으로 무엇이 변하는지에 대한 한 문장 — 작성하세요- 프로그램이 읽는 곳에서 애니메이션 적용:
const d = ctx.data || {}; if (typeof d.hue === 'number') u.uHue.value = d.hue - 노브 이름은 의도 (
mood,drama,pace)로 지정하고, 구현(blurRadius)으로는 절대 지정하지 마세요. 모든 노브는 시각적으로 출력을 변경해야 합니다 — 죽은 노브는 노브가 없는 것보다 나쁩니다.
룩은 config.presets를 통해 제공하세요 — 명명된 파라미터-값 세트 (파라미터가 4개 이상인 프로그램의 경우 2-3개):
"presets": [{ "name": "Vivid", "values": { "hue": 0.9, "speed": 2 } },
{ "name": "Calm", "values": { "hue": 0.3, "speed": 0.6 } }]
값은 선언된 파라미터 키와 일치하는 유형을 참조해야 합니다.
서버 렌더링 주의사항
vos.so에 푸시된 구성은 소프트웨어-GL 팜에서 미리보기 렌더링됩니다: 전송 재질에 THREE.DoubleSide 사용 금지 (하드 행), dispersion 사용 금지 (너무 느림), 가져온 에셋은 접근 가능하고 CORS가 열려 있어야 합니다. 이러한 제한은 실제 GPU의 순수 로컬 렌더링에는 해당되지 않습니다.
디자인 원칙
- 시각적 임팩트: 글로우를 위한 블룸 (0.3–0.5만으로도 향상됨); 방사 재질 + 블룸 = 광원; exp2 안개 (0.01–0.05)로 깊이감; 어두운 배경이 색상을 돋보이게 함; 유리 (전송 + HDR 환경)는 프리미엄 느낌; 미묘한 카메라 드리프트로 생동감 추가.
- 애니메이션 품질: 4-10초 루프, 10-30초 내러티브; 일정한 움직임에
ease: 'none'; 전환에power2.inOut/sine.inOut; 순차적 표시에 스태거; 서로 다른 시작 시간에 레이어 트윈. - 색상: 최대 2-3개의 보색 사용; 따뜻한 키 + 차가운 림; 어두운 톤 (
0x0a0a1a,0x1a1a2e)을 배경에 사용. - 성능: 10개 이상의 동일한 오브젝트에
InstancedMesh; 5k–20k Points, 100–500 InstancedMesh; 모든 것을 폐기.
카테고리 선택 가이드
| 개념 | 카메라 | 기술 |
|---|---|---|
| 셰이더 아트 / 프랙탈 | fullscreen | ShaderMaterial + 유니폼 트윈 |
| 3D 오브젝트 | perspective | Mesh + 재질 + 조명 |
| 아이소메트릭/평면 | orthographic | InstancedMesh + 계산된 위치 |
| 텍스트 쇼케이스 | perspective 또는 fullscreen | TextGeometry (3D) 또는 elements (2D) |
| 유리/굴절 | perspective | MeshPhysicalMaterial + HDR 환경 맵 |
| 입자 | perspective | Points + 셰이더 또는 InstancedMesh |
| 혼합 미디어 | perspective | 3D 씬 + elements 배열 |