Communitygithub.com

pedrojaques99/site-reels

웹사이트나 웹 앱의 최고 장면(WebGL 히어로, 3D, 스크롤)을 1080p MP4로 녹화하여 피그마 슬라이드에 업로드하고, Remotion으로 브랜드 또는 제품 쇼릴을 제작합니다. "사이트 장면 녹화", "덱용 사이트 비디오", "쇼릴", "런칭 비디오", "site-reels" 명령어에 사용합니다.

site-reels란 무엇인가요?

이 스킬은 로컬에서 실행 중인 웹사이트나 웹 앱의 스크립트된 장면을 1080p 30fps MP4 파일로 녹화하고, 이를 피그마 슬라이드에 업로드하며, Remotion으로 브랜드 또는 제품 쇼릴을 제작하는 전문 도구입니다. Puppeteer-core와 puppeteer-screen-recorder(CDP screencast + ffmpeg)를 기반으로 화면 녹화 대신 직접 프레임을 캡처하여 안정적인 WebGL 및 3D 성능을 보장합니다. macOS, Windows, Linux 환경에서 Node.js 18+, ffmpeg, Chrome/Chromium/Edge를 필요로 하며, GPU와 가시적인 데스크톱이 필요합니다. 주요 기능으로는 `record.mjs`를 통한 일반 녹화, `snap.mjs`를 통한 60fps 결정적( deterministic) 프레임 캡처(가상 클록 사용), 피그마 슬라이드에 숨겨진 슬라이드로 업로드, Remotion 기반 브랜드/제품 쇼릴 제작 등이 포함됩니다. 또한 장면별 CSS로 UI 요소를 숨기고, 인터랙션(스크롤, 클릭, 파일 업로드)을 자동화하며, 품질 감사 접촉 시트를 생성합니다. 알려진 문제점으로는 숨겨진 창의 rAF 동결, CSS 재적용 필요성, 지연 로딩되는 3D 콘텐츠 대기 시간, 머신 부하에 따른 screencast 끊김 등이 있으며, 이에 대한 해결책이나 대체 방법을 제공합니다.

지원 대상~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/pedrojaques99/visant-skills-public/tree/HEAD/site-reels

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

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

문서

Site Reels: site → MP4 → deck

요구 사항

필요한 항목내용확인 방법
모든 기능GPU와 가시적인 데스크톱이 있는 Windows, macOS 또는 Linux (Chrome이 headful 모드로 실행됨)n/a
모든 기능Node.js 18+node -v
모든 기능PATH에 ffmpeg + ffprobe (또는 FFMPEG=<전체 경로>)ffmpeg -version
모든 기능Chrome, Chromium 또는 Edge (자동 감지, 아래 참조)node -e "import('./scripts/find-chrome.mjs').then(m=>console.log(m.findChrome()))"
record.mjspuppeteer-core + puppeteer-screen-recorder (npm, 프로젝트 내)npm ls puppeteer-core puppeteer-screen-recorder
snap.mjspuppeteer-core + timesnap (timeweb 포함)npm ls timesnap
쇼릴Remotion 프로젝트npx remotion --version
감사 (선택 사항)ImageMagickmagick -version
Figma 업로드Figma MCP (use_figma, upload_assets) + 스킬 figma-use, figma-use-slidesMCP 도구 목록

Chrome 감지 (scripts/find-chrome.mjs, record.mjs 및 snap.mjs에서 사용): CHROME 환경 변수가 설정된 경우 해당 경로 사용; 그렇지 않으면 설치된 Chrome/Edge (Windows: Program Files 및 LocalAppData; macOS: /Applications 및 ~/Applications; Linux: PATH의 google-chrome, chromium, microsoft-edge); 그 외에는 가장 최신의 ms-playwright/chromium-<N> (또는 PLAYWRIGHT_BROWSERS_PATH); 그래도 없으면 중단하고 CHROME 설정 방법 또는 npx playwright install chromium 실행 방법을 안내합니다. Windows에서는 --use-angle=d3d11을 추가하고, 다른 OS에서는 Chrome이 기본 GPU 백엔드를 유지합니다.

기능 설명

