بنقرة واحدة
animation-viewer
AI/ML 애니메이션 플레이어 scene 구현·승격·연동.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
AI/ML 애니메이션 플레이어 scene 구현·승격·연동.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Babel 다중파일 JSX 프로젝트 scope 관리.
파일 편집 전 반드시 읽고 검증하는 절차.
Transformer 해부도 Z/P 데이터 편집·검증.
| name | animation-viewer |
| description | AI/ML 애니메이션 플레이어 scene 구현·승격·연동. |
| version | 1.0.0 |
| author | vault |
| metadata | {"hermes":{"tags":["animation","visualization","deep-learning"],"category":"ml-education"}} |
1920×1080 다크 캔버스(SVG viewBox 0 0 1600 900) 위에서 딥러닝 개념을 scene-by-scene으로 애니메이션하는 플레이어.
AI-ML Animation Player.html ← 진입점 (11 Unit 통합)
Unit 01 Animation.html ← Unit 01 전용 (독립 실행 가능)
animation-viewer/
player-app.jsx ← 통합 플레이어 App (unit/scene/step 상태 관리 + 그래프 토글 + 해부도 링크 + backlink)
unit01-app.jsx ← Unit 01 전용 App (독립 버전)
tween-engine.js ← JS timer tween (rAF, cubic-bezier solver)
u01-shared.jsx ← 다크 캔버스 SVG 프리미티브 (전 Unit 공유)
u01-scenes-a.jsx ← Unit 01 전용 Scene 01~05
u01-scenes-b.jsx ← Unit 01 전용 Scene 06~09
generic-visualizers.jsx ← 범용 시각화 8종 (sketch.kind 기반)
special-visualizers.jsx ← 전용 시각화 4종 (params.kind 패턴 매칭)
u0507-scenes.jsx ← ⭐ scene_id override 5종 (U05 gate ×3, U07 QK·softmax)
graph-view.jsx ← Wiki 그래프 뷰 (102 노드 + backlink 인덱스)
PROMPT.md ← 통합 시스템 설계 문서 (참고용)
storyboard/
curriculum.js ← 11 Unit 메타데이터 (window.SB_CURRICULUM)
data-unit01.js ~ data-unit11.js ← scene 데이터 (window.SB_UNITS.unitXX)
AI-ML Animation Player.html의 script 로드 순서 (변경 금지):
<!-- 1. React + Babel + KaTeX -->
<script src="react@18.3.1"></script>
<script src="react-dom@18.3.1"></script>
<script src="@babel/standalone@7.29.0"></script>
<script src="katex@0.16.9"></script>
<!-- 2. DS 번들 (ScenePlayer, FrameBar 등) -->
<script src="_ds/.../ds_bundle.js"></script>
<!-- 3. Tween 엔진 (plain JS, NOT babel) -->
<script src="animation-viewer/tween-engine.js"></script>
<!-- 4. 데이터 (plain JS, window.SB_UNITS에 등록) -->
<script src="storyboard/data-unit01.js"></script>
...
<script src="storyboard/data-unit11.js"></script>
<!-- 5. 시각화 컴포넌트 (Babel, 순서 중요) -->
<script type="text/babel" src="animation-viewer/u01-shared.jsx"></script> ← 프리미티브
<script type="text/babel" src="animation-viewer/u01-scenes-a.jsx"></script> ← U01 전용
<script type="text/babel" src="animation-viewer/u01-scenes-b.jsx"></script> ← U01 전용
<script type="text/babel" src="animation-viewer/generic-visualizers.jsx"></script>
<script type="text/babel" src="animation-viewer/special-visualizers.jsx"></script>
<script type="text/babel" src="animation-viewer/u0507-scenes.jsx"></script> ← scene_id override
<script type="text/babel" src="animation-viewer/graph-view.jsx"></script>
<script type="text/babel" src="animation-viewer/player-app.jsx"></script> ← 마지막
새 파일 추가 시: player-app.jsx 앞에 배치. player-app.jsx는 반드시 마지막.
window.SB_UNITS.unitXX = {
id: "unitXX", no: "XX", topic: 1~5,
title_en: "...", title_ko: "...",
core_question: "...", // 단원 핵심 질문
correct_framing: "...", // 올바른 프레이밍
avoid: ["..."], // 금지 표현
routing: {
emphasis: "표현: ...", // 5-Unit Frame 강조 slot
types: ["packet_flow", ...] // 사용하는 animation_type
},
importance: { usage, necessity, difference, strength },
scenes: [ ... ]
}
{
scene_id: "uXX_sYY", // 고유 ID (Unit XX, Scene YY)
no: "YY", // 2자리 번호
title: "한국어 제목",
title_en: "English title",
animation_type: ["packet_flow"], // 배열 (복수 가능)
five_unit_frame: {
data: "...", // slot 0: 데이터
representation: "...", // slot 1: 표현
calculation_target: "...", // slot 2: 계산 대상
concept_classification: "...", // slot 3: 개념 분류
importance: { type: "usage|necessity|difference|strength", text: "..." } // slot 4
},
moving_object: "...", // 핵심 움직이는 객체 설명
scene_objects: [ { id, type, role, visibility } ],
animation_steps: [ // 각 항목 = 1 tween step (caption으로 표시)
"step 1 설명",
"step 2 설명",
...
],
zoom: { required: bool, reason: "..." },
formula_lock: {
needed: bool,
formula: "LaTeX string", // KaTeX로 렌더링
when_to_show: "..."
},
// ⭐ 핵심: 시각화 디스패치 키
sketch: {
kind: "packet_flow", // generic-visualizers.jsx의 KIND_MAP 키
params: { ... } // 해당 visualizer의 파라미터
},
links: [
{ target: "uXX_sYY", label: "...", type: "next|prev|cross|related", note: "..." }
],
prereqs: [
{ target: "_unit:unitXX", concept: "...", label: "...", note: "..." }
],
correction: "교정 문구 (있으면 amber callout)"
}
플레이어는 scene의 sketch.kind로 시각화를 선택한다:
window.GenericVis)| sketch.kind | 컴포넌트 | params 키 |
|---|---|---|
packet_flow | VPacketFlow | stations[], packet, labels[] |
matrix_build | VMatrixBuild | rows[], cols[], cells[][], highlightRow, highlight[r,c], pullRow, caption |
before_after_contrast | VBeforeAfter | left{title,note}, right{title,note}, arrow |
xy_transform | VXYTransform | points[{x,y,label,near}], pair[i,j] |
zoom_hierarchy | VZoomHierarchy | levels[{scope,blocks[{label,focus}],content}], focus, breadcrumb |
formula_lock | VFormulaLock | stations[], packet, formula |
gate_mask | VGateMask | values[], labels[], caption |
_generic | VGeneric (fallback) | kind (라벨용) |
window.SpecialVis)_generic fallback에 도달하기 전, params.kind 문자열을 정규식으로 매칭:
| params.kind 패턴 | 컴포넌트 | 설명 |
|---|---|---|
/sigmoid|tanh|relu.*curve/ | VSquashCurve | 실제 수학 함수 곡선 + 샘플 점 |
/weighted_?sum/ | VRowWeightedSum | weights[], vlabels[], result — distribution × V → weighted sum (params 없으면 기본값) |
/parallel_?heads/ | VParallelHeads | Multi-Head 병렬 분기 + heatmap |
/heatmap|query focus/ | VHeatmapFocus | n×n attention 패턴 + row focus |
GenericScene(scene, step, p)
├── ⭐ window.SceneVisOverrides[scene.scene_id] 있음? → 전용 override (u0507-scenes.jsx)
├── sketch.kind → KIND_MAP[kind] 찾음? → 해당 generic visualizer
├── 못 찾음 → window.SpecialVis.match(params.kind) 시도
│ ├── 매칭됨 → special visualizer
│ └── 매칭 안 됨 → VGeneric (fallback: 점선 박스 + kind 라벨)
└── correction 있으면 amber callout, prereqs 있으면 참조 라벨 추가
window.SceneVisOverrides)scene_id 키 → 컴포넌트. KIND_MAP보다 우선. 현재 등록:
| scene_id | 컴포넌트 | 내용 |
|---|---|---|
u05_s03 | VLstmGate(forget) | σ→f_t bar→⊙ C_{t−1}→결과 (유지/폐기 verdict) |
u05_s04 | VLstmGate(input) | tanh→C̃_t + σ→i_t→⊙ |
u05_s06 | VLstmGate(output) | C_t→tanh + σ→o_t→⊙→h_t |
u07_s04 | VQKDot | Q₂가 K₁~K₄와 순차 dot product → score row + ÷√d_k |
u07_s05 | VSoftmaxDist | score bars → softmax box → distribution bars + Σ=1 segment |
새 override 추가: 컴포넌트 작성 → window.SceneVisOverrides[scene_id] = 컴포넌트 등록.
props: { g, params, scene } (g = 전체 진행도 0~1).
player-app.jsx에서 U.id === "unit01" 이면 U01_VIS[scene] 배열에서 전용 컴포넌트 직접 선택.
전용 Scene 목록: u01-scenes-a.jsx (Scene0105), 09).u01-scenes-b.jsx (Scene06
window.U01Shared)모든 시각화가 공유하는 빌딩 블록. 전 Unit에서 사용.
bg: "#0F1110" // 캔버스 배경
card: "#171A18" // 카드 fill
cardBorder: "#2C312E" // 카드 border
text: "#E8EAE7" // 주 텍스트
dim: "#9BA29D" // 보조 텍스트
faint: "#5A615C" // 희미한 텍스트/선
line: "#3A403C" // 연결선
accent: "#8B78F0" // violet (강조)
accentSoft: "rgba(139,120,240,0.16)"
teal: "#22B8A5" // teal (Encoder, 데이터)
tealSoft: "rgba(34,184,165,0.15)"
amber: "#E5B84B" // amber (Decoder, gate)
amberSoft: "rgba(229,184,75,0.14)"
red: "#E5716B" // red (Mask, 위험)
stepP(step, p, i) // step > i → 1, step === i → p, step < i → 0
lerp(a, b, t) // 선형 보간
clamp01(v) // 0~1 클램프
stag(t, i, n, overlap) // n개 항목에 stagger 분배, i번째의 로컬 진행도
| 이름 | 용도 | 주요 props |
|---|---|---|
DarkGrid() | 배경 도트 격자 (80px 간격) | 없음 |
TChip | 텍스트 칩 (rect + text) | x,y,w,h,text,p,accent,mono,dashed,dim,fontSize |
CardBox | 큰 카드 컨테이너 | x,y,w,h,p,label,accent,children |
Flow | 화살표 (선 + 삼각형 머리) | x1,y1,x2,y2,p,dashed,color |
Callout | 하단 보조 설명 박스 | x,y,w,text,p,tone(dim/amber/teal),fontSize |
Lbl | 자유 라벨 | x,y,text,p,size,fill,anchor,mono,weight |
Packet | 이동 패킷 (강조 사각형) | cx,cy,p,text,w,h |
Generic visualizer는 g = clamp01((step + p) / nSteps) 전체 진행도를 구간별로 분배:
const pr = (g, a, b) => clamp01((g - a) / (b - a));
// pr(g, 0, 0.3) → g=0~0.3 구간에서 0→1
// pr(g, 0.3, 0.6) → g=0.3~0.6 구간에서 0→1
전용 Scene은 stepP(step, p, i) 로 개별 step 단위 제어.
window.TweenEngine)const { tween, tokenMs, tokenEase, cubicBezier } = window.TweenEngine;
tween({ duration, ease, onUpdate, onDone }) → rAF 기반 0→1 트윈tokenEase("--ease-default") → CSS 커스텀 프로퍼티에서 cubic-bezier 파싱tokenMs("--duration-normal") → CSS 토큰에서 ms 값 읽기clock 옵션으로 외부 시간 주입 가능unit → 현재 Unit 인덱스 (0~10)
scene → 현재 Scene 인덱스 (0-based)
step → 현재 animation_step 인덱스 (0-based)
p → 현재 step 내 진행도 (0~1, tween으로 구동)
playing → 자동 재생 여부
slot → FrameOverlay에 표시 중인 5-Unit Frame slot (null~4)
step 변화 → tween(0→1, STEP_MS=1500ms) → p 업데이트
p=1 도달 + playing → HOLD_MS=750ms 대기 → advance()
advance():
step+1 < nSteps → step++
else scene+1 < nScenes → scene++, step=0
else unit+1 < 11 → unit++, scene=0, step=0
else → playing=false (끝)
마지막에서 ▶ → unit=0, scene=0, step=0 (처음부터)
localStorage("aiml_anim_player") → {unit, scene, step} 저장/복원#u=07&s=5 → Unit 07 Scene 5 (해시가 localStorage보다 우선)→ advance, ← 되감기, Space 재생/정지const canvas = U.id === "unit01"
? React.createElement(U01_VIS[scene], { step, p })
: <GenericScene scene={cur} step={step} p={p} />;
const { ScenePlayer, FrameBar } = window.MLAnimationsDesignSystem_947957;
씬 플레이어 셸. 캔버스 + 컨트롤 바 + 씬 칩 목록.
<ScenePlayer
scenes={[{ label: "칩라벨", steps: 5 }, ...]}
scene={sceneIdx} step={stepIdx} playing={bool}
onSceneChange={(i) => ...}
onStepChange={(i) => ...}
onPlayToggle={() => ...}
caption="현재 step 설명 텍스트"
canvasHeight="min(54vw, calc(100vh - 360px))">
{/* children = 캔버스 안쪽 (SVG + 오버레이) */}
</ScenePlayer>
5-Unit 프레임 바 (데이터/표현/계산대상/개념분류/중요성).
<FrameBar size="sm" states={["collapsed","open","collapsed","collapsed","collapsed"]}
onSelect={(slotIdx) => ...} />
Generic visualizer는 sketch.params만으로 그리므로 표현력에 한계가 있다. 중요한 scene은 전용 컴포넌트로 승격하여 정교한 애니메이션을 구현한다.
U05 gate_mask — LSTM gate 시각화. 현재 VGateMask는 단순 막대그래프. 전용 버전은:
U07 attention — Attention 핵심 3 scene:
QKᵀ score 행렬 → 실제 dot product 계산 과정 (current: matrix_build)row → V weighted sum → VRowWeightedSum 있지만 커스텀 data 매핑 필요전체 output 행렬 → 행별 weighted sum 결과가 쌓이는 과정U09/U10 zoom_hierarchy → Transformer 해부도(transformer-anatomy/)와 실제 연동
1. 대상 scene의 data-unit*.js 읽기
→ animation_steps, scene_objects, sketch.params 파악
2. 전용 컴포넌트 작성 (새 파일 또는 special-visualizers.jsx에 추가)
- props: { step, p } (Unit 01 스타일) 또는 { g, params, scene } (generic 스타일)
- U01Shared 프리미티브 사용
- stepP() 또는 pr(g, start, end) 패턴으로 타이밍 제어
3. 디스패치 등록:
방법 A — sketch.kind를 새 키로 변경 + KIND_MAP에 추가
방법 B — params.kind 패턴을 SpecialVis.match()에 추가
방법 C — Unit 전용이면 player-app.jsx에서 직접 분기
4. HTML에 <script> 태그 추가 (special-visualizers.jsx 앞)
5. ready_for_verification으로 검증
special-visualizers.jsx에 추가u05-scenes.jsx 등 Unit별 파일 분리window.XXX = { ... } 로 export, 다른 파일에서 참조player-app.jsx의 ANATOMY_MAP (scene_id → { level, z }).
매핑된 scene은 LINKS 행에 teal "⧉ 해부도 Z*" 칩 표시 →
transformer-anatomy/Transformer%20Anatomy.html?level=<levelId>&from=<scene_id> 링크.anatomy-app.jsx의 SceneBacklink 컴포넌트가 ?from= 파라미터를 읽어
툴바에 "◂ 애니메이션 UXX SYY" 버튼 표시 → ../AI-ML%20Animation%20Player.html#u=XX&s=Y.?level=L3-self URL 파라미터로 직접 진입 가능 (parent chain 자동 복원).| Unit | scene → level |
|---|---|
| U07 | s02→L4-self · s04/s05/s06/s08/s09→L5 (SDPA) |
| U08 | s01/s02/s04/s05/s07/s08→L3-self · s03→L4-self |
| U09 | s06→L2-enc · s07→L3-self · s08→AddNorm · s09→L8 · s11/s14→L1 |
| U10 | s02/s12/s13→L2-dec · s03→L3-masked · s04→L3-cross · s05/s10→L1 · s11→L0 |
U05 LSTM은 Transformer 해부도 대상이 아니므로 매핑 없음.
해부도 편집은 별도 Skill (transformer-anatomy/SKILL.md) 참조.
window.WikiGraph = { GraphView, buildBacklinks }G 키 → 캔버스 영역이 GraphView로 교체_gvTok 헬퍼 — hex 하드코딩 금지)buildBacklinks(UNITS) 인덱스를 App에서 1회 계산 →
각 scene 하단에 "← REFERENCED BY" 점선 칩 (3개 + "+N" 펼치기) → 클릭 시 출발 scene으로 이동packet_flow:
sketch: {
kind: "packet_flow",
params: {
stations: ["출발지", "중간1", "중간2", "도착지"], // 3~6개 권장
packet: "이동체 라벨", // 짧게 (4자 이내)
labels: ["하단라벨1", "하단라벨2", ...] // stations와 동일 길이
}
}
matrix_build:
sketch: {
kind: "matrix_build",
params: {
rows: ["행1", "행2", "행3"],
cols: ["열1", "열2", "열3"],
cells: [["1,1", "1,2", "1,3"], ["2,1", "2,2", "2,3"], ...],
highlightRow: 1, // 0-based, 강조할 행
highlight: [1, 2], // [row, col] 강조할 셀
pullRow: true, // highlightRow를 아래로 끌어내기
caption: "하단 설명"
}
}
before_after_contrast:
sketch: {
kind: "before_after_contrast",
params: {
left: { title: "제목 (Before)", note: "설명\n여러 줄" },
right: { title: "제목 (After)", note: "설명\n여러 줄" },
arrow: "→ 화살표 라벨"
}
}
xy_transform:
sketch: {
kind: "xy_transform",
params: {
points: [
{ x: 0.3, y: 0.25, label: "점A", near: false }, // x,y는 0~1 정규화
{ x: 0.7, y: 0.65, label: "점B", near: true } // near=true → teal 색상
],
pair: [0, 1] // 연결선을 그을 점 인덱스 쌍
}
}
zoom_hierarchy:
sketch: {
kind: "zoom_hierarchy",
params: {
levels: [
{ scope: "L0: 전체", blocks: [{ label: "Transformer", focus: true }] },
{ scope: "L1: Stack", blocks: [{ label: "Encoder" }, { label: "Decoder" }] },
{ scope: "L2: Layer", content: "Self-Attn\nFFN\nAdd&Norm" }
],
focus: 1, // 0-based, 강조할 level
breadcrumb: "Transformer > Encoder > Layer 1"
}
}
gate_mask:
sketch: {
kind: "gate_mask",
params: {
values: [0.9, 0.1, 0.7, 0.3], // 0~1 gate 값
labels: ["기억", "폐기", "유지", "약화"],
caption: "하단 설명"
}
}
formula_lock:
sketch: {
kind: "formula_lock",
params: {
stations: ["단계1", "단계2", "단계3"],
packet: "●",
formula: "f(x) = ..." // 하단 수식 (plain text; KaTeX는 formula_lock.formula에서)
}
}
sketch.kind가 _generic이고 params.kind가 패턴에 매칭되면 Special이 대신 렌더링:
// 예: sigmoid 곡선 전용
sketch: { kind: "_generic", params: { kind: "sigmoid curve" } }
// → VSquashCurve가 처리 (정규식: /sigmoid|tanh|relu.*curve/)
// 예: weighted sum 전용 (weights/vlabels/result 주입 — 없으면 기본값 fallback)
sketch: { kind: "_generic", params: { kind: "weighted sum", weights: [0.18, 0.04, 0.63, 0.15], vlabels: ["V_1","V_2","V_3","V_4"], result: "합산 → 새 representation" } }
// → VRowWeightedSum이 처리
// 예: parallel heads 전용
sketch: { kind: "_generic", params: { kind: "parallel 3 heads" } }
// → VParallelHeads가 처리
// 예: heatmap 전용
sketch: { kind: "_generic", params: { kind: "self-attention heatmap" } }
// → VHeatmapFocus가 처리
# 1. 대상 검색
grep "u07_s04" storyboard/data-unit07.js
# 2. 수정
str_replace_edit storyboard/data-unit07.js
# 3. 검증
ready_for_verification "AI-ML Animation Player.html"
# → 해당 unit/scene으로 이동하여 재생 확인
# 1. special-visualizers.jsx 또는 새 파일에 컴포넌트 작성
# 2. window export: window.SpecialVis에 추가 또는 새 window 객체
# 3. match() 함수에 패턴 추가 (special) 또는 KIND_MAP에 추가 (generic)
# 4. AI-ML Animation Player.html에 <script> 태그 추가
# 5. ready_for_verification
_ds/ml-animations-design-system-94795768-6cca-4336-8dca-6ececad5a6ab/var(--color-*), var(--font-sans), var(--font-mono), var(--radius-*), var(--shadow-*)window.MLAnimationsDesignSystem_947957에서 가져오기캔버스 내부(SVG)에서는 CSS var가 동작하지 않으므로 U01C 상수를 사용한다. 캔버스 외부(HTML)에서는 반드시 DS 토큰을 사용한다.
| 파일 | 현재 | 한계 |
|---|---|---|
| player-app.jsx | 296줄 | 유지 |
| generic-visualizers.jsx | 278줄 | 400줄 이하 |
| special-visualizers.jsx | 206줄 | 400줄 이하 |
| u01-shared.jsx | 142줄 | 유지 |
| u01-scenes-a.jsx | 194줄 | 유지 |
| 새 파일 | — | 500줄 이하 |
초과 시 Unit별로 분할: u05-gate-scenes.jsx, u07-attention-scenes.jsx 등.
| 이슈 | 원인 | 해결 |
|---|---|---|
| Babel scope 분리 | 각 <script type="text/babel">은 별도 scope | window.XXX로 export/import |
| styles 이름 충돌 | 전역 const styles = {} 충돌 | const playerStyles = {} 등 고유 이름 |
| SVG 내 CSS var | SVG attribute에서 CSS var 미작동 | U01C 상수 사용 |
| KaTeX 깨짐 | formula 문자열의 백슬래시 이스케이프 | \\frac 등 이중 이스케이프 |
| stagger 타이밍 이상 | overlap 파라미터 누락 | stag(t, i, n, 0.55) 기본값 확인 |
| localStorage 충돌 | 키 중복 | Unit 01 전용 aiml_anim_u01, 통합 aiml_anim_player |
| 새 script 로드 안 됨 | HTML에 태그 미추가 | checklist 확인 |