Community코딩 & 개발github.com

gfx-html

사용자가 화면 그래픽, 타이틀, 오버레이 등을 요청할 때 사용합니다. HTML 템플릿, Chrome 헤드리스 프레임 렌더링, 알파 채널을 보존하는 무비 인코딩, 그리고 project.json의 오버레이 블록을 다룹니다.

gfx-html란 무엇인가요?

이 스킬은 HTML 기반의 방송/소셜 미디어 그래픽을 제작하기 위한 전문 기술입니다. 사용자가 "하단 자막 만들어 줘", "타이틀 카드 추가해", "이 숫자를 애니메이션 해줘" 등과 같은 명령을 내리면, 이 스킬이 HTML 템플릿 작성부터 Chrome 헤드리스 렌더링, 알파 채널이 포함된 영상 인코딩, 그리고 프로젝트 JSON에 오버레이를 연결하는 전 과정을 자동화합니다. 핵심 원칙은 모든 그래픽이 순수 함수여야 하며, CSS 애니메이션이나 requestAnimationFrame 대신 '?t=<초>' 파라미터에 의존해 결정론적이고 캐시 가능한 프레임을 생성한다는 점입니다. 1920x1080 해상도에 투명 배경을 지원하며, 현재는 Windows 환경의 Chrome 헤드리스와 QuickTime RLE 코덱을 사용합니다. 프레임당 약 430-470ms가 소요되므로, 긴 그래픽의 경우 사용자에게 예상 시간을 미리 알려야 합니다. 캐싱 시스템이 있어 수정 없이 재렌더링할 경우 추가 비용이 들지 않습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/businessbaum/vedit/tree/HEAD/.agents/skills/gfx-html

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

gfx-html은(는) 무엇을 하나요?

그래픽은 Chrome 헤드리스가 한 프레임씩 렌더링하는 HTML 페이지이며, 알파 무비로 인코딩됩니다. 전체 요청 텍스트는 $ARGUMENTS에 있으며, 첫 번째 토큰은 보통 프로젝트 슬러그(slug)입니다. 동사 이름은 빌드 계약을 따릅니다. 충돌 시 python engine/ve.py --help가 우선합니다.

이 작업 방식을 가능하게 하는 규칙

모든 그래픽은 ?t=<초>의 순수 함수입니다. CSS 애니메이션, GSAP 타임라인, requestAnimationFrame, 트랜지션은 사용하지 않습니다. 페이지는 location.search에서 t를 읽고, 레이아웃을 계산한 후 하나의 정적 프레임을 렌더링합니다.

이는 스타일 선호도가 아닙니다. chrome --headless --screenshot은 "페이지가 로드된 것으로 보일 때" 실행됩니다. 이 빌드에는 가상 클록, Emulation.setVirtualTimePolicy, beginFrame이 없습니다. 그런 방식으로 샘플링된 CSS 애니메이션 페이지는 실행할 때마다 다른 픽셀을 반환합니다. 이는 콘텐츠 주소 기반 캐싱을 망가뜨리고(같은 키지만 다른 픽셀), 눈에 띄는 떨림을 유발합니다.

동일한 단순 루프로 샘플링된 t의 순수 함수는 결정론적이며 캐시 가능합니다. 이것이 전부입니다.

미리 알려야 할 예산

Chrome은 정상 상태에서 프레임당 약 430-470ms로 렌더링하며, 첫 시작 시 7.9초가 소요됩니다. 30fps에서 5초 분량의 그래픽은 150프레임으로, 단일 스레드에서 약 65초가 걸립니다. 엔진은 4개의 Chrome 프로세스를 병렬화하고 프레임별 PNG를 캐시하므로, 변경되지 않은 그래픽은 무료로 재렌더링됩니다. 하지만 첫 렌더링은 즉각적이지 않습니다.

긴 그래픽을 시작하기 전에 프레임 수와 예상 시간을 사용자에게 알리세요. 짧은 그래픽을 선호하세요: 6초 동안 유지되는 하단 자막은 6초 전체가 움직일 필요 없이, 0.4초 인, 정적 유지, 0.4초 아웃이면 충분합니다.

1단계 - 템플릿 선택 또는 작성