로컬에서 실행 중인 사이트의 스크립트된 장면을 녹화하여 Figma에 바로 사용할 수 있는 MP4 (1920×1080, 30fps, x264, < 100MB)를 생성합니다. 검증된 엔진: puppeteer-core + puppeteer-screen-recorder (CDP screencast + ffmpeg). 화면 녹화를 사용하지 않습니다.

실행

mkdir <proj>-reels && cd <proj>-reels && npm init -y && npm i puppeteer-core puppeteer-screen-recorder
mkdir -p scripts && cp <skill-dir>/scripts/{record,find-chrome}.mjs scripts/
cp <skill-dir>/scripts/scenes.example.mjs scenes.mjs   # 장면 편집
# 사이트 저장소에서: npx next build && npx next start -p 3100   (프로덕션 빌드: WebGL이 더 부드러움)
OUT="<출력 폴더>/Videos" node scripts/record.mjs scenes.mjs [scene ...]

프로젝트로 복사된 스크립트를 실행합니다: ESM이 puppeteer-core를 cwd가 아닌 스크립트 폴더에서 확인합니다.

환경 변수: BASE (기본값 http://localhost:3100), OUT, CHROME, FFMPEG, W, H, DPR, MOBILE=1 (터치 에뮬레이션), KEEP_UI=1 (내비게이션 유지), MIN_FPS (24). 휴대폰 4:5: W=360 H=450 DPR=2 MOBILE=1은 720x900을 생성합니다.

각 장면 = { name, path, theme?, trim?, crf?, css?, prepare(page, ctx), act(page, ctx) }. prepare는 녹화 전에 실행 (섹션으로 스크롤, 3D 로딩 대기); act는 비디오에 기록됩니다. ctx에는 wait, scrollTo(page, y|selector, ms) 및 scrollThrough(page, selector|null, ms, from, to) (easing 포함)가 있습니다. 로드 시 실행되는 인트로: act 내에서 page.reload()를 사용하고 trim으로 로딩을 잘라냅니다.

작동하는 브랜드 스크립트: 로고/히어로 인트로 (라이트 및 다크), 히어로 미디어 교체, 스크롤 시 카메라 진입이 있는 3D 섹션, 몰입형 경로 (산책), 푸터 워드마크 및 전체 페이지 한 번 통과. 57개 장면, 각 1255초.

전달 전 감사 (종료 코드를 신뢰하지 마십시오)

  • 비디오별 접촉 시트: ffmpeg -i x.mp4 -vf "fps=6/<dur>,scale=480:-1,tile=3x2" -frames:v 1 sheet.png 그리고 확인.
  • 19초에 1MB = 빈 장면. > 100MB = Figma에 들어가지 않음 (장면 crf로 수정; 입자와 트레일이 있는 장면은 24 사용).
  • 여러 시점에서 상단 스트립 (crop=1920:120:0:0)을 확인하여 내비게이션 노출 감지.
  • LOW-FPS에 시작/끝에 긴 멈춤이 있으면 오경보 (계측기가 정지 프레임을 계산함); 접촉 시트 확인.

알려진 문제점

  1. 숨겨진 창이 rAF를 정지 → 프레임 동결. 실행 시 이미 --disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling --disable-features=CalculateNativeWinOcclusion를 전달합니다. headless: false를 의도적으로 사용: headless 모드는 SwiftShader로 폴백되어 WebGL이 끊깁니다.
  2. Hide-UI CSS가 리로드 시 및 React 하이드레이션이 <head>를 지울 때 사라짐 → 스크립트가 100ms마다 <style id="reels-hide">를 body에 다시 첨부합니다.
  3. 잘못된 선택자 하나가 전체 규칙을 무효화함: JS 템플릿 리터럴의 \[는 [가 되고 .z-[60]는 header, nav, .z-[60]를 무효화합니다. 위험한 선택자당 하나의 규칙.
  4. [data-cursor]를 숨기지 마십시오: 사이트가 3D 캔버스와 캐러셀을 이 속성으로 태그합니다. 사용자 정의 커서 요소를 대상으로 지정 (fixed + mix-blend + pointer-events-none).
  5. 지연 로딩 3D (IntersectionObserver + 동적 가져오기): 섹션에 도달한 후 5~7초 대기. preserveDrawingBuffer 없이 WebGL 캔버스를 읽으면 빈 값이 반환됨: 고정 대기 또는 스크린샷 사용.
  6. Screencast는 ~32개의 고유 프레임/초를 전달 → 30fps로 녹화 (60fps는 불규칙한 반복 프레임을 생성). 기본 trim 0.3초는 첫 번째 캐시된 프레임을 제거합니다.
  7. 로드된 머신에서 Screencast 끊김 (Photoshop 실행, 병렬 렌더): MP4가 "30fps"로 표시되지만 9~18개의 고유 프레임/초. record.mjs가 측정 (mpdecimate=hi=64:lo=24:frac=0.05)하고 MIN_FPS(24) 미만이면 최대 2회 재녹화. WebGL/3D 플레이트는 snap.mjs를 선호합니다.
  8. 쿠키 배너: prepare에서 수락 ("Accept/Aceitar/OK"를 일반 클릭) 후 녹화.
  9. 스크롤이 없는 도구 페이지 (생성기, 효과 편집기)는 정지 상태로 녹화됨: 장면이 인터랙션해야 함 (input[type=file]에 파일 업로드, 버튼 텍스트로 프리셋 클릭). 사용자 정의 슬라이더는 합성 input에 반응하지 않음.

60fps 고정 플레이트: snap.mjs (결정적)

scripts/snap.mjs는 timeweb (timesnap의 가상 클록: rAF, performance.now, setTimeout 및 video)을 주입하고, 프레임당 1/60초씩 진행하며 스크린샷을 찍습니다. 머신 부하와 무관합니다. 장면: { name, path, theme, css, dur, wait (3D 로딩을 위한 실제 ms), pre (스크롤 전 가상 ms, 2000; **로드 시 실행되는 인트로의 경우 0**), start (건너뛸 가상 s), scroll: { sel, from, to, at, len } | { bottom: [from, to] in screen heights }, frame(page, t) (예: 마우스) }. 스크롤은 가상 시간의 함수입니다.

npm i puppeteer-core timesnap && mkdir -p scripts
cp <skill-dir>/scripts/{snap,find-chrome,patch-timeweb}.mjs scripts/
npm pkg set scripts.postinstall="node scripts/patch-timeweb.mjs" && npm run postinstall
FPS=60 OUT=... node scripts/snap.mjs scenes-snap.mjs [scene]
  • timesnap() 자체를 사용하지 마십시오: Next + R3F + Lenis와 함께 사용하면 빈 페이지를 기록하고 WebGL 히어로에 동결된 구간을 남깁니다 (rAF가 실행되지 않음). 자체 루프 (goTo + scrollTo + 스크린샷)가 모든 곳에서 작동했습니다: 60/60 고유 프레임.
  • timeweb 패치는 필수: video.play()를 Promise 및 playing 이벤트가 없는 버전으로 교체 → video.play().catch()가 throw되고 페이지가 빈 상태가 됩니다.
  • preserveDrawingBuffer: true가 getContext에 강제 적용 (preserve: false로 해제 가능).
  • 프레임은 OS 임시 디렉터리로 이동되며, 동기화된 드라이브로 절대 이동하지 않습니다 (수천 개의 jpg 동기화 방지).
  • GPU 작업을 병렬로 실행하지 마십시오 (Remotion 렌더, Photoshop): WebGL 컨텍스트를 잃고 몇 초간 배경만 기록합니다. 스크립트는 FLASH (휘도로 프레임 깜박임)를 경고합니다. 접촉 시트에서만 긴 빈 구간이 나타납니다. 확인.
  • 실제 부드러움 감사: 전체 해상도에서 이웃 프레임 간 magick compare -metric MAE. 0 = 반복 프레임.
  • 전달: scripts/deliver.example.sh (플레이트 복사 및 이름 변경; V= 필요)는 reveal, 루프 (loop.sh) 및 깨끗한 3D를 빌드하고, 95MB 미만이 될 때까지 crf를 3씩 증가시킵니다.

Figma 슬라이드에 업로드 (숨김, 팀 방해 없음)

MP4는 MCP를 통해 들어가지 않습니다: use_figma는 네트워크가 없고 Plugin API는 바이트에서 비디오를 생성하지 않으며, upload_assets는 이미지(≤ 10MB)만 허용합니다. 따라서:

  1. 각 비디오의 커버: ffmpeg -ss <t> -i x.mp4 -frames:v 1 -q:v 2 _capas/x.jpg.
  2. use_figma (스킬 figma-use + figma-use-slides): 마지막 슬라이드 전에 createSlideRow(idx), row.name = 'WIP · Motion (hidden)', 비디오당 하나의 createSlide(idx, i) (isSkippedSlide = true, 검은 배경, VIDEO · x.mp4라는 이름의 1920×1080 사각형 및 speakerNotes에 지침).
  3. 사각형의 nodeIds로 upload_assets → 커버의 multipart POST.
  4. 사용자가 MP4를 사각형으로 드래그 (커버를 비디오로 교체). 명확히 말하고, 삽입된 비디오를 약속하지 마십시오.

getSlideGrid()는 건너뛴 슬라이드를 생략합니다: SLIDE_GRID.children[i].children을 통해 구조를 확인하십시오.

브랜드 쇼릴 (Remotion): 플레이트 + 디자인된 장면

브랜딩 쇼릴 (로고, 색상, 타이포그래피, 레이아웃, 매니페스토, 목업)의 경우, 사이트에서 깨끗한 플레이트만 녹화하고 (텍스트 없음: 장면별 CSS로 제목/내비게이션 숨김) 나머지는 Remotion에서 구성합니다 (프레임별 렌더, 지터 없음). src/fx.tsx에 가질 가치가 있는 재사용 가능한 빌딩 블록: Mist, MistWords, Grain, KenBurns, Plate, nevoa() (안개) 전환, 브랜드 데이터는 brand.ts에.

  • 시각적 아이덴티티는 덱에서 가져오고, 사이트에서 가져오지 마십시오: 슬라이드에서 공식 타이포그래피와 팔레트를 확인하십시오 (사이트는 브랜드 북에서 벗어날 수 있음).
  • 작동하는 장면: 안개 속의 오프닝 문구, 녹화된 리빌, 로고 구성 (evolvePath + 로고 래스터에서 측정된 가이드), 문구별 매니페스토, 가변 wght 축이 있는 기둥, 밴드 형태의 팔레트, 타이포그래피, 계층형 그래픽, mix-blend-difference 제목이 있는 3D, 스크롤 레이아웃 열, 1/2/3-up 목업 몽타주, 이름에 동기화된 제품, 워드마크로 마무리.
  • 스틸로 검토 (npx remotion still ... --frame=N + 몽타주) 후 렌더링.
  • --concurrency=3으로 렌더링: 8에서 Photoshop이 열려 있으면 32GB 머신의 메모리가 부족해짐.
  • 루프: scripts/loop.sh in out [start] [dur] [crossfade]. 첫 번째 프레임과 마지막 프레임 간 magick compare -metric RMSE로 이음새 확인 (< 0.5% = 보이지 않음).
  • 나쁜 플레이트는 폐기: 회전하는 회전체는 움직임을 보여주지 않음; 세로 캔버스용으로 프레이밍된 카메라는 16:9에서 작게 보임.
  • 격렬한 릴 (승인된 리듬): 2535초, 60fps, 120 BPM 그리드 (30프레임)에서 하드 컷이 있는 Series, 36프레임마다 이미지 버스트, 그리드 위의 거대한 로고 크롭, 깜박이는 글꼴 두께, 재배열되는 팔레트 블록, 원형 마스크의 초상화, 그래픽 버스트, 운동성 문구, 한 박자 목업, ~2.5초 동안 유지되는 클로징 로고. 레이블 제로: 텍스트는 그래픽으로만 사용. 느린 1:40 쇼릴은 거부됨.
  • 소프트/스모키 릴: 히어로 연기를 로고나 사진 없이 녹화, 순수 흰색 위 검은색 (장면에 storage = localStorage의 params 및 block: ['assets/logo.png']; 빈 PNG는 투명해야 함; 비디오를 차단하지 마십시오: 깨진 비디오는 timeweb을 중단시킴). 플레이트를 mix-blend-mode: multiply 오버레이로 사용: 장면 시작 시 열리고, 반전 버전(ffmpeg -vf reverse)으로 닫힘; 컷은 검은색에 떨어짐. ~1.1초 (비율 2)에서 전환, 또는 장면의 절반이 검은색으로 진행.
  • Figma 슬라이드 레이아웃 조립: 슬라이드 (건너뛴)를 복제하고, 각 카드를 배경/로고/텍스트만 표시되도록 3배 복제하고, 각 노드를 download_assets (스케일 2)로 다운로드하고 복제본을 삭제. 슬라이드는 투명도를 내보내지 않음: 슬라이드 검은색과 흰색으로 로고/텍스트를 내보내고 차이로 알파를 추출 (ImageMagick). use_figma를 통해 base64를 반환하지 마십시오 (컨텍스트가 깨짐).

제품 쇼릴 (실제 앱 촬영)

화면을 실제로 촬영 (Playwright + CDP screencast at 2x, 단계 goto/move/click/type/scroll/wait, 포인터 궤적을 <id>.cursor.json으로 저장)하고, 커서를 Remotion에서 위에 다시 그림.

거부됨, 반복하지 마십시오:

  • 디자인된 릴을 실제 화면에 접착 (일러스트레이션 오프닝 + 촬영된 중간 + 그려진 CTA): 두 가지 언어, 캐릭터가 사라졌다가 돌아오고, 액트 사이의 검은 카드가 리듬을 중단. 비디오당 하나의 언어.
  • 16:9 창의 작고 속도가 빨라진 제품 화면: 피드와 순위가 읽을 수 없는 벽이 됨.
  • 화면을 자르는 줌 및 비디오 위에 텍스트 스탬프 (거대한 동사, 01/07 카운터, 그라데이션): 침입적.
  • 화면에서 빈 대기 ("waiting…"): 바로 액션으로 컷.
  • 모호한 카피: 명백해야 하고 청중의 평범한 구어체로 작성되어야 함.

작동하는 형식: 액션당, 먼저 텍스트만, 중앙 정렬 및 짧게 (2비트), 그런 다음 전체 화면, 크롭되지 않음, 캡처 비율의 중앙 창, 깨끗한 배경. 신중한 텍스트 (굵기 700, 약간의 블러로 단어별 진입), 카운터 없음, 그라데이션 없음. 트랙 그리드에서 컷 (14프레임 = 128.57 BPM). 최대 40초.

효과가 있었던 기술:

  • 캡처 속도 높이기: <OffthreadVideo trimBefore={from} playbackRate={rate}>를 사용하고 클릭 프레임을 / rate로 다시 계산 (사운드 및 커서 링).
  • 중간에서 다른 컴포지션 시작: <Sequence from={X}><Sequence from={-start}><Comp/></Sequence></Sequence>. 해당 구간의 첫 번째 프레임 확인: 공유 요소가 "날아 들어오면" 느슨해짐. 몇 프레임 건너뛰고 그 앞에 떨어진 충격음을 다시 배치.
  • 모듈 수준 타임라인이 있는 컴포지션별 언어: Root 전에 가져온 자체 항목 파일에 의해 설정된 globalThis 플래그.
  • 컷을 가로지르는 연속 트랙: 배드에서 trimBefore를 사용하여 이전 구간이 중단된 지점부터 계속.
  • Loudness: 렌더링, 측정 및 2-pass loudnorm (-14 LUFS / -1 dBTP)으로 정규화, 비디오 복사. 마지막에만 측정: Remotion의 원시 믹스는 0 dBFS를 초과함.
  • 감사: 모든 렌더의 타임스탬프 접촉 시트 (fps=2,drawtext=%{pts\:hms},tile=8x6) 및 모든 구간 접합부의 조밀한 시트 (fps=6).
  • git 없고 다른 세션이 동시에 편집하는 프로젝트: 새 파일에 새 컷, 공유 파일 편집 전 *.bak-* 백업, 사용자에게 알림.

포트폴리오 카드 및 스튜디오 릴

  • record.mjs with KEEP_UI=1 (카드가 사이트를 그대로 표시) 및 위의 휴대폰 프리셋.
  • 인벤토리 파일에서 생성된 장면 (라이브 URL이 있는 공개 항목만); 라이브 URL이 없음 = 스크린샷의 Ken Burns.
  • 카드 패키지: 끝에서 시작으로의 크로스페이드 루프, KB 제한에 대한 2-pass 비트레이트, 함께 제공되는 VP9 WebM, 프레임 1의 WebP 포스터.
  • 웹용 알파가 있는 로고: `-

…

[번역이 잘렸습니다]

관련 스킬