044 · 모션그래픽 통합 제작환경
Claude Code에 영상·3D·수학애니메이션·제너러티브아트·자막·실사편집 제작 능력을 붙이는 8개 도구의 설치 전 과정이다. 프로그램 정식 명칭, 받는 곳, 설치되는 절대 경로, 검증 명령, 실패 지점까지 전부 적었다. GitHub에 접근할 수 없는 환경을 위한 오프라인 경로도 함께 제공한다.
--check로 설치 상태만 볼 수도 있다.node_modules 미포함.| 정식 명칭 | 만든 곳 | 무엇을 하는 도구인가 | 받는 곳 / 설치 형태 |
|---|---|---|---|
| Remotion v4.0.536 | Remotion (remotion.dev) | React 컴포넌트로 영상을 만든다. 프레임 번호를 받아 화면을 그리면 그게 영상이 된다. 데이터 기반 영상·자막·차트 애니메이션에 강하다. | npm 레지스트리 · 전역 설치 없음, 프로젝트 단위(npx create-video) |
| HyperFrames v0.8.167 | HeyGen | HTML+CSS+GSAP로 영상을 만든다. 헤드리스 크롬으로 그린 뒤 ffmpeg로 mp4를 뽑는다. 웹 지식만으로 영상을 만들 수 있어 Remotion의 대안이 된다. | npm(CLI) + Claude Code 플러그인(스킬 9종, GitHub 마켓플레이스) |
| MCP for Blender (blender-mcp) | ahujasid | Claude가 Blender를 직접 조작한다. 오브젝트 생성·머티리얼·파이썬 실행·Poly Haven/Sketchfab 에셋 로드까지. 3D 장면을 대화로 만든다. | PyPI(uvx mcp-for-blender) + Blender 앱(blender.org / brew cask) |
| Manim CE v0.22.0 | Manim Community | 수학·개념 애니메이션. 3Blue1Brown이 쓰는 그 엔진의 커뮤니티판. 수식 전개, 그래프, 알고리즘 시각화. | PyPI(pip3 install --user manim) + 스킬은 GitHub |
| algorithmic-art | Pardesco | 코드로 만드는 제너러티브 아트. 시드 기반 결정론적 생성 — 캔버스·SVG(플로터/레이저용)·WebGL 셰이더·오디오 반응형·루프 영상. | GitHub 저장소(스킬 폴더) · MIT |
~/.claude/skills/<이름>/SKILL.md에 있는 마크다운 지시문 묶음이다. 설치란 사실상 폴더를 그 위치에 두는 것이고, 그러면 Claude가 관련 요청에서 알아서 그 문서를 읽고 따른다. 실행 파일이 아니라서 오프라인 팩으로 통째 전달해도 그대로 동작한다.
| 구성요소 | GitHub 필요 | 실제로 받는 곳 | 차단 시 대안 |
|---|---|---|---|
| Remotion 본체 | 불필요 | npm 레지스트리 | — |
| Remotion 공식 스킬 12종 | 필요 | GitHub remotion-dev/skills | 없음. 자체 라이선스라 오프라인 팩에 미포함. 본체만으로도 제작은 가능 |
| HyperFrames CLI | 불필요 | npm hyperframes@0.8.167 | — |
| HyperFrames 스킬 9종 | 필요하나 대체됨 | GitHub 마켓플레이스 | 오프라인 팩에 포함(Apache-2.0) |
| MCP for Blender 서버 | 불필요 | PyPI uvx mcp-for-blender | — |
| Blender 애드온(270KB) | 불필요 | PyPI 패키지에 번들 blender_mcp/bundled/addon.py | — (설치본과 번들본 sha256 동일 확인) |
| Blender 앱 | 불필요 | blender.org 또는 brew cask | blender.org 직접 다운로드 |
| Manim CE | 불필요 | PyPI | — |
| manim-skill | 필요하나 대체됨 | GitHub awesome-skills/manim-skill | 오프라인 팩에 포함(MIT) |
| algorithmic-art | 필요하나 대체됨 | GitHub Pardesco/algorithmic-art-skill | 오프라인 팩에 포함(MIT) |
| Homebrew 패키지 (ffmpeg·cairo·pango·uv) | 간접 의존 | bottle이 ghcr.io(GitHub 인프라) | ffmpeg → ffmpeg.org 공식 빌드 uv → pip3 install --user uvcairo·pango → conda-forge |
curl -fsSL https://design-portfolio-7yo.pages.dev/motion-stack-offline.zip -o pack.zip
unzip pack.zip && cd motion-stack-offline
bash install-offline.sh # 스킬 11종 배치 + Blender MCP 등록(PyPI 경유)
skills/ 11종(manim-skill · algorithmic-art · hyperframes 9종) · starters/ 2종 · install-offline.sh · ATTRIBUTION.md(출처·라이선스 전문). 설치 스크립트는 이미 있는 스킬은 건너뛴다(멱등).~는 홈 디렉터리(/Users/<사용자명>).| 경로 | 무엇 | 비고 |
|---|---|---|
~/.claude/skills/ | 전역 스킬 폴더 | manim-skill · algorithmic-art · hyperframes 9종이 여기 들어간다. 폴더를 넣고 빼는 것이 설치/제거 |
~/.claude/plugins/ | 플러그인 캐시·마켓플레이스 | cache/hyperframes/hyperframes/0.8.167/에 LICENSE 포함 |
~/Library/Application Support/ | Blender 애드온 | 270KB. PyPI 패키지가 자동 배치한다. Blender 버전이 다르면 5.2 부분이 바뀐다 |
/Applications/Blender.app | Blender 본체 | brew cask 또는 blender.org 설치본 |
~/Library/Python/3.11/bin | pip --user 실행파일 | 기본 PATH에 없다 — manim 명령을 못 찾는 가장 흔한 원인(9번 참고) |
<프로젝트>/.agents/skills/remotion-* | Remotion 공식 스킬 | 전역이 아니라 프로젝트 스코프. 12종, skills-lock.json으로 버전 고정 |
~/.cache/uv/ | uvx 패키지 캐시 | mcp-for-blender 실체가 여기 풀린다 |
ls ~/.claude/skills/ # 스킬 폴더 목록
claude plugin list # 플러그인(hyperframes@hyperframes)
claude mcp list | grep blender # MCP 서버 연결 상태
ls "$HOME/Library/Application Support/Blender/5.2/scripts/addons/"
node -v && npm -v # v24.14.1 / 11.11.0 에서 검증
ffmpeg -version | head -1 # 9.0.1
PATH="$HOME/Library/Python/3.11/bin:$PATH" manim --version # v0.22.0
# 또는 스크립트로 한 번에 (설치는 하지 않음)
bash motion-stack-setup.sh --check
curl -fsSL https://design-portfolio-7yo.pages.dev/motion-stack-setup.sh -o setup.sh
bash setup.sh --check # ① 먼저 현재 상태만 확인 (아무것도 설치/수정하지 않는다)
bash setup.sh # ② 실제 설치
GitHub·npm·PyPI 각각에 실제로 붙어보고 이후 경로를 분기한다.
ffmpeg·uv·cairo·pango·pkgconf
npm 경로. 플러그인은 GitHub 가능할 때만.
PyPI 서버 등록 + 애드온 자동 배치
PyPI + 스킬 폴더(차단 시 오프라인 팩 안내)
설치된 버전을 표로 찍어준다.
--check 모드에서는 파일·설정을 일절 건드리지 않는다.# Homebrew (없을 때만)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Node — Remotion 요구사항: ^20.19.0 || ^22.13.0 || >=24
brew install node
node -v # 검증 환경: v24.14.1 / npm 11.11.0
# 시스템 패키지
# ffmpeg : 영상 인코딩 (HyperFrames·Remotion·algorithmic-art 공용)
# uv : uvx로 Blender MCP 서버 실행
# cairo·pango·pkgconf : Manim 렌더 백엔드
# ⚠️ brew 공식 이름은 pkg-config가 아니라 pkgconf 다
brew install ffmpeg uv cairo pango pkgconf
# 전역 설치할 것이 없다. 프로젝트를 만든다.
npx create-video@latest my-video
cd my-video && npm install
# Claude Code용 공식 스킬 12종 (프로젝트 스코프 → .agents/skills/remotion-*)
# ⚠️ 이 단계만 GitHub(remotion-dev/skills)을 탄다
npx remotion skills add
# 검증
npx remotion studio # http://localhost:3000 미리보기
npx remotion render HelloMotion out/hello.mp4 # 스타터 팩 예제
# 기대 출력: + out/hello.mp4 약 45.9 kB (1280×720, 30fps, 90프레임)
create-video 기본 스캐폴드의 MyComp는 null을 반환한다. 그대로 렌더하면 완전히 빈 영상이 나오는데 이게 정상 동작이다 — 설치 실패로 오해하기 쉽다. 눈으로 확인하려면 7번의 HelloMotion.tsx를 쓸 것.# 방법 A — Claude Code 플러그인 (스킬 9종이 전역으로 붙는다, GitHub 필요)
claude plugin marketplace add heygen-com/hyperframes
claude plugin install hyperframes@hyperframes
claude plugin list # hyperframes@hyperframes · 0.8.167 · user · enabled
# 방법 B — CLI만 (npm, GitHub 불필요). 스킬 자동 진입은 없지만 제작·렌더는 전부 가능
npx --yes hyperframes@0.8.167 init my-video
cd my-video
npx --yes hyperframes@0.8.167 check # 린트 + 모션/대비 검증
npx --yes hyperframes@0.8.167 render # mp4 (헤드리스 크롬 + ffmpeg)
# 설치되는 스킬 9종:
# hyperframes(라우터) · -animation · -audio · -cli · -core
# -creative · -keyframes · -registry · -studio
# Blender 앱 (검증 버전 5.2.2 LTS)
brew install --cask blender # 또는 blender.org에서 직접 다운로드
# MCP 서버 등록 — PyPI에서 받는다
claude mcp add -s user blender -- uvx mcp-for-blender
# 검증
claude mcp list | grep blender # blender: uvx mcp-for-blender - ✔ Connected
# 애드온은 패키지에 번들돼 있어 서버가 직접 배치한다(별도 다운로드 불필요)
ls "$HOME/Library/Application Support/Blender/5.2/scripts/addons/blender_mcp.py"
# Manim 본체 (PyPI)
pip3 install --user manim
# ⚠️ pip --user 실행파일은 기본 PATH에 없다 → 셸 설정에 추가
echo 'export PATH="$HOME/Library/Python/3.11/bin:$PATH"' >> ~/.zshrc
export PATH="$HOME/Library/Python/3.11/bin:$PATH" # 현재 셸 즉시 적용
manim --version # Manim Community v0.22.0
# 스킬 2종 — GitHub 가능할 때
git clone https://github.com/awesome-skills/manim-skill.git ~/.claude/skills/manim-skill
git clone https://github.com/Pardesco/algorithmic-art-skill.git ~/.claude/skills/algorithmic-art
# GitHub 차단 환경이면 오프라인 팩으로 (동일 내용, MIT)
# curl -fsSL https://design-portfolio-7yo.pages.dev/motion-stack-offline.zip -o pack.zip
# unzip pack.zip && cd motion-stack-offline && bash install-offline.sh
Blender 실행 → Edit → Preferences → Add-ons
3D 뷰포트에서 N 키 → 사이드바
Claude Code에서
claude mcp list → blender ✔ Connectedimport { AbsoluteFill, interpolate, useCurrentFrame, useVideoConfig } from "remotion";
// 설치가 제대로 됐는지 눈으로 확인하는 용도.
// npx remotion render HelloMotion out/hello.mp4
export const HelloMotion: React.FC = () => {
const frame = useCurrentFrame();
const { durationInFrames } = useVideoConfig();
const opacity = interpolate(frame, [0, 20], [0, 1], { extrapolateRight: "clamp" });
const y = interpolate(frame, [0, 24], [28, 0], { extrapolateRight: "clamp" });
const out = interpolate(frame, [durationInFrames - 15, durationInFrames], [1, 0], {
extrapolateLeft: "clamp",
});
return (
<AbsoluteFill
style={{
backgroundColor: "#0f172a",
justifyContent: "center",
alignItems: "center",
fontFamily: "Pretendard, -apple-system, system-ui, sans-serif",
}}
>
<div
style={{
opacity: opacity * out,
transform: `translateY(${y}px)`,
color: "#a3e635",
fontSize: 84,
fontWeight: 800,
letterSpacing: "-0.03em",
}}
>
Remotion OK
</div>
<div style={{ opacity: opacity * out, color: "#94a3b8", fontSize: 28, marginTop: 14 }}>
frame {frame} / {durationInFrames}
</div>
</AbsoluteFill>
);
};
import { Composition } from "remotion";
import { MyComposition } from "./Composition";
import { HelloMotion } from "./HelloMotion";
export const RemotionRoot: React.FC = () => {
return (
<>
{/* 기본 스캐폴드 컴포지션 (내용 없음 — 빈 영상이 정상이다) */}
<MyComposition />
{/* 설치 확인용 동작 예제 */}
<Composition
id="HelloMotion"
component={HelloMotion}
durationInFrames={90}
fps={30}
width={1280}
height={720}
/>
</>
);
};
// ─── package.json ─────────────────────────────────────────────
{
"name": "remotion-starter",
"private": true,
"engines": { "node": "^20.19.0 || ^22.13.0 || >=24" },
"dependencies": {
"@remotion/cli": "4.0.536",
"react": "19.2.3",
"react-dom": "19.2.3",
"remotion": "4.0.536"
},
"devDependencies": {
"@remotion/eslint-config-flat": "4.0.536",
"@types/react": "19.2.7", "@types/web": "0.0.166",
"eslint": "10.12.0", "prettier": "3.8.1", "typescript": "5.9.3"
},
"scripts": {
"dev": "remotion studio",
"build": "remotion bundle",
"lint": "eslint src && tsc"
}
}
// ─── remotion.config.ts ───────────────────────────────────────
import { Config } from "@remotion/cli/config";
Config.setRspack(true);
Config.setVideoImageFormat("jpeg");
Config.setOverwriteOutput(true);
Config.setDefaultPremountInSeconds(2);
// ─── src/index.ts (엔트리) ────────────────────────────────────
import { registerRoot } from "remotion";
import { RemotionRoot } from "./Root";
registerRoot(RemotionRoot);
data-composition-id / data-start / data-duration / data-track-index 속성이 렌더러가 읽는 계약이고, GSAP 타임라인을 window.__timelines["main"]에 등록하면 렌더러가 프레임마다 그걸 시킹한다(재생이 아니라 시킹이라 결정론적이다).<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
margin: 0; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#root {
width: 100%; height: 100%; display: flex;
align-items: center; justify-content: center;
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
}
#title { color: #f4f4f5; font-size: 72px; font-weight: 600; letter-spacing: -0.03em; }
</style>
</head>
<body>
<div
id="root"
data-composition-id="main"
data-start="0"
data-duration="10"
data-width="1920"
data-height="1080"
>
<h1 id="title" class="clip" data-start="0" data-duration="10" data-track-index="0">Title</h1>
</div>
<script>
const tl = gsap.timeline({ paused: true });
// 예: tl.fromTo("#title", { opacity: 0, y: 24 }, { opacity: 1, y: 0, duration: 0.6 }, 0);
window.__timelines["main"] = tl;
tl.seek(0);
</script>
</body>
</html>
// ─── package.json — 전역 설치 없이 npx로 CLI를 고정 버전 호출 ───
{
"name": "hyperframes-starter",
"private": true,
"type": "module",
"scripts": {
"dev": "npx --yes hyperframes@0.8.167 preview",
"check": "npx --yes hyperframes@0.8.167 check",
"render": "npx --yes hyperframes@0.8.167 render",
"publish": "npx --yes hyperframes@0.8.167 publish"
}
}
// ─── hyperframes.json — 레지스트리·경로 설정 ───────────────────
{
"$schema": "https://hyperframes.heygen.com/schema/hyperframes.json",
"registry": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
"paths": {
"blocks": "compositions",
"components": "compositions/components",
"assets": "assets"
},
"media": { "autoProxy": true }
}
// ⚠️ registry가 raw.githubusercontent.com을 가리킨다 →
// GitHub 차단 환경에서는 레지스트리 블록 설치(hyperframes add)가 실패한다.
// 직접 작성한 index.html 렌더는 레지스트리 없이도 정상 동작한다.
| 도구 | 확인 명령 | 성공 기준(실측) |
|---|---|---|
| Remotion | npx remotion render HelloMotion out/hello.mp4 | + out/hello.mp4 45.9 kB · 1280×720 · 90프레임. 재생 시 라임색 "Remotion OK" + 프레임 카운터 |
| HyperFrames | npm run check → npm run render | check 통과 후 renders/에 mp4 생성 |
| Manim | manim --version → 예제 씬 렌더 | Manim Community v0.22.0, mp4 산출 |
| Blender MCP | claude mcp list | grep blender | ✔ Connected + GUI에서 서버 시작 후 "큐브 만들어줘"가 실제로 동작 |
| algorithmic-art | Claude에게 "코드로 제너러티브 아트 만들어줘" | 스킬이 자동 진입해 시드 기반 코드 생성 |
ffmpeg -ss 1.2 -i hello.mp4 -frames:v 1 frame.png로 프레임을 뽑아 눈으로 확인한 뒤 배포했다. 빈 영상(기본 MyComp)도 "성공" 로그를 남기기 때문이다.pip3 install --user가 넣는 ~/Library/Python/3.11/bin이 기본 PATH에 없다. 셸 설정에 추가하고 새 터미널을 열어야 적용된다. 기존 세션은 export PATH=...로 즉시 적용.
Homebrew의 공식 포뮬러 이름은 pkgconf다. pkg-config는 별칭이라 환경에 따라 혼동을 준다. 설치 확인도 brew list --versions pkgconf로.
Remotion 기본 스캐폴드의 MyComp는 null 반환이 정상이다. 설치 문제가 아니다. HelloMotion을 렌더해 확인할 것.
claude mcp list가 Connected여도 Blender GUI에서 Start MCP Server를 누르지 않으면 동작하지 않는다. Blender를 끄면 서버도 꺼지므로 쓸 때마다 다시 켠다.
Homebrew는 미리 빌드된 bottle을 GitHub Container Registry에서 받는다. GitHub이 차단되면 설치가 멈춘다. ffmpeg는 ffmpeg.org 공식 빌드, uv는 pip3 install --user uv, cairo·pango는 conda-forge로 우회.
hyperframes.json의 registry가 raw.githubusercontent.com을 가리킨다. 레지스트리 블록 설치만 막히고, 직접 작성한 컴포지션 렌더는 정상이다.
스킬은 ~/.claude/skills/<이름>/SKILL.md 구조여야 인식된다. 압축을 잘못 풀어 ~/.claude/skills/manim-skill/manim-skill/SKILL.md처럼 한 겹 더 들어가면 인식되지 않는다. ls ~/.claude/skills/<이름>/SKILL.md로 확인할 것. 또한 스킬 추가 후에는 Claude Code 세션을 새로 시작해야 반영된다.
| 이렇게 말하면 | 이게 동작한다 |
|---|---|
| "Remotion으로 15초 인트로 만들어줘" | remotion-* 스킬 12종 (프로젝트 스코프) |
| "/hyperframes로 제품 소개 영상 만들어줘" 또는 자연어로 영상 요청 | hyperframes 라우터 스킬이 자동 진입해 하위 스킬(animation·audio·keyframes…)을 고른다 |
| "매님으로 푸리에 변환 시각화해줘" | manim-skill |
| "코드로 제너러티브 아트 만들어줘" | algorithmic-art |
| "블렌더로 이 장면 만들어줘" | blender MCP (GUI에서 서버 시작 후) |
| 자산 | 출처 | 라이선스 | 오프라인 팩 포함 |
|---|---|---|---|
| manim-skill | awesome-skills/manim-skill | MIT | 포함 |
| algorithmic-art | Pardesco/algorithmic-art-skill | MIT © 2026 Pardesco | 포함 (node_modules 제외) |
| HyperFrames 스킬 9종 | HeyGen HyperFrames v0.8.167 | Apache-2.0 | 포함 (LICENSE 동봉) |
| Remotion 공식 스킬 | remotion-dev/skills | 자체 라이선스 | 미포함 — 각자 npx remotion skills add |
| MCP for Blender | ahujasid/blender-mcp (PyPI 배포) | 저장소 표기 따름 | 불필요 — PyPI에서 직접 |
license 필드가 SEE LICENSE IN LICENSE.md로, 개인·소규모는 무료지만 기업 사용은 별도 조건이 붙는 모델이다. 이 때문에 오프라인 팩에도 Remotion 스킬을 넣지 않았다.ATTRIBUTION.md와 각 폴더의 원본 LICENSE를 그대로 넣어뒀다 — 이 팩을 다시 배포할 때 제거하지 말 것.
~/.claude/skills/<이름>/SKILL.md에 있는 마크다운 지시문 묶음. 실행 파일이 아니다. Claude가 요청을 보고 "이 스킬이 맞겠다" 싶으면 그 문서를 읽고 따른다.
설치 = 폴더를 그 경로에 두는 것. 그래서 오프라인 팩으로 통째 옮겨도 그대로 동작한다. 인식 안 되면 ① 폴더가 한 겹 더 들어갔는지 ② 세션을 새로 시작했는지 확인.
스킬·에이전트·명령을 묶어 배포하는 단위. claude plugin install로 설치하며 마켓플레이스(대개 GitHub 저장소)에서 받는다.
HyperFrames가 이 방식이고, 설치하면 스킬 9종이 한꺼번에 붙는다. 확인: claude plugin list.
Claude가 외부 프로그램과 대화하는 표준 규격. 스킬이 "지시문"이라면 MCP는 "도구 연결"이다.
Blender MCP는 별도 프로세스(서버)가 떠서 Claude의 요청을 Blender 조작으로 번역한다. 그래서 Blender 앱 안에서 서버를 켜야 하고, 앱을 끄면 연결이 끊긴다. 확인: claude mcp list.
둘 다 "설치하지 않고 바로 실행"하는 도구다. npx는 npm 패키지를, uvx는 Python 패키지를 임시로 받아 실행한다.
그래서 uvx mcp-for-blender는 전역 설치 흔적을 남기지 않고, 캐시는 ~/.cache/uv/에 쌓인다.
화면 없는 브라우저로 페이지를 그려서 이미지·영상으로 뽑는 방식. HyperFrames가 이 구조다 — HTML/CSS/GSAP로 만든 화면을 프레임 단위로 캡처해 ffmpeg로 mp4를 만든다.
따라서 웹에서 되는 것은 대부분 영상으로도 된다. 반대로 브라우저가 못 그리는 건 영상에도 안 나온다.
"영상 한 편"의 단위. Remotion은 <Composition id durationInFrames fps width height>로 등록하고, HyperFrames는 data-composition-id 속성으로 선언한다.
한 프로젝트에 여러 컴포지션을 두고 이름으로 골라 렌더한다: npx remotion render <id> out.mp4.
영상 렌더링은 재생이 아니라 시킹이다. "0.0초 상태를 그려라 → 캡처 → 0.033초 상태를 그려라 → 캡처" 방식이라 느린 컴퓨터에서도 결과가 똑같다(결정론적).
그래서 HyperFrames는 GSAP 타임라인을 paused: true로 만들어 window.__timelines에 등록한다 — 렌더러가 직접 시킹하기 위해서다.
제너러티브 아트에서 난수의 출발점. 같은 시드 = 같은 결과물이라, 마음에 든 작품을 다시 만들 수 있다.
시드를 안 고정하면 "아까 그거 다시"가 불가능하다. algorithmic-art가 시드 기반인 이유.
| 만들려는 것 | 1순위 | 대안 | 고르는 이유 |
|---|---|---|---|
| 데이터가 바뀌면 영상도 바뀌어야 함(차트·리포트·주간 요약) | Remotion | — | React라 props로 데이터를 흘려넣고 반복 생성이 쉽다. 자막·차트 라이브러리 생태계가 그대로 쓰인다 |
| 브랜드 필름·킨타입·설명 영상(웹 디자인 감각 그대로) | HyperFrames | Remotion | HTML/CSS를 그대로 쓴다. 디자인 토큰·웹폰트·CSS 효과를 영상에 가져오기 가장 빠르다. 대비·모션 검사가 내장 |
| 수식 전개·알고리즘·그래프 설명 | Manim | — | 수학 객체(수식·좌표계·변환)가 1급 시민. 다른 도구로 하면 전부 수작업 |
| 3D 장면·제품 렌더·카메라 워크 | Blender MCP | — | 진짜 3D 엔진이 필요한 영역. 대화로 장면을 조립하고 파이썬으로 세밀 조정 |
| 배경 패턴·추상 비주얼·플로터/레이저 출력물 | algorithmic-art | HyperFrames | 시드 기반 결정론 + SVG 출력(물리 출력 가능) |
| 짧은 인트로 타이틀(5~15초) | HyperFrames | Remotion | 둘 다 가능. HTML로 빨리 만들 거면 전자, 컴포넌트 재사용이 목적이면 후자 |
| 위 요소들을 섞은 한 편 | 각자 뽑고 ffmpeg로 이어붙인다 | 도구마다 강한 구간이 다르다. 한 도구로 전부 하려다 막히는 경우가 가장 흔하다 | |
# 1) 각 도구로 구간을 뽑는다 (해상도·fps를 반드시 통일할 것)
# intro.mp4 ← HyperFrames (1920x1080, 30fps)
# formula.mp4 ← Manim (manim -qh 로 1080p 렌더)
# chart.mp4 ← Remotion (width/height/fps를 Composition에 명시)
# 2) 목록 파일을 만든다
cat > list.txt <<'EOF'
file 'intro.mp4'
file 'formula.mp4'
file 'chart.mp4'
EOF
# 3) 재인코딩 없이 이어붙이기 (코덱·해상도·fps가 같을 때만 가능)
ffmpeg -f concat -safe 0 -i list.txt -c copy final.mp4
# 사양이 다르면 통일해서 다시 인코딩
ffmpeg -i formula.mp4 -vf "scale=1920:1080,fps=30" -c:v libx264 -crf 18 formula_fixed.mp4
# 확인: 길이·해상도·코덱이 의도대로인지
ffprobe -v error -show_entries stream=codec_name,width,height,r_frame_rate \
-show_entries format=duration -of default=nw=1 final.mp4
from manim import *
class HelloManim(Scene):
def construct(self):
title = Text("Motion Stack OK", font_size=56, color=GREEN)
circle = Circle(radius=1.4, color=BLUE).shift(DOWN * 1.2)
self.play(Write(title), run_time=1.2) # 글자가 그려지며 등장
self.play(title.animate.shift(UP * 1.6), run_time=0.6) # .animate = 상태 전이
self.play(Create(circle), run_time=1.0) # 선이 그려짐
self.play(circle.animate.set_fill(BLUE, opacity=0.5), run_time=0.8)
self.wait(0.4)
# PATH가 안 잡혀 있으면 먼저 (pip --user 설치본)
export PATH="$HOME/Library/Python/3.11/bin:$PATH"
manim -ql --disable_caching scene.py HelloManim # 빠른 미리보기 480p15
manim -qh scene.py HelloManim # 최종 1080p60
manim -qk scene.py HelloManim # 4K
manim -ql -s scene.py HelloManim # 마지막 프레임만 PNG로
# 출력 위치: ./media/videos/scene/<품질>/HelloManim.mp4
# 실측(이 환경, -ql): 5개 애니메이션 · 4.0초 영상 렌더 완료
Scene.construct()가 진입점 ② self.play(...)에 들어가는 것이 애니메이션(Write·Create·FadeIn…) ③ 기존 객체를 바꿀 땐 obj.animate.메서드() — 이 .animate를 빼면 즉시 변경돼 애니메이션이 안 생긴다(가장 흔한 실수).<div id="root" data-composition-id="main"
data-start="0" data-duration="6" data-width="1920" data-height="1080">
<h1 id="title" class="clip" data-start="0" data-duration="6" data-track-index="0">
Motion Stack
</h1>
<p id="sub" class="clip" data-start="0.6" data-duration="5.4" data-track-index="1">
HTML · CSS · GSAP
</p>
</div>
<script>
const tl = gsap.timeline({ paused: true }); // ★ 반드시 paused
tl.fromTo("#title",
{ opacity: 0, y: 28 },
{ opacity: 1, y: 0, duration: 0.8, ease: "power3.out" }, 0);
tl.fromTo("#sub",
{ opacity: 0 },
{ opacity: 1, duration: 0.6 }, 0.6); // ← 세 번째 인자 = 시작 시각(초)
tl.to("#title, #sub", { opacity: 0, duration: 0.5 }, 5.2);
window.__timelines["main"] = tl; // ★ 렌더러가 이걸 시킹한다
tl.seek(0);
</script>
check가 먼저다npm run check # 린트 + 모션 + 대비(WCAG) 검사
npm run render # mp4 (헤드리스 크롬 + ffmpeg)
npm run dev # 미리보기 서버
# 이 환경에서의 실제 check 출력:
# ◇ 0 issues across 9 sample(s)
# Motion ◇ 0 errors, 0 warnings
# Contrast ◇ 59/59 text checks pass WCAG AA
# ◇ Check passed
paused: true — 자동 재생하면 렌더 결과가 들쭉날쭉해진다 ② 요소마다 data-start/data-duration으로 존재 구간을 선언 ③ window.__timelines["<composition-id>"]에 등록. 셋 중 하나만 빠져도 "화면은 맞는데 영상은 비어 있는" 결과가 나온다.import { AbsoluteFill, interpolate, spring, useCurrentFrame, useVideoConfig } from "remotion";
type Props = { data: { label: string; value: number }[] };
export const BarChart: React.FC<Props> = ({ data }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const max = Math.max(...data.map((d) => d.value));
return (
<AbsoluteFill style={{ background: "#0f172a", padding: 80, gap: 18,
flexDirection: "column", justifyContent: "center" }}>
{data.map((d, i) => {
// 막대마다 4프레임씩 지연 — stagger
const p = spring({ frame: frame - i * 4, fps, config: { damping: 14 } });
const w = interpolate(p, [0, 1], [0, (d.value / max) * 100]);
return (
<div key={d.label} style={{ display: "flex", alignItems: "center", gap: 16 }}>
<div style={{ width: 160, color: "#94a3b8", fontSize: 28 }}>{d.label}</div>
<div style={{ height: 44, width: `${w}%`, background: "#e879f9", borderRadius: 8 }} />
<div style={{ color: "#e2e8f0", fontSize: 26 }}>
{Math.round(d.value * p)}
</div>
</div>
);
})}
</AbsoluteFill>
);
};
// Root.tsx 에 등록 — defaultProps로 데이터를 주입한다
<Composition
id="BarChart" component={BarChart}
durationInFrames={150} fps={30} width={1920} height={1080}
defaultProps={{ data: [
{ label: "Remotion", value: 82 },
{ label: "HyperFrames", value: 74 },
{ label: "Manim", value: 61 },
] }}
/>
useCurrentFrame()이 전부다 — 현재 프레임을 받아 그 시점의 화면을 그리면 된다 ② interpolate(frame, [입력범위], [출력범위])로 값 매핑 ③ 자연스러운 움직임은 spring(). CSS transition·animation은 쓰지 않는다 — 시킹 방식이라 반영되지 않는다.# props를 JSON으로 주입해 같은 컴포지션을 다른 데이터로 렌더
npx remotion render BarChart out/week1.mp4 \
--props='{"data":[{"label":"A","value":40},{"label":"B","value":90}]}'
# 파일에서 읽어 반복 (주간 리포트 자동화 패턴)
for f in data/week*.json; do
npx remotion render BarChart "out/$(basename "$f" .json).mp4" --props="$f"
done
코드로 제너러티브 아트 만들어줘.
조건:
- 플로우 필드 기반, 선 중심 구성
- 캔버스 1080x1080, 배경은 아주 어두운 네이비
- 팔레트는 3색 이내, 라임 계열 강조색 하나
- 시드를 고정해서 같은 결과를 다시 뽑을 수 있게 해줘
- 결과를 PNG로 저장하고, 시드 값을 파일명에 넣어줘
나중에 플로터로 출력할 수도 있으니 SVG 버전도 같이 만들어줘.
[1단계] Blender를 열고 N키 → MCP 탭 → Start MCP Server 를 눌렀는지 먼저 확인
(이걸 안 하면 claude mcp list가 Connected여도 조작이 안 된다)
[2단계] Claude에게:
"블렌더에 지름 2짜리 구를 원점에 만들고,
금속 재질(roughness 0.2)을 입혀줘.
카메라는 45도 위에서 내려다보게 배치하고,
HDRI 환경광을 넣어서 1920x1080으로 렌더해줘."
[3단계] 결과를 확인하고 세부 조정:
"구를 조금 왼쪽으로 옮기고, 바닥에 반사되는 평면을 추가해줘"
※ 복잡한 작업은 한 번에 요청하지 말고 단계로 나눌 것.
3D는 중간 결과를 봐야 다음 지시가 정확해진다.
| 증상 / 에러 메시지 | 원인 | 조치 |
|---|---|---|
zsh: command not found: manim | pip --user 바이너리 경로가 PATH에 없음 | export PATH="$HOME/Library/Python/3.11/bin:$PATH" (영구 적용은 ~/.zshrc에 추가 후 새 터미널) |
Manim 설치 중 pycairo 빌드 실패 | cairo·pango 헤더 없음 | brew install cairo pango pkgconf 후 재설치. brew 이름은 pkg-config가 아니라 pkgconf |
| Manim 렌더는 되는데 한글이 네모로 나옴 | 기본 폰트에 한글 글리프 없음 | Text("한글", font="Pretendard")처럼 폰트를 명시(설치된 폰트명이어야 함) |
| Manim에서 아무 움직임이 없음 | .animate를 빼고 속성을 직접 바꿈 | self.play(obj.animate.shift(UP)) 형태로. 직접 대입은 즉시 적용되어 애니메이션이 생기지 않는다 |
| Remotion 렌더 결과가 완전히 검거나 비어 있음 | 기본 스캐폴드 MyComp가 null 반환 | 정상 동작이다. HelloMotion 같은 실제 컴포지션을 렌더할 것 |
| Remotion에서 CSS 애니메이션이 영상에 안 나옴 | 렌더는 재생이 아니라 프레임 시킹이라 CSS 애니메이션이 진행되지 않음 | useCurrentFrame() + interpolate()/spring()으로 프레임 기반으로 다시 작성 |
Remotion Cannot find module / 버전 충돌 | Node 버전 미달 | ^20.19.0 || ^22.13.0 || >=24 필요. node -v 확인 후 brew install node |
| HyperFrames 렌더 결과가 정지 화면 | 타임라인을 window.__timelines에 등록하지 않음, 또는 paused:true 누락 | 두 가지를 모두 확인. tl.seek(0)까지 호출 |
HyperFrames check에서 대비 경고 | 텍스트/배경 대비가 WCAG AA 미달 | 색을 조정한다. 영상은 압축으로 대비가 더 떨어지므로 경고를 무시하지 말 것 |
hyperframes add가 실패 | 레지스트리가 raw.githubusercontent.com | GitHub 차단 환경이면 레지스트리 블록은 못 받는다. 직접 작성한 컴포지션 렌더는 정상 |
Blender: claude mcp list는 Connected인데 조작이 안 됨 | Blender 안에서 Start MCP Server를 안 누름 | N키 → MCP 탭 → Start. Blender를 끄면 매번 다시 켜야 한다 |
| Blender Add-ons 목록에 MCP가 안 보임 | 애드온 파일이 해당 Blender 버전 경로에 없음 | ~/Library/Application Support/Blender/<버전>/scripts/addons/ 확인. 버전이 다르면 경로의 숫자가 다르다 |
uvx: command not found | uv 미설치 | brew install uv 또는 pip3 install --user uv |
| brew install이 ghcr.io에서 멈춤 | Homebrew bottle이 GitHub 인프라에서 옴 | 2장의 대안 사용(ffmpeg.org 빌드·conda-forge·pip) |
| 스킬을 넣었는데 Claude가 모름 | 폴더 깊이 오류 또는 세션 미갱신 | ~/.claude/skills/<이름>/SKILL.md 구조 확인 후 새 세션 시작 |
| 작업 | 설정 | 실측 | 메모 |
|---|---|---|---|
| Remotion 렌더 | 1280×720 · 30fps · 90프레임(3초) | 45.9 KB 산출 | 텍스트 위주라 용량이 작다. 영상·이미지가 들어가면 수십 배 |
| Manim 렌더 | -ql 480p15 · 애니메이션 5개 · 4초 | 즉시 수준 | -qh(1080p60)는 수 배, -qk(4K)는 수십 배 걸린다. 작업 중엔 항상 -ql |
| HyperFrames check | 9 샘플 · 대비 59항목 | 0 issues 통과 | 렌더보다 훨씬 빠르다. 렌더 전에 항상 check |
| HyperFrames 데모 영상 | 1920×1080 · 10초 | 3.0 MB | h264. 웹 삽입용으로 적정 |
| 프레임 추출(확인용) | ffmpeg 1프레임 | 즉시 | ffmpeg -ss 1.2 -i a.mp4 -frames:v 1 f.png |
Manim -ql, Remotion은 짧은 durationInFrames로 구조부터 맞춘다. 최종 품질은 마지막에 한 번.
HyperFrames는 check가 렌더보다 훨씬 싸다. 모션·대비 문제를 렌더 전에 거른다.
"렌더 성공" 로그는 내용 보장이 아니다. 프레임을 뽑아 눈으로 확인하는 습관이 가장 싸게 먹힌다.
ffmpeg -frames:v 1# 스킬 (폴더를 지우면 끝)
rm -rf ~/.claude/skills/manim-skill
rm -rf ~/.claude/skills/algorithmic-art
# 플러그인
claude plugin uninstall hyperframes@hyperframes
claude plugin marketplace remove hyperframes # 마켓플레이스 등록도 해제
# MCP 서버 등록 해제
claude mcp remove blender -s user
# Blender 애드온은 따로 지운다
rm -f "$HOME/Library/Application Support/Blender/5.2/scripts/addons/blender_mcp.py"
# Manim (PyPI)
pip3 uninstall manim
# ~/.zshrc, ~/.bash_profile 에 추가한 PATH 줄도 직접 삭제
# Blender 앱
brew uninstall --cask blender
# 캐시 정리 (용량 회수)
rm -rf ~/.cache/uv # uvx 패키지 캐시
npm cache clean --force # npx 캐시
# 프로젝트는 폴더째 지우면 된다 (전역에 남는 것이 없다)
rm -rf my-video
| 되돌릴 때 알아둘 것 | 설명 |
|---|---|
| Remotion은 지울 게 거의 없다 | 전역 설치가 없다. 프로젝트 폴더만 지우면 끝(공식 스킬도 프로젝트 안 .agents/에 있다) |
| 스킬 제거는 즉시 반영되지 않는다 | Claude Code 세션을 새로 시작해야 목록에서 빠진다 |
| Blender 설정은 앱 삭제 후에도 남는다 | ~/Library/Application Support/Blender/ 전체를 지우면 사용자 설정까지 사라지니 주의 — 애드온 파일만 골라 지울 것 |
| brew 제거가 다른 것을 깨뜨릴 수 있다 | ffmpeg·cairo는 다른 도구도 쓴다. brew uses --installed ffmpeg로 의존을 먼저 확인 |
| 구간 | 도구 | 산출물 | 이 구간에서 확인할 것 |
|---|---|---|---|
| 0~5초 인트로 | HyperFrames | intro.mp4 1920×1080·30fps | npm run check에서 대비 경고 0건. 타이틀이 5초 안에 완전히 등장·퇴장 |
| 5~25초 개념 설명 | Manim | formula.mp4 — -qh로 1080p | 수식 전개가 읽을 시간을 주는가(self.wait()). 한글 폰트 명시했는가 |
| 25~40초 데이터 | Remotion | chart.mp4 — props로 실제 수치 주입 | 숫자가 실제 데이터와 일치하는가. 막대 stagger가 과하지 않은가 |
| 합치기 | ffmpeg | final.mp4 | 해상도·fps·코덱 통일 확인 후 -c copy. 다르면 재인코딩 |
# 1) 각 구간 산출 (해상도·fps를 처음부터 맞춘다 — 나중에 맞추면 재인코딩 손실)
cd intro-hf && npm run check && npm run render # → renders/*.mp4
cd ../formula && manim -qh scene.py Explain # → media/videos/.../Explain.mp4
cd ../chart-rem && npx remotion render BarChart out/chart.mp4 --props=data.json
# 2) 사양 확인 — 다르면 합치기가 실패하거나 결과가 깨진다
for f in intro.mp4 formula.mp4 chart.mp4; do
echo -n "$f: "
ffprobe -v error -select_streams v:0 \
-show_entries stream=width,height,r_frame_rate,codec_name -of csv=p=0 "$f"
done
# 3) 합치기
printf "file 'intro.mp4'\nfile 'formula.mp4'\nfile 'chart.mp4'\n" > list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy final.mp4
# 4) 결과를 "열어서" 확인 (구간 경계마다 프레임 추출)
for t in 2 10 30; do ffmpeg -y -ss $t -i final.mp4 -frames:v 1 check_$t.png; done
대화로 장면 구성 → 1920×1080 연속 렌더(PNG 시퀀스 또는 mp4)
프레임 추출로 조명·구도 점검. 여기서 틀리면 뒤 작업이 전부 헛일
렌더 결과를 <video>로 깔고 그 위에 HTML 텍스트·로고 오버레이
영상 위 텍스트는 대비가 떨어지기 쉽다 — WCAG 검사로 거른다
최종 mp4
# 전제: Remotion 컴포지션이 props로 데이터를 받도록 작성돼 있다(13장 ③)
# 1) 데이터 준비 — 주차별 JSON
# data/2026-W41.json, data/2026-W42.json ...
# 2) 일괄 렌더
mkdir -p out
for f in data/*.json; do
name=$(basename "$f" .json)
echo "── 렌더: $name"
npx remotion render BarChart "out/$name.mp4" --props="$f" || echo "❌ 실패: $name"
done
# 3) 산출물 검증 — 파일이 생겼다고 내용이 맞는 건 아니다
for m in out/*.mp4; do
d=$(ffprobe -v error -show_entries format=duration -of default=nw=1:nk=1 "$m")
s=$(wc -c < "$m")
echo "$m ${d}s ${s}B"
# 길이가 0이거나 용량이 비정상적으로 작으면 빈 영상일 가능성
done
# 4) 한 편만 눈으로 확인 (샘플링)
ffmpeg -y -ss 2 -i out/2026-W41.mp4 -frames:v 1 sample.png
data.length === 0일 때 눈에 띄는 경고 화면을 그리게 해두면 샘플만 봐도 즉시 드러난다.□ node -v → v20.19+ / v22.13+ / v24+
□ ffmpeg -version | head -1 → 버전이 찍힌다
□ uv --version → 버전이 찍힌다
□ manim --version → Manim Community v0.22+
□ ls -d /Applications/Blender.app → 존재
□ claude mcp list | grep blender → ✔ Connected
□ claude plugin list → hyperframes@hyperframes · enabled
□ ls ~/.claude/skills/ → manim-skill, algorithmic-art, hyperframes* 포함
※ 한 번에: bash motion-stack-setup.sh --check
□ Remotion : npx remotion render HelloMotion out/hello.mp4
→ 약 45KB · 재생 시 라임색 "Remotion OK" 보임
□ HyperFrames : npm run check → "Check passed" / npm run render → mp4 생성
□ Manim : manim -ql scene.py HelloManim
→ media/videos/scene/480p15/HelloManim.mp4
□ algorithmic-art : Claude에게 "코드로 제너러티브 아트" 요청 → 시드 포함 PNG
□ Blender : GUI에서 Start MCP Server 후
"큐브 하나 만들어줘" → 뷰포트에 실제로 생성됨
※ 각 산출물을 반드시 "열어서" 확인할 것.
렌더 성공 로그는 내용이 맞다는 증거가 아니다.
이 스택의 도구는 전부 프레임 시킹 모델이다. 그 개념이 잡히면 도구 전환이 쉬워진다 — 11장의 "시킹 vs 재생"을 다시 읽을 것.
편집기·출력 파이프라인을 직접 만들 계획이라면 029·042·043 구현 핸드북으로. 단일 데이터 모델·오프스크린 렌더·검증 스위트가 정리돼 있다.
도구별 활용 시나리오와 조합 예시는 044 기능 보드에 사용 예 중심으로 정리돼 있다.
| 추가 | 메우는 공백 | 라이선스 | 검증 결과 |
|---|---|---|---|
| whisper.cpp + 자체 자막 스킬 | 받아쓰기·자막 — 완전 오프라인 | MIT (ggml-org) | 한국어 문장 받아쓰기 정확, SRT 생성 확인. Metal GPU 백엔드 사용 |
| text-to-lottie | 웹·앱에 심는 벡터 애니메이션(Lottie JSON) | MIT | 스킬 + Skia Skottie 플레이어 프로젝트 설치 |
| OpenMontage | 실사 푸티지 조립·나레이션 영상 (12개 파이프라인) | AGPLv3 | 데모 렌더 완주 — 1920×1080·30fps·25초·3.6MB, 프레임 육안 확인 |
brew install whisper-cpp # 1.9.5 (ggml-org 공식)
# 모델은 별도 다운로드. 한국어 정확도를 위해 large-v3-turbo 권장(1.5GB)
mkdir -p ~/.cache/whisper-models && cd ~/.cache/whisper-models
curl -fL -o ggml-large-v3-turbo.bin \
"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-v3-turbo.bin"
# 가벼운 대안: ggml-small.bin(488MB) · ggml-base.bin(148MB)
# whisper.cpp는 16kHz 모노 WAV만 받는다. 영상은 먼저 변환
ffmpeg -y -i input.mp4 -ar 16000 -ac 1 -c:a pcm_s16le audio.wav
# 받아쓰기 (❌ -l ko 를 빼면 영어로 가정해 엉뚱하게 나온다)
whisper-cli -m ~/.cache/whisper-models/ggml-large-v3-turbo.bin \
-l ko -f audio.wav -osrt # -ovtt -otxt -oj 동시 지정 가능
# 소프트 자막(끌 수 있음, 빠름)
ffmpeg -y -i input.mp4 -i audio.wav.srt -c copy -c:s mov_text output.mp4
# 하드 자막(영상에 각인, SNS·발표용)
ffmpeg -y -i input.mp4 -vf "subtitles=audio.wav.srt:force_style='FontName=Pretendard,FontSize=22,BorderStyle=3'" \
-c:v libx264 -crf 18 -c:a copy output.mp4
hyperframes doctor → ✓ whisper-cpp /opt/homebrew/bin/whisper-cli). HyperFrames 쪽 자막 기능도 함께 살아난 셈이다.# 저장소에 스킬과 "Skia Skottie 플레이어" 프로젝트가 함께 들어있다.
# 스킬은 이 플레이어로 결과를 검증하도록 설계돼 있어 둘 다 필요하다.
git clone --depth 1 https://github.com/diffusionstudio/lottie.git /tmp/lottie
# 1) 스킬은 전역 스킬 경로로 (폴더 한 겹 더 들어가지 않게 주의)
cp -R /tmp/lottie/skills/text-to-lottie ~/.claude/skills/text-to-lottie
# 2) 플레이어 프로젝트는 작업 폴더에 둔다
mv /tmp/lottie <프로젝트>/lottie-player
cd <프로젝트>/lottie-player && npm install && npm run dev
git clone --depth 1 https://github.com/calesthio/OpenMontage.git openmontage
cd openmontage
make setup # .venv 생성 + pip + remotion npm + piper-tts + hyperframes 캐시
# 검증
make hyperframes-doctor
make demo-list # zero-key 데모 3종
.venv/bin/python render_demo.py code-to-screen # 실측: 750프레임 → 3.6MB mp4
hyperframes doctor가 알려준 선택 항목 중 로컬 TTS(Kokoro)를 함께 설치했다. 기존 media-use 스킬의 음성·음악은 HeyGen CLI 로그인을 요구하는데, Kokoro는 로컬에서 돌아 로그인도 인터넷도 필요 없다.# 로컬 TTS — 설치함 (나레이션)
pip3 install --user kokoro-onnx soundfile
# 확인: hyperframes doctor → ✓ TTS (Kokoro) Kokoro deps installed
# 로컬 BGM — 설치하지 않음 (torch 포함 약 2.5GB)
# 필요해지면: pip install transformers torch soundfile numpy
| 만들려는 것 | 도구 | 비고 |
|---|---|---|
| 데이터가 바뀌면 영상도 바뀜 | Remotion | props로 반복 생성 |
| 브랜드 영상·타이틀·카메라 무브 | HyperFrames | HTML/CSS/GSAP |
| 수식·그래프 설명 | Manim | 수학 객체가 1급 |
| 3D 장면·제품 렌더 | Blender MCP | GUI에서 서버 시작 필요 |
| 배경 패턴·플로터 출력 | algorithmic-art | 시드 고정 필수 |
| 말을 글로 / 자막 | whisper.cpp | 완전 오프라인, 한국어는 -l ko |
| 앱·웹에 심는 애니메이션 | text-to-lottie | MP4 아님, JSON |
| 실사 푸티지 조립·나레이션 | OpenMontage | AGPLv3 주의 |