템플릿은 presets/graphics/*.html에 있습니다. 새로 작성하기 전에 기존 템플릿을 재사용하세요. 템플릿은 다음을 따르는 단일 자체 포함 HTML 파일입니다:

  • location.search에서 t(초)와 해당 속성(props)을 읽습니다.
  • 일반 산술 연산으로 t를 기반으로 모든 위치/불투명도를 계산합니다. 이지(ease) 함수도 괜찮습니다. 순수 함수이기 때문입니다.
  • 투명한 바디(body) 위에 렌더링합니다. 배경색을 절대 칠하지 마세요.
  • assets/fonts/의 폰트를 file:/// URL과 함께 @font-face를 사용하여 불러옵니다. 시스템 폰트에 의존하지 마세요. 이 상자에서 Chrome이 해석하는 폰트는 QC 컨트롤 렌더가 가정하는 것과 다릅니다.
  • 1920x1080에서 읽을 수 있어야 하며 플랫폼의 캡션 안전 영역 안에 있어야 합니다. output.platform을 확인하세요: 하단 1/3에 위치하는 하단 자막은 TikTok UI 아래에 있습니다.

레이아웃 세부 사항, 이징(easing) 규칙 및 속성(props) 계약은 docs/graphics.md에 있습니다. 여기서 다시 설명하지 않습니다.

2단계 - 프레임 렌더링

python engine/ve.py gfx <slug>

프레임별로 엔진은 Chrome을 argv 목록으로 호출합니다. 대략 다음과 같습니다:

chrome.exe --headless=new --disable-gpu --no-sandbox --hide-scrollbars
           --user-data-dir=<win-path>\prof --default-background-color=00000000
           --window-size=1920,1080 --screenshot=<win-path>\frame_0007.png
           "file:///C:/.../template.html?t=0.233&title=..."

두 가지 플래그가 전체 작업 방식을 결정합니다:

  • --default-background-color=00000000은 진정한 알파를 생성합니다. 확인됨: 모서리 픽셀이 0 0 0 0으로 읽힙니다. 이 플래그가 없으면 불투명한 검정이 되어 오버레이가 비디오를 덮습니다.
  • file:/// URL은 실제 Windows 경로여야 합니다. Git Bash의 $PWD 경로는 조용히 실패합니다. 세 개의 다른 HTML 파일이 빈 다크모드 오류 페이지의 동일한 md5를 생성하며, exit 0, 그럴듯한 파일 크기를 냅니다. 이것이 QC 검사(g)가 알려진 빈 해시와 비교하는 이유입니다.

프레임은 .cache/html/ 아래에 sha256(html_content + t + params + renderer + renderer_version)를 키로 하여 캐시됩니다. 템플릿을 변경하면 모든 프레임이 무효화되며, 이는 올바른 동작입니다.

3단계 - 알파를 유지하며 인코딩

동일한 명령어입니다. ve gfx는 프레임을 렌더링하고 한 번에 인코딩합니다. 별도의 인코딩 동사는 없습니다. PNG 시퀀스 옆에 무비가 없으면 하류에서 사용할 수 없는 반쯤 완성된 아티팩트이기 때문입니다.

PNG 시퀀스는 **QuickTime RLE (qtrle, argb)**로 인코딩합니다. ProRes 4444 및 원시 PNG 시퀀스도 알파를 올바르게 전달합니다. 이 빌드에서 VP9-in-WebM은 -metadata alpha_mode=1을 사용하더라도 알파를 조용히 버립니다. 사용하지 말고, 사용자의 에셋에서도 프로빙 없이 받아들이지 마세요.

출력은 <key>.json 사이드카와 함께 .cache/ovl/<xx>/<key>.mov로 저장됩니다.

4단계 - 오버레이 고정

overlays[] 항목을 작성합니다: id, kind: "html", template, props, anchor, duration_frames, layer, render.

anchor는 항상 {segment_id, offset_frames}입니다. 절대 타임라인 위치를 사용하지 마세요. 타임라인 위치는 활성화된 세그먼트의 누적 합계에서 파생됩니다. 누군가 상류에서 세그먼트를 비활성화하는 순간 모든 절대 위치가 틀어지고 모든 그래픽이 표류합니다. 고정된 오버레이는 자동으로 해당 세그먼트와 함께 이동하며, 이는 편집자가 기대하는 동작입니다.

layer는 합성 순서를 결정합니다. 숫자가 높을수록 위에 표시됩니다. 캡션은 마지막에 렌더링되며 그래픽 위에 위치합니다.

5단계 - 완료 전 확인

세 가지 검사가 있으며, 모두 비용이 저렴합니다:

  1. 알파 프로브. 모서리 프레임에서 원시 rgba 픽셀 하나를 읽고 a = 0인지 확인합니다. QC (d).
  2. 빈 페이지 확인. 프레임이 하나의 단색이 아님을 확인합니다. QC (g). 이는 잘못된 file:/// URL, 로드 실패한 폰트, JS 오류를 잡아냅니다. 이 모든 경우 exit 0으로 정상 종료되고 그럴듯한 파일을 생성합니다. 첫 번째, 중간, 3/4 지점 프레임을 샘플링하여 모두 균일할 때만 실패합니다. 모든 템플릿은 불투명도 0에서 시작하므로, 작동하는 그래픽의 프레임 0은 정당하게 비어 있으며, 이것만으로 판단하면 올바른 렌더를 실패로 처리합니다.

ve gfx는 생성된 결과에 대해 두 검사를 자동으로 실행하며, 실패 시 exit 2로 종료합니다. 3. 결정론. 하나의 프레임을 두 번 렌더링하고 해시를 비교합니다. 다르면 템플릿의 무언가가 t의 순수 함수가 아닙니다. 찾으세요. 이 검사가 존재하는 이유는, 그것이 잡아내는 실패가 캐시된 렌더가 새 렌더와 일치하지 않을 때까지 보이지 않기 때문입니다.

그런 다음 중간 지점의 스틸 이미지를 사용자에게 보여주세요. 스크린샷이 한 명령어 거리에 있을 때 그래픽을 글로 설명하지 마세요.

실패 모드

증상원인수정 방법
오버레이가 비디오를 검정으로 덮음--default-background-color 누락, 또는 템플릿이 배경을 그림플래그 추가; 바디를 투명하게 만듦
모든 프레임이 동일하고, 파일 크기가 그럴듯함잘못된 file:/// URL로 인한 빈 오류 페이지실제 Windows 경로 사용; 빈 해시와 비교 확인
실행 간에 같은 키지만 다른 픽셀템플릿이 t의 순수 함수가 아님모든 CSS 애니메이션과 rAF 제거
텍스트가 잘못된 서체로 렌더링됨시스템 폰트 대체assets/fonts/에서 file:/// URL로 @font-face 사용
인코딩 후 알파 소실VP9/WebMqtrle argb 또는 ProRes 4444 사용
상류 편집 후 그래픽이 표류절대 타임라인 앵커{segment_id, offset_frames}로 앵커 지정
렌더링에 수 분 소요430 ms/프레임이 최소치그래픽을 짧게 하거나 수용; 캐시로 두 번째 실행은 무료

Individual skills in this repo

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

businessbaum/vedit

This skill should be used when the user asks for subtitles or on-screen text of what is being said - "brenn die Untertitel ein", "mach Untertitel", "Untertitel drauf", "leg Untertitel drüber", "brenn die Subs ein", "mach die Captions", "Wort für Wort Untertitel", "Karaoke-Untertitel", "korrigier den Untertitel-Text", "burn in the captions", "add subtitles", "burn the subs", "word-by-word captions", "fix a caption typo". Covers ASS generation, the pixel-width grouping rule for German, the font table, and burn-in through libass.

businessbaum/vedit

This skill should be used when the user asks for mechanical tightening of material that is already selected - "mach den Rohschnitt", "bau den Rohschnitt", "schneid die Ähms raus", "nimm die Füllwörter raus", "entferne die Pausen", "kürz die Pausen", "schneid die Versprecher raus", "mach den Radioschnitt", "straff das", "build the rough cut", "cut the fillers", "remove the ums", "tighten the pauses", "trim the dead air", "cut the retakes", "radio edit". Turns kept story blocks into enabled segments in project.json. Does not decide what the video is about - that is cut-story.

businessbaum/vedit

This skill should be used when the user asks to decide what the video is actually about before any mechanical cutting - "bau die Story", "was soll ins Video", "was kann raus", "welche Themen bleiben", "kürz das auf 6 Minuten", "mach daraus ein kurzes Video", "wo ist der Hook", "sortier die Themen", "worum geht es hier eigentlich", "build the story", "decide what stays", "structure this", "cut this down to N minutes", "find the hook", "reorder the topics". This is the CONTENT-selection pass that writes the "story" block in project.json; it runs after transcribe and before cut-rough.

businessbaum/vedit

This skill should be used when the user asks for the finished video file - "exportier das", "render das fertig", "mach das final", "renderte das Video", "mach den Endexport", "bau das Video zusammen", "mach es fertig für YouTube", "gib mir die MP4", "export the final", "render the final cut", "make the master", "export for TikTok", "give me the mp4". Covers segment rendering with the cache, guarded concat, the one-pass audio, loudness normalisation and the single mux.

관련 스킬