SOIL 애니메이션 프레젠테이션: 한 번 제작, 두 가지 결과물
| 결과물 | 내용 | 대상 |
|---|---|---|
| A 프레젠테이션 (.pptx) | SOIL 6개 엔진으로 제작된 강의 프레젠테이션. 2–4개의 🎬 애니메이션 슬라이드에 애니메이션 클립이 포함됨. 클립은 무음, 자막 없음, 교사가 재생하며 설명 | 수업 프로젝터 |
| B 영상 (MP4) | 동일한 내용의 완전한 해설 영상: 나레이션, 자막, 3색 테마 | 예습, 보충 수업, 업로드 |
두 결과물은 동일한 애니메이션을 공유합니다: 먼저 B를 제작한 후, B의 '무음 쌍둥이 버전'에서 🎬 슬라이드에 해당하는 프레임을 잘라내어 A에 삽입합니다. 따라서 애니메이션은 한 번만 제작하면 되며, 두 결과물의 시간이 완벽하게 일치합니다.
이 스킬은 '연결'만 담당합니다. 제작은 두 상위 스킬의 지침을 따릅니다:
soil-teaching-deck(mathruffian-dot/soil-teaching-deck): 6개 엔진, 슬라이드 역할, visual 템플릿, 샌드위치 구조, F-0 폰트 설치.motion-explainer(mathruffian-dot/claude-motion-explainer), 및 포함된motion-design,review-loop및 HyperFrames 공식 스킬: 스크립트, 나레이션, 프레임 제작, 자막, 3색 테마, 검토, 출력.
0. 환경 및 경로
이 문서는 세 개의 코드명을 사용합니다. 작업을 시작하기 전에 실제 위치를 확인하세요:
| 코드명 | 설명 | 일반적인 위치 |
|---|---|---|
<smd> | 이 스킬 폴더 | ~/.claude/skills/soil-motion-deck (Claude Code), ~/.agents/skills/soil-motion-deck (Codex) |
<me> | motion-explainer 스킬 폴더 | 위와 동일, 폴더명 motion-explainer |
<soil> | SOIL 스킬 폴더 | 위와 동일, 폴더명 soil-teaching-deck |
ls -d ~/.claude/skills/{soil-motion-deck,motion-explainer,soil-teaching-deck,review-loop} ~/.agents/skills/{soil-motion-deck,motion-explainer,soil-teaching-deck,review-loop} 2>/dev/null
PowerShell (Windows에서 bash가 없을 경우):
Get-ChildItem "$HOME\.claude\skills","$HOME\.agents\skills" -Directory -ErrorAction SilentlyContinue | Select-Object FullName
다른 에이전트 (예: Gemini CLI): 해당 에이전트의 skills 폴더에서 동일한 이름의 폴더를 찾으세요.
없는 폴더가 있다면, 해당 레포지토리의 README에 따라 사용자가 설치하도록 안내하세요 (각 레포지토리는 skills/ 또는 skill/ 하위 폴더를 에이전트의 skills 폴더에 복사합니다). SOIL의 install.py는 찾은 첫 번째 폴더에만 설치하며, 사용자가 Enter를 누를 때까지 기다립니다. 에이전트가 직접 설치할 경우, 직접 복사하세요: cp -r soil-teaching-deck/skill <skills 폴더>/soil-teaching-deck.
기타 요구 사항:
- 상위 두 스킬의 요구 사항: Node.js 22+, ffmpeg, Python 3, HyperFrames, 그리고 하나의 TTS: edge-tts (기본값). GPU가 필요 없으며, 대부분의 사용자가 이 옵션을 사용합니다. 또는 Breeze TTS 2 (고급, NVIDIA GPU 필요, 연구 및 비상업적 용도로만 제한, 사용자가 요청할 때만
motion-explainer/references/tts-setup.md에 따라 설치 안내). - 프레젠테이션: PptxGenJS 3.12.0 (이 스킬의
templates/package.json을 사용하여 설치, §5 참조), GenSekiGothic2 H/B/M (SOIL의 F-0에 따라 설치). - 썸네일: Windows에서 PowerPoint이 있으면 가장 좋습니다 (
thumbs.py가 PowerPoint을 직접 사용하여 출력하므로 폰트와 영상 썸네일이 실제 강의와 동일함). 없는 경우 LibreOffice + poppler를 사용하며, 레이아웃만 확인 가능하고 영상 썸네일은 보이지 않을 수 있습니다. - 테스트된 버전: HyperFrames 0.8.168, PptxGenJS 3.12.0.
sub-agent가 없는 에이전트 (예: 일부 Codex, Gemini 설정): motion-explainer에서 '각 프레임에 sub-agent를 할당하여 병렬 처리'하는 부분을, 순서대로 하나씩 처리하세요. 각 프레임이 읽는 파일과 순서는 동일합니다.
1. 시작 인터뷰 (한 번에 모두 질문)
- 과목, 단원, 대상, 수업 시간 (SOIL의 추가 질문).
- 원하는 결과물: A+B (기본값), A만, B만. A만 원할 경우에도 먼저 B를 제작합니다: 나레이션이 각 프레임의 리듬을 결정해야 애니메이션이 자연스럽습니다. 단, B를 제공하지는 않으며, 나레이션을 사용자에게 미리 듣게 할 필요는 없습니다.
- 색상. 기본값은 '영상 3색 + SOIL 샌드위치':
- 영상 (및 프레젠테이션의 애니메이션 창)은 motion-explainer의 3색을 사용하며, 기본값은 검정 배경
#121212, 흰색 텍스트#F2F0EA, 주황색 강조#FF7A1A입니다. - 프레젠테이션의 표지, 행동 슬라이드, 결론은 어두운 색상 슬라이드로, 영상의 3색을 사용합니다. 내용 슬라이드는 밝은 색상 슬라이드이며, 애니메이션은 어두운 '재생 창'에 표시됩니다.
- 영상 (및 프레젠테이션의 애니메이션 창)은 motion-explainer의 3색을 사용하며, 기본값은 검정 배경
- 나레이션 목소리: motion-explainer의 규칙을 따릅니다.
motion-explainer 자체의 시작 인터뷰 (플랫폼, 길이, 목소리, 3색)는 여기서 이미 질문했으므로, 다시 질문하지 마십시오. 영상 길이는 motion-explainer의 '60–90초' 제안에 제한받지 않습니다: 영상 길이는 나레이션 길이와 동일하며, 일반적으로 각 🎬 슬라이드당 15–25초에 앞뒤 몇 프레임이 추가됩니다.
2. SOIL 엔진 1~3, 🎬 애니메이션 슬라이드 표시
soil-teaching-deck에 따라 엔진 1 (개념定位), 엔진 2 (맥락定位), 엔진 3 (슬라이드 구조)를 실행하고, 각 엔진 실행 후 사용자에게 확인을 받습니다. 엔진 3의 슬라이드 역할 표에서 애니메이션을 만들 슬라이드에 animate 필드를 추가합니다. 🎬로 표시하고, ★ (SOIL의 ★는 '핵심 슬라이드'로 의미가 다름)는 사용하지 마십시오:
슬라이드 6: 왜 항상 성립할까? 면적 퍼즐 🎬
- 슬라이드 역할: 프로세스 슬라이드
- 유일한 핵심 포인트: 큰 정사각형과 삼각형이 변하지 않았으므로, 빈 공간의 면적이 같음
- animate: { frame: 04-proof, poster: last }
frame은 이 슬라이드가 영상에서 해당하는 프레임의 파일명입니다 (.html 제외). §3에서 스토리보드를 작성할 때 고정됩니다: STORYBOARD.md에서 해당 프레임의 src가 compositions/frames/04-proof.html이면, 여기에는 04-proof를 작성합니다. 각 구간의 길이는 나레이션이 결정하며, 여기서 지정하지 않습니다.
애니메이션을 만들 가치가 있는 슬라이드 (전체 2–4페이지, 각 구간 15–25초): '시간에 따른 변화' 자체가 핵심인 슬라이드.
| 적합 | 예시 |
|---|---|
| 유도, 증명, 과정 | 퍼즐 증명, 반응 진행, 빛의 경로 |
| 수량 누적 또는 비교 | 9 + 16 = 25 격자 세기, 막대 그래프 성장 |
| 오개념 전환 | 잘못된 설명 → X 표시 → 올바른 설명으로 전환 |
| 공간 관계 | 회전, 평행 이동, 전개도, 기울어진 정사각형 |
부적합: 정의, 표, 연습 문제, 요약. 이러한 슬라이드는 SOIL의 visual 템플릿을 사용하는 것으로 충분합니다.
3. 프레젠테이션 흐름에서 영상 스크립트 작성 (연결)
영상의 프레임 구성은 프레젠테이션의 흐름을 따릅니다. 먼저 영상 프로젝트의 BRIEF.md에 대조표를 작성하고, 여기서 파일명을 결정합니다:
| 프레젠테이션 슬라이드 | 영상 프레임 (파일명) | 나레이션 내용 | 용도 |
|---|---|---|---|
| 2 문제 도입 | 01-ladder | 엔진 1의 상황 문제 | 영상에만 사용 |
| 🎬 3 | 02-squares | 이 슬라이드의 유일한 핵심 포인트 | p03으로 잘라냄 |
| 4–5 개념 | 03-names | 전체 개념 + 용어 | 영상에만 사용 |
| 🎬 6 | 04-proof | 이 슬라이드의 유일한 핵심 포인트 | p06으로 잘라냄 |
| … | … | … | … |
| 13 요약 | 07-takeaway | 엔진 1의 '한 문장으로 정리' | 영상에만 사용 |
잘라낸 클립의 이름은 p + 두 자리 슬라이드 번호 (p03, p06)를 사용하며, build_deck.js의 video(s, "p06", …) 이름과 일치해야 합니다.
그런 다음 motion-explainer의 형식에 따라 SCRIPT.md (각 프레임당 한 줄 ## Line N)와 STORYBOARD.md (각 프레임의 src는 위 표의 파일명 사용)를 작성합니다. 나레이션 자료는 SOIL의 결과물을 직접 사용합니다:
- 엔진 1의 '세 가지 일반적인 오해' → 오개념 전환 프레임의 세 부분 나레이션.
- 엔진 1의 '한 문장으로 정리' → 마지막 프레임의 나레이션, 한 글자도 변경하지 않으며, 프레젠테이션의 요약 슬라이드에서도 동일한 문장을 사용합니다.
- 각 🎬 슬라이드의 '유일한 핵심 포인트' → 해당 프레임 나레이션의 마지막 문장.
- 길이는 초당 4–4.5자 중국어로 추정합니다. 🎬 프레임의 나레이션은 약 70–100자 (15–25초)입니다.
🎬 프레임을 worker에 전달할 때, _dispatch_context.md에 다음 네 가지를 추가로 작성해야 합니다 (이 프레임은 소리와 자막 없이 재생되므로 별도로 처리됨):
- 화면만 봐도 이해할 수 있어야 함: 중요한 숫자, 레이블, 결론은 화면에 표시되어야 하며, 나레이션에만 의존해서는 안 됩니다.
- 빈 상태에서 시작하여 결론에서 끝남: 마지막 상태는 완전하고 최소 1초 이상 정지 상태여야 합니다. 그것이 프레젠테이션의 썸네일이기 때문입니다.
- 중요한 내용을 자막 영역에 배치하지 마십시오 (y > 896 px): 영상의 자막이 가릴 수 있으며, 프레젠테이션에는 자막이 없지만 동일한 프레임이 양쪽에서 사용되어야 합니다.
- 앞뒤 프레임의 연결 (동일한 사다리, 동일한 삼각형)은 평소대로 진행합니다. 편집 시 전환重叠 부분이 자동으로 제거됩니다.
다음은 완전히 motion-explainer의 프로세스를 따릅니다: 나레이션 → 카메라 → 각 프레임 (sub-agent가 있으면 병렬, 없으면 순차) → 조립, 자막, 색상 → 초안 검토 → B 출력.
4. 무음 쌍둥이 버전 → 🎬 클립 잘라내기
절대 경로를 사용하거나 동일한 폴더에서 실행합니다. 아래에서는 $FILM이 영상 프로젝트의 전체 경로를 나타냅니다:
python <smd>/scripts/make_silent.py --project "$FILM" # $FILM-silent 생성: 모든 오디오 트랙과 자막 제거
(cd "$FILM-silent" && npx hyperframes render --quality high --output renders/silent.mp4)
python <smd>/scripts/cut_clips.py --project "$FILM" --video "$FILM-silent/renders/silent.mp4" \
--map p03=02-squares p06=04-proof p09=06-myths@first --out "$FILM/deck/media"
cut_clips.py는index.html의 각 프레임 시작 시간과 길이를 읽고, '이전 프레임과 중첩되는 부분이 끝나는 지점'부터 '다음 프레임과 중첩되는 부분이 시작되는 지점'까지 잘라냅니다. 전환이 crossfade, push-slide 또는 zoom-through이든 관계없이 앞뒤 프레임이 혼합되지 않습니다. 각 클립의 길이를 출력하고, 길이를 발표자 노트에 기록합니다.- 썸네일 이미지는 기본적으로 마지막 프레임 (결론에서 정지)입니다. 클립별로 지정하려면
--map뒤에@first또는@초를 추가합니다 (예:[email protected]). 오개념 슬라이드에서 답을 미리 보여주지 않으려면@first를 사용하세요. - 영상이 수정된 경우 (프레임, 색상, 길이 중 하나라도 변경):
make_silent.py재실행, 출력,cut_clips.py재실행. 쌍둥이 버전은 스냅샷이며 자동으로 변경되지 않습니다.
5. 프레젠테이션 제작 (SOIL 엔진 4~6)
- 엔진 4 (인지 교정), 엔진 5 (스타일)는 SOIL의 규칙에 따라 진행합니다. 엔진 5의 색상은 1단계에서 선택한 방식을 사용합니다.
- 프레젠테이션 폴더를 생성합니다. 반드시 이 스킬의
package.json을 사용하세요: HyperFrames 프로젝트 루트 디렉터리의package.json은"type": "module"이며, 자체package.json이 없으면require가 실패합니다.
mkdir -p "$FILM/deck" && cp <smd>/templates/package.json <smd>/templates/deck_helpers.js "$FILM/deck/"
cp <smd>/templates/example_pythagoras.js "$FILM/deck/build_deck.js" # 예제를 수정하여 시작
(cd "$FILM/deck" && npm install)
build_deck.js를 수정합니다. 시작 부분은 다음과 같이 고정합니다. 레이아웃은 항상LAYOUT_WIDE(13.333 × 7.5 인치)를 사용합니다: SOIL의 PptxGenJS 치트시트는LAYOUT_16x9(10인치 너비)와 Microsoft JhengHei 폰트를 사용하지만, 여기서는 그대로 사용하지 마십시오.deck_helpers.js의 좌표는 13.333인치 너비로 설계되었습니다. 폰트는 SOIL의 규칙을 따릅니다: 제목GenSekiGothic2 TW H, 본문GenSekiGothic2 TW M.
const path = require("path");
const pptxgen = require("pptxgenjs");
const { makeDeck } = require("./deck_helpers");
const pptx = new pptxgen();
pptx.layout = "LAYOUT_WIDE";
const D = makeDeck(pptx, { theme: { bg: "121212", fg: "F2F0EA", accent: "FF7A1A" }, mediaDir: path.join(__dirname, "media") });
const s = D.lightSlide("프로세스・애니메이션");
D.title(s, "왜 항상 성립할까? 면적 퍼즐");
D.video(s, "p06", 0.6, 2.0, 8.0); // 왼쪽 8인치 너비 재생 창 (16:9), 아래에 '클릭하여 재생' 안내 표시
// 오른쪽 x = 9.0, 너비 3.9인치: 애니메이션의 단계 요약 또는 핵심 포인트 배치, 재생되지 않을 때도 슬라이드를 이해할 수 있도록
pptx.writeFile({ fileName: path.join(__dirname, "단원명.pptx") });
- 🎬 슬라이드 레이아웃: 왼쪽에 애니메이션 (8.0 × 4.5 인치), 오른쪽에 2–3줄의 단계 또는 결론. 발표자 노트에는 다음을 작성: 클립 길이, 학생에게 질문할 지점, 예상 답변.
- 다른 슬라이드는 SOIL의 슬라이드 역할과 visual 템플릿에 따라 제작합니다. 연습 문제의 답변은 발표자 노트에만 작성하고, 슬라이드에는 표시하지 않습니다.
node build_deck.js. 클립이 아직 준비되지 않은 경우,video()는 먼저 자리 표시자 프레임을 배치하므로 레이아웃을 미리 볼 수 있습니다.
사용자가 이미 SOIL 프레젠테이션을 가지고 있는 경우: PptxGenJS는 기존 .pptx를 수정할 수 없습니다. SOIL의 '진단 수정 모드'에 따라 각 슬라이드의 내용을 읽습니다 (python -m markitdown <파일>.pptx, markitdown이 없으면 사용자에게 텍스트를 붙여넣도록 요청). 유지할 슬라이드는 원본 그대로 build_deck.js에 작성하고, 선택한 슬라이드는 🎬 레이아웃으로 변경한 후, 전체를 다시 생성합니다. 전달 시에는 새로 생성된 파일이며 원본 파일은 변경되지 않았음을 설명합니다.
6. 검토
python <smd>/scripts/thumbs.py "$FILM/deck/<프레젠테이션>.pptx" --out "$FILM/deck/thumbs" # 슬라이드당 PNG + 개요 이미지 deck-*.png
SOIL 문서에 언급된 thumbnail.py (pptx 스킬)를 별도로 찾을 필요 없이, 이 thumbs.py를 사용하세요. 개요 이미지를 슬라이드별로 확인하고, 아래 체크리스트와 대조하세요:
- 🎬 슬라이드의 썸네일이 애니메이션의 마지막 프레임이며, 검은 프레임이나 자리 표시자 텍스트가 아님
- 애니메이션 창이 다른 텍스트나 그래픽에 의해 가려지지 않음; 아래의 재생 안내가 보임
- 각 슬라이드에는 하나의 핵심 포인트만 있음; 샌드위치 구조 (어두운 색상의 표지, 행동, 결론, 밝은 색상의 내용 슬라이드)
- 프레젠테이션과 영상의 강조 색상이 동일한 계열임 (밝은 슬라이드에서는 한 단계 더 어두운 동일 계열 색상 사용 가능, 예:
#E8601A를 영상의#FF7A1A와 함께 사용), 숫자와 용어가 양쪽에서 일치함 - 발표자 노트에 클립 길이, 질문할 지점, 답변이 있음
- 연습 문제의 답변을 직접 계산해 보았음
- B 영상 검토는
review-loop에 따라 완료됨 (각 문장마다 프레임 캡처, 슬라이드별 확인)
7. 전달
.pptx 및 MP4의 경로를 제공하고, 다음 사항을 설명합니다:
- 🎬 슬라이드가 몇 번 슬라이드인지, 각각 몇 초인지, 어떤 프레임이 썸네일로 사용되었는지.
- 재생할 컴퓨터에 GenSekiGothic2 폰트가 미리 설치되어 있어야 합니다 (.pptx는 폰트를 내장하지 않음; 설치되지 않으면 시스템 폰트로 대체됨).
- 영상이 .pptx에 내장되어 있으며, PowerPoint 및 Keynote에서 직접 재생할 수 있습니다. Google Slides로 가져올 경우 내장 영상이 유지되지 않을 수 있습니다. Google Slides를 사용해야 하는 경우,
media/*.mp4를 Google Drive에 업로드한 후 영상을 삽입하세요. - 나레이션 발음은 사용자가 직접 귀로 들어보도록 안내하세요 (ASR은 성조를 구분하지 못함). 잘못 발음된 글자는 motion-explainer의
fix_voice.py로 수정합니다. 수정 후에는 클립을 다시 잘라내고 프레젠테이션을 다시 빌드해야 합니다.
references/lessons.md는 첫 번째 구현 (〈피타고라스 정리〉, 13페이지, 3개 애니메이션, 110초 영상)에서 겪은 시행착오를 기록한 문서입니다.