javascript-animation
This skill should be used when the user asks to "draw every frame in JavaScript", "make an animation with no image assets", "animate this in code / on a canvas", "make a hand-drawn style animated video", "turn this into a short animated film", "make an animated explainer drawn in code", "make an animated story / picture book of my photos", or "make a zero-asset animation like the Opus 5.5 ones". Produces a single self-contained HTML file whose frames are computed on an HTML canvas (seekable, deterministic), renders it to MP4, and self-checks the result. Pairs with the soundtrack skill for code-synthesized music. NOT for charts from data (use chart-animation), pure text motion (use kinetic-typography), or 3D/WebGL (use threejs-animation / shader-glsl from webgl-animation-skills).
1How It Works & Mechanism
The core premise is "condensing an entire short film into a self-contained HTML file": every pixel on the canvas is computed via code without images, font files, or external libraries; a headless browser then seeks frame-by-frame to render an MP4—determinism comes from "each frame being a pure function of the frame number."
Make short animated films where every pixel is computed: no images, no fonts to load, no libraries. One HTML file holds the whole piece; a headless browser seeks it frame by frame into an MP4.
The default workflow deliberately avoids "pausing to ask questions": it infers the format, duration, and aspect ratio to execute the full pipeline directly, explaining what can be adjusted only upon delivery; it only prompts the user first if there is no topic at all.
Run straight through. Pick sensible defaults and mention alternatives at delivery. Ask *before* starting only if there is no subject at all (e.g. "make an animation", nothing else).
Visuals and sound are driven by the same dataset: beat grids, storyboard sheets, and staging are pre-defined in the page under BPM/SECTIONS/SHOTS/EVENTS, read simultaneously by scenes, cameras, and soundtracks, making audio-visual synchronization structural.
These go into the page as `BPM`, `SECTIONS`, `SHOTS`, `EVENTS`: the scenes, camera and soundtrack all read them, so picture and sound line up by construction.
Drawing disciplines target typical failure modes of hand-drawn animation: pure frame-number functions (prohibiting rAF state/CSS animations/cumulative physics), line boil re-seeding every 4 frames rather than shaking every frame, constructing bodies from ellipses and rounded rectangles, framing angles that maximize pose readability, and explicitly enforcing draw order.
- **Boil, don't jitter.** Change the wobble seed every 4 frames (`BOIL`), not every frame. Every frame reads as noise; ~7.5 changes a second reads as hand-drawn.
Self-checks combine automated script gates with two visual review passes: first running asset-audit / layout-check / sync-check, followed by storyboard conformance and a director pass (scoring a 2fps contact sheet).
Scripts first: `asset-audit` CLEAN, `layout-check` CLEAN, `sync-check` passing (a remaining STARTLE is acceptable only on a deliberate stinger you can name). Then the two passes by eye below.
Text collisions are determined programmatically rather than by eye: starter's claim()/placeFree() register the bounding boxes of every text frame and key object, and layout-check inspects sampled frames to report overlaps and timestamps.
- **Text never collides.** Claim every text box and key object; `layout-check` must print CLEAN. A caption overlapping a monitor edge or a face looks careless, and eyes on a contact sheet miss it.
Real human photos are strictly limited to "local viewing": subjects are drawn into stylized characters using feature extraction, with an explicit rule that photos are never uploaded, embedded, or included in any deliverables—this is the skill's sole privacy clause and a hard constraint.
Photos stay local: never upload them or put them in anything published.
2Core Capabilities
3External Dependencies
| Kind | Dependency / Endpoint |
|---|---|
| package | playwright-core(脚本用它启动 Chrome 逐帧 seek 页面);另需 Chrome 或 `npx playwright install chromium` |
| cli | ffmpeg(jpeg 管道编码 H.264、拼接触表、把离线渲染的 WAV 混进 MP4) |
| cli | 兄弟 skill 的同步检查:node ../soundtrack/scripts/sync-check.mjs(跨 skill 调用其 ffmpeg/ffprobe 分析) |
| network | 无执行期外部端点:交付页面的契约就是「不加载任何东西」,由 asset-audit 的静态规则 + 实跑网络记录双向证明 |
| package | Web Audio(浏览器内置的 OfflineAudioContext,用于离线渲染页面内合成的配乐;由 soundtrack 模板提供) |
4Recommended Governance & Evidence
- scripts/: Local script or command execution: node scripts/render.mjs piece.html sheet.jpg --sheet 1 / --stills 90,300,61
- 网络端点: External network access declaration: The skill's scripts make no outbound business requests: Playwright accesses local file:// pages, and ffmpeg/ffprobe only read local files
- 本地工作目录: File I/O and artifact output: <out>.mp4 (full video), <out>-<frame>.jpg (still frames), contact sheet image named via out parameter (render.mjs)
5Second-Pass Verification
- [ok] 脚本是否真的无网络外发(蓝档的关键) — 三个 .mjs 逐个读过:render.mjs 只 spawn ffmpeg 与 playwright 打开 file:// 页面;asset-audit.mjs 的 live check 只监听请求并打印;layout-check.mjs 只 evaluate window.draw/LAYOUT。skill 目录内无 fetch/http 调用点。
- [ok] 是否有凭证/环境变量读取 — `process.env|API_KEY|TOKEN|SECRET|Authorization` 在本仓命中仅一处:skills/soundtrack/references/ai-music.md 的 $OPENROUTER_API_KEY(属 soundtrack)。本 skill 内零命中,credential_reads 为空成立。
- [ok] 是否有更高的触发条件被漏掉(红档特征) — `sandbox|automationcontrolled|no-verify|stealth|bypass|dangerously` 全仓零命中;浏览器以默认方式启动(`chromium.launch({ channel: 'chrome' })`,无 stealth 补丁、无关 TLS/沙箱开关)。
- [ok] SKILL.md 引用的脚本与模板是否都真实存在(有无示意命令) — 四条命令逐条核对到真实文件:scripts/render.mjs、scripts/asset-audit.mjs、scripts/layout-check.mjs、templates/starter.html 均在 skill 目录内;跨 skill 的 ../soundtrack/scripts/sync-check.mjs 在同仓兄弟 skill 目录内存在。无空引用。
- [ok] 「zero-asset」是否是可执行契约而非口号 — asset-audit.mjs 有 13 条静态规则(含 data: URI、@font-face、动态 import、base64 大块)与一次实跑网络记录,命中即 exit 1;qc.md 把 'asset-audit CLEAN' 列为交付前第一道门禁。契约可执行。
- [ok] 「照片只本地」的隐私声明是否两处一致且可定位 — SKILL.md 触发条件表内 'Photos stay local: never upload them or put them in anything published.';character.md 第 6 步 'Photos are for looking at locally. Never upload them, embed them, or put them in anything published.' 两处同口径,无矛盾。
- [ok] 「每帧是帧号的纯函数」是否与 starter/render 实现相符 — starter.html 顶部注释写死契约(不许 Math.random/Date,用 rng(seed)/hash2,任意帧可单独渲染),render.mjs 只调用 window.draw(f) 取 dataURL、不做时间推进;两侧一致,声明属实。
6Verdict & Scenarios
cf575d5199b6a4ed…