Communitygithub.com

motion-video-skill

Create animations and videos with code: intros, motion effects, subtitle animations, data visualization, UI demos, explanatory clips, and looping motion graphics. Outputs mp4/webm/gif/webp with machine verification to ensure the animation actually plays and no intermediate states are missing.

What is motion-video-skill?

This AI agent skill, named motion-video-skill, addresses a critical pain point for small language models (Flash/mini/turbo class) that can write CSS and Canvas code but frequently produce animations that 'look like they were written, but don't actually move'—skipped intermediate states, fake easing, missing key elements, or endings that stop midway. The skill transforms animation creation into a machine-verifiable engineering process through six key steps: (1) writing specifications before code, (2) making all visual states pure functions of time (seek(t)), (3) outsourcing all numerical calculations to scripts (no mental math), (4) using a deterministic renderer for frame-by-frame output, (5) machine-verifying both numerical trajectories and pixel-level behavior, and (6) only involving human visual review after structural verification passes. It supports platforms like Claude Code, Cursor, and Codex, and enforces strict quality rules: no skipping verification, no concurrent rendering (which wastes CPU), and no reducing validation density for speed. The skill provides a full toolkit of scripts (ease.mjs for curves/springs/stagger/contrast, verify.mjs for numerical checks, render.mjs for deterministic rendering, audit.mjs for pixel-level validation, rigcheck.mjs for skeletal geometry), comprehensive reference documents for code patterns, asset recipes, and failure modes, and a clear workflow from specification to delivery with machine-verified outputs plus contact sheets and audit conclusions.

Works with✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/IFIAnder/motion-video-skill/tree/HEAD

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

motion-video · Create Verifiable Animations with Code

This skill solves a very specific problem: small models (Flash / mini / turbo class) can write CSS and Canvas, but the animations they produce often "look like they were written, but don't actually move"—intermediate states are skipped, easing is fake, key elements appear at other times, and the ending stops halfway. The model itself can't tell because it has no eyes.

So the approach here is not "teach it to write better-looking code," but to turn animation into an engineering artifact that can be judged by machines:

  1. Specification first, code second — translate aesthetic judgment into numerical values in tables
  2. Every frame must be a pure function of t — structurally force it to write intermediate states
  3. Let scripts handle numbers — Bézier curves, springs, stagger offsets, contrast ratios — always use tools, no mental math
  4. Use a deterministic renderer for frame-by-frame output — same code, same t, always the same pixel
  5. Machine verification — numerical layer first, pixel layer second
  6. Only after the machine says "structure is fine" does a human look at composition

The gap is not bridged by a single magic phrase. It's bridged by these six things turning "can't think it through" into "discoverable and fixable." Where the gap comes from, what can be filled, and what cannot, see references/delta-vs-opus.md.


Quality First: Only Cut Wasted Overhead, Never Validation

This overrides the ironclad rules below. The goal of this skill is to produce Opus-grade pieces, not the cheapest ones. So the criterion for "should I do this?" is not whether it's fast, but whether it makes it better.

Only one type of overhead can be cut: repetitive, ineffective, or resource-contending overhead. Verification density, rendering quality, frame rate, motion blur—these are never cut.

Allowed to Cut (waste; cutting doesn't add or subtract quality)

WasteHow to Cut
Launching 4 Chrome instances contending for CPUSerial (faster than parallel in practice); render.mjs has a built-in single-instance lock
A 20KB spec document per chapterOne storyboard.md covering everything
Repeatedly calling render --still for single framesExtract 6–8 time points at once
Repeatedly re-analyzing the same assetAnalyze once, write results into storyboard
Writing each frame PNG to disk then reading it backAlready enabled optimizeForSpeed; for large projects use image2pipe (cinema-grade.md §9)
Verifying the same thing three times "to be safe"Two verifications (numerical + pixel) are sufficient; a third adds no information

Forbidden to Cut (cutting reduces quality)

Don't CutReason
Full audit, including processing each jump/flat individuallyLast checkpoint before delivery; skipping means no verification
verify --idempotentIf idempotency is broken, all subsequent verifications are unreliable
rigcheck (every time a character or pose changes)Bone misalignment can only be caught by numbers, not eyes
Motion blur on delivery versionThe difference between "with shutter" and "without" is a whole grade; it's not a nice-to-have
60fps for fast motionFast limbs will stutter at 30fps; this is a defect, not a trade-off
Extracting frames for review (multiple time points + local zoom)1 frame can't reveal mid-segment problems; at least 6 frames + 2–3× zoom on key parts
Re-running after a fix (rather than "it should be fine now")If you didn't re-run, you didn't fix it

One-Sentence Judgement

Cutting waiting time is okay; cutting opportunities to find errors is not.

A 70-minute render is unacceptable, not because "70 minutes is too long," but because 85% of that 70 minutes was 4 processes contending for CPU—waiting bought no quality. But spending 4× more rendering time for a 180° shutter buys a tangible improvement in visual feel—that spend is mandatory.

Correct Speed-Up Order

  1. First kill waste: concurrent → serial, merge duplicate startups, delete redundant documents, batch frame extraction
  2. Then optimize implementation: screenshot parameters, multi-tab in a single process, pipe to encoder without writing to disk
  3. Only then consider reducing verification — and this step should almost never happen; if it does, steps 1 and 2 weren't done properly

Ironclad Rules

Violating any of these makes the output non-verifiable; redo it.

  1. No code without specifications. Produce the shot table and timeline table first (STEP 1). Every element in the table must have three time intervals: enter / active / exit. Missing one means you haven't thought through when it exists. But when the user gives only a single sentence ("make something impressive" "show me what you can do" "go all out"), do not ask them follow-up questions — fill in the four slots (motif, meta-narrative, form, tier) yourself and write them into storyboard.md, then start working. See references/wish-mode.md.
  2. Every visual state is a pure function of seek(t). Forbid requestAnimationFrame, Date.now(), performance.now(), Math.random(), auto-increment counters, and self-running CSS @keyframes/transition. The same code at the same t must always render the same pixel.
  3. All numerical values must come from scripts; mental math is forbidden. Curve control points, spring parameters, stagger offsets, coordinate transformations, contrast ratios, CSS linear() sequences—everything must be produced by ease.mjs. Before any such number appears in your response, there must be a tool output. See below "Numbers go to tools, judgement stays with you."
  4. After any change, run verify and render + audit. FAIL is not deliverable. For each WARN, provide either "fix" or "this is by design because…" Do not skip verify and render the full piece—that's spending an extra 8 seconds just to get a vague "the animation didn't move" with no clue why.
  5. Delivery triad: final piece (mp4/webm/gif) + contact sheet (-sheet.png) + audit conclusion.
  6. If unsure about aesthetics, offer options, don't guess. Two color schemes, two rhythms—let the user choose. Better than delivering a mediocre version.
  7. Don't wait for assets; draw them yourself. The deliverable must not contain phrases like "suggest inserting an image," "place image here," or "wait for assets." The rendering pipeline only uses HTML/CSS/SVG/Canvas—all visual elements must be generated by code. When you need lighting/textures/graphics/icons, first check references/asset-recipes.md (20 categories of ready-to-use snippets that can be pasted directly: glow, sweep light, grain, glass card, neon, grid, abstract illustration, particles, wireframe sphere, charts, city silhouette, glitch, scan lines, icon set, light beam, depth of field, fabric texture, bokeh, pseudo-3D), and if not there, write the SVG path yourself. Forbidden to use emoji or Unicode symbols as graphics. Every frame must have at least three layers (background gradient/grid → main subject → atmospheric grain/bokeh), at least 2 custom visual elements, and at least 1 depth cue (shadow/gradient/overlay/grain). A frame with only one layer, only text and color blocks = fail.
  8. Prioritize existing assets; prohibit rebuilding. If the workspace already contains assembled skeletons/specs/components/design files, you must adopt the entire set without re-estimating a single value. Use rigcheck.mjs --ref to prove you used the original values. Incident record: a rebuild changed head.pos from [-6.8,-182.5] to [0,-248] (65.9px off), torso.pv from [160,160] to [160,291] (131px off), resulting in the head floating to the left of the body, and "visual inspection" didn't catch it—because the human eye reads it as "a character." Such errors must be caught with numbers.
  9. Asset whitelist. Only use assets explicitly listed in storyboard.md. Other files in the workspace (previous versions, unrelated character images, configs from other projects) must not be touched, even if they seem usable. When a new asset is needed, add a line to the storyboard explaining its purpose, then look for it.
  10. Don't lower verification standards, and don't render concurrently. audit reporting WARN is not "deliverable": hard jumps and flat frames must be eliminated or explained one by one, or redo. verify/rigcheck reporting FAIL means stop immediately. Only one render.mjs process at a time—the script has a single-instance lock; concurrency just wastes CPU. To parallelize, use multiple tabs in a single Chrome instance (see cinema-grade.md §9), not multiple processes.

Numbers Go to Tools, Judgement Stays with You

Models of this class (Flash / mini / turbo) share a common tendency: they'd rather estimate than call a tool. They think mental math is zero-cost and tools make you wait. This cost calculation is wrong. Let's break it down with numbers:

Mental MathRun ease.mjs
Direct time0 seconds~0.3–0.5 seconds
Probability of getting it right the first time30–40% (guess one Bézier control point wrong, and the entire curve changes character)100%
Cost of being wrongRender 90 frames ≈ 8 seconds + review, and you likely won't even see the error—it'll just "not feel smooth"0
Expected cost0 + 0.5 × (8 + 30) ≈ 19 seconds, with silent quality degradation0.5 seconds

The 0.5 seconds "saved" by mental math is the most expensive kind of saving. So this is a hard requirement, not a suggestion:

The following numbers must always come from tool output; never hand-write inferred values in your response:

What you needWhat to run
Properties of any Bézier (overshoot, plateau, sampling table)node scripts/ease.mjs cubic 0.22 1 0.36 1
Spring parameters (how much bounce, how long to settle, whether it stops mid-way)node scripts/ease.mjs spring 120 14 1 1.2
Compress a curve into CSS linear()node scripts/ease.mjs css-linear --preset silkIn
Stagger offsets for a group of elements, check if they exceed total durationnode scripts/ease.mjs stagger 7 2.4 0.5
Whether text contrast meets requirementsnode scripts/ease.mjs contrast "#0b0e14" "#6d8bbd"
Generate readable grayscale steps from a base colornode scripts/ease.mjs ramp "#0b0e14" 5

Self-Check Three Questions (ask yourself before delivery; if you can't answer, it's a violation):

  1. Can every curve parameter in my response be traced back to a command output?
  2. Did I write down an estimated number like "around 0.3" anywhere? Yes → delete it, run the command.
  3. When I say "this curve is very smooth," is it because I checked the ASCII plot and overshoot percentage from ease.mjs, or because I guessed?

It also wants to verify itself, so use the verifier to reassure it: verify.mjs reads real computed styles, not guesses. When it thinks "let me just confirm," the correct action is to run verify, not simulate in its head.


Environment

Zero npm dependencies. Requires Chrome or Edge (already on the machine) + ffmpeg + ffprobe.

ffprobe is only used by audit to verify resolution/duration. Running without it will not silently pass: if --width/--height/--duration is declared with ffprobe missing, it FAILs; if undeclared, it WARNS and prints "Resolution unknown (not verified)."

node scripts/render.mjs --check      # Print chrome / ffmpeg / node / WebSocket capabilities

If chrome shows NOT FOUND, specify with --chrome <path>, or install a browser first.

What to Do When There's No Image Understanding

Some models (e.g., text-only inference versions) cannot read_image. This chain's judgement is entirely numerical, not visual, so it can still be executed: ease (parameters) → verify (numerical trajectory + idempotency) → audit (pixel statistics; conclusions are numbers) → rigcheck (geometry assertions).

  • What's possible: structure, motion, geometry, readability
  • What's not possible: composition quality, color harmony, style consistency, visual clipping

The latter must be handled by someone who can see images or a visual model. But you can delay it to the end: use numbers to catch all detectable errors first, then present 6–8 frames (--still batch extract + --guides to overlay safe zones) to a human all at once.


Document Index (Read on Demand, Not All at Once)

FileWhen to Read
references/wish-mode.mdRead this first when the user gives only one sentence: four slots, chapter derivation, default value table, no interviews in wish mode
references/spec-and-prompt.mdWhen writing the spec table in STEP 1; when you need a reproducible prompt template; when dealing with models that "resist using tools"
references/code-patterns.mdWhen writing code in STEP 2: seek(t) paradigm, easing library, keys() keyframe table, spring(f,z), SVG/Canvas, 20 modern CSS effects
references/asset-recipes.mdFirst place to look when a frame lacks elements: glow, sweep light, grain, glass, neon, grid, illustration, particles, wireframe sphere, charts, icon sets, bokeh, pseudo-3D
references/rig-characters.mdWhen a character needs to move in the frame: parts + parent-child transform chain + pose table + how to map the 12 principles to fields
references/cinema-grade.mdBefore finalizing: motion blur, encoding parameters (anti-banding), vignette, HUD system, whip pan, frame rate selection, long-form organization, parallelization for speed
references/review-checklist.mdSTEP 5 human visual spot-check + pre-delivery checklist
references/failure-modes.mdWhen audit/verify errors occur, look up the symptom
references/delta-vs-opus.mdWhen you want to know "how far can this system really compensate?" or need to decide whether to switch models

First, Route: Wish Mode or Specification Mode

What the Input Looks LikeWhich Path to Take
Provides most of size/duration/content/style; or is responding to your follow-up questionsSpecification Mode: STEP 0 ask questions → STEP 1 spec table → …
One sentence + authorizing tone ("make something impressive" "show me what you can do" "like that kind of piece" "go all out")Wish Mode: Skip STEP 0, no follow-up questions allowed. First scan the workspace for motifs, fill in the four slots yourself, then proceed from STEP 1. See wish-mode.md

The criterion is simple: Did they leave things unclear meaning "wait for me to ask" or "you decide"? A tone like "you figure it out/go all out/show me" is the latter. Wish mode still requires writing storyboard.md — the difference is that you write the table, not ask for it.

Workflow

STEP 0 · Clarify Three Things (Specification Mode only; skip for Wish Mode)

  • Use case: Presentation intro / Social short video / Web hero animation / Explanatory clip / Data growth animation / GIF
  • Size and duration: See default values in table below, unless user specifies otherwise
  • Emotion: Stable (corporate/finance), Fast (tech/product launch), Soft (lifestyle/brand), Hard (gaming/sports)
Use CaseSizeDurationFrame RateFormat
Intro / Product Launch1920x10803–6s30mp4
Social Portrait1080x19205–15s30mp4
Web Hero / Banner1920x800 or 1440x6002–4s, loopable30webp / apng (for transparency); webm requires alpha verification first
Data Animation / Chart1920x10803–8s30mp4
Looping Motion Graphic800x800 / 1080x10801–3s, must loop seamlessly15–20gif / webp
Explanatory Clip1920x10802–5s per shot, assembled later30mp4

Exceeding 10 seconds or 3 shots: split into multiple scene files, render and verify each individually, then concatenate with ffmpeg. Don't let a single seek() handle 30 seconds—nobody will be able to debug it.

STEP 1 · Write Specifications (The Only Allowed Creative Freedom)

Produce storyboard.md, two things are mandatory:

① Shot Table

#TimeWhat the Frame Is DoingMain MotionEnd State
10.00–1.10sTitle lines float up character by charactery: +96→0, 75ms stagger per characterTitle in place
21.10–2.40sProgress bar fills in + three dots pop outscaleX: 0→1 (ease-in-out)Bar at edge, dots aligned

② Element Timeline Table (one row per element; this is the sole basis for code)

ElemententeractiveexitPropertyCurve
#title span0.15 + i*0.075till end—translateY 96→0, opacity 0→1easeOutCubic
#bar b0.300.30–2.40—scaleX 0→1easeInOutCubic
.dots i1.50 + i*0.15till end—scale 0→1easeOutBack

If you can't write this table, you haven't thought it through. Don't skip it and write code; you'll only end up with "first and last frames only."

The "Curve" column isn't just a name. After naming the curve, immediately run the tool once to pin down "what this curve actually does at your duration," then fill in the row with numerical details:

node scripts/ease.mjs cubic 0.33 1 0.68 1     # easeOutCubic → check plateau/overshoot
node scripts/ease.mjs stagger 6 2.4 0.7       # 6 title characters in 2.4s, 0.7s each → stagger and overflow

So the two rows in the table become:

ElemententeractiveexitPropertyCurve (Tool Confirmed)
#title span0.15 + i*0.075till end—translateY 96→0, opacity 0→1cubic-bezier(0.33,1,0.68,1), overshoot 0%, plateau after 0.75 → compress duration to under 0.7s
.dots i1.50 + i*0.15till end—scale 0→1spring(120,14) overshoot 7.1%, stabilizes in 0.67s → allocate 0.75s segment

Criteria for a good curve (the tool will tell you directly): overshoot ≤ 15%, no mid-segment plateau, spring stabilization time ≤ segment length. If any criterion is not met, change parameters and re-run, rather than "write it like this first and see how it renders."

Template and more patterns: references/spec-and-prompt.md

STEP 2 · Implementation

Start with a skeleton, don't write the entire scene at once. Skeleton = background + one element that can move + a working seek contract.

window.scene = {
  duration: 3.0, fps: 30, width: 1920, height: 1080, ready: true,
  seek(t) {
    // Each element: translate the row from the timeline table into 3 lines of code
    // k = curve(seg(t, enter, enter + duration)) → use k to interpolate property
  },
};

Must inline CSS and JS. Don't reference external CDNs (they will fail as if offline during rendering). Use system fonts ("Segoe UI", "Microsoft YaHei", Arial).

Available patterns (DOM/CSS/SVG/Canvas/GSAP/Web Animations, easing library, multi-segment orchestration, character-by-character text, path stroke, number scroll, mask transition, CSS linear(), filter/blend-mode/mask usage): references/code-patterns.md

When you need to draw something (lighting, textures, graphics, icons, charts, illustrations, pseudo-3D), first consult references/asset-recipes.md. It has 20 categories of zero-dependency snippets, all of which can be pasted directly into a single-file HTML and accept seek(t) driving. Don't build from scratch by intuition, and don't wait for assets.

STEP 3 · Numerical Verification (Check Numbers First, Don't Rush to Render)

This is the first link in the "verify more" chain: takes seconds, doesn't require encoding, and can pinpoint specific elements.

# See the actual trajectory of each element: who's moving, how far, where it's stuck
node scripts/verify.mjs --html scene.html --select "#bar b,#title span" --props transform,opacity --steps 31

# Verify that "the frame is determined solely by t" (same t called twice must give identical results)
node scripts/verify.mjs --html scene.html --select ".item" --idempotent

It will give you answers like:

  • ⚠ #b[0] · sx — Stopped mid-way 0–2s then resumed changing → interpolation was broken or omitted here
  • ℹ #card[0] · (all) — This element had no changes at all across the whole segment → selector didn't match / style not written into DOM
  • ✖ seek is not idempotent: 13 spots where same t returned different results → did seek use cumulative state

Proceed only after the numerical layer passes. Don't render the full piece when numbers are wrong—you'll only get a blurry "the animation doesn't seem to move" and have to reverse-engineer the cause from pixels. Let numbers speak first.

STEP 4 · Rendering + Pixel Verification

First, only render keyframes—fast and sufficient to judge composition:

node scripts/render.mjs --html scene.html --still 0.2,1.0,2.2 --out out/stills

Look at these images (use read_image yourself; don't skip). If composition is OK, render the full piece:

node scripts/render.mjs --html scene.html --out out/v.mp4 --fps 30 --sheet
node scripts/audit.mjs --video out/v.mp4 --fps 30 --duration 3 --width 1920 --height 1080

audit provides: pixel change curve, rhythm bar, and graded conclusions. Fix based on the conclusions, fix only the one named issue per round, then re-run audit immediately. Maximum 5 rounds; if still FAIL at round 5, go back to STEP 1 and change the specification (indicating the spec itself was wrong).

Common error lookup: references/failure-modes.md

STEP 5 · Human Visual Spot-Check

The machine only guarantees "structure is correct". These things require you to look at the images:

  • Is text overflowing / clipped / blending into the background?
  • Is the visual center of gravity where expected (rule of thirds / centered / center of mass of element group)?
  • Is contrast sufficient (gray text at 40% opacity on a dark background is invisible)?
  • Are entry/exit points clean (no elements starting to move outside the frame, or suddenly disappearing inside)?

Checklist: references/review-checklist.md

STEP 6 · Delivery

# Add one more render for the required format
node scripts/render.mjs --html scene.html --out out/v.webm --format webm
node scripts/render.mjs --html scene.html --out out/v.gif --format gif --fps 15

Deliver: final piece path, contact sheet, the verdict lines from audit, and storyboard.md. If audit has WARN but you judge it's by design, explicitly state which one and why.

Final version must be re-rendered with motion blur.

node scripts/render.mjs --html scene.html --out out/final.mp4 --fps 30 --motion-blur 180

This is the boundary between "looks smooth" and "looks like a screen recording." During iteration, you can temporarily disable it to save time, but the delivery version must have shutter—skipping it is quality degradation. After enabling, re-run audit to verify duration and frame count aren't changed. For fast motion (character actions, whip pans, particles), also use --fps 60.


Quick Command Reference

# Health check
node scripts/render.mjs --check

# Math (curves / springs / stagger / contrast) — mental math forbidden
node scripts/ease.mjs cubic 0.22 1 0.36 1
node scripts/ease.mjs spring 120 14 1 1.2
node scripts/ease.mjs css-linear --preset silkIn
node scripts/ease.mjs stagger 7 2.4 0.5
node scripts/ease.mjs contrast "#0b0e14" "#6d8bbd"
node scripts/ease.mjs ramp "#0b0e14" 5
node scripts/ease.mjs presets

# Skeletal geometry check (mandatory when characters are present; must use --ref to align with existing rig)
node scripts/rigcheck.mjs --rig rig/ander.rig.json
node scripts/rigcheck.mjs --rig lib/rig.json --ref rig/ander.rig.json
node scripts/rigcheck.mjs --rig rig.json --pose '{"head":{"rot":0.2}}' --assert

# SKILL.md frontmatter check (run after editing the skill to prevent "entire skill fails to load")
node scripts/lint-skill.mjs

# Numerical verification (seconds-level, can pinpoint elements)
node scripts/verify.mjs --html scene.html --select "#bar b" --props transform,opacity --steps 31
node scripts/verify.mjs --html scene.html --select ".item" --idempotent
node scripts/verify.mjs --html scene.html --all --json

# Keyframes (for visual review, fastest)
node scripts/render.mjs --html scene.html --still 0,0.5,1.5,2.9 --out out/stills

# Keyframes + composition guides (100px grid / 80px safe zone / rule of thirds / center cross)
node scripts/render.mjs --html scene.html --still 1.6 --guides --out out/guides

# Full piece + contact sheet
node scripts/render.mjs --html scene.html --out out/v.mp4 --fps 30 --sheet

# Final delivery version with motion blur (180° shutter = cinematic standard; 4× render time, use only for final)
node scripts/render.mjs --html scene.html --out out/final.mp4 --fps 30 --motion-blur 180

# 60fps only for fast motion (character actions, whip pans, particles)
node scripts/render.mjs --html scene.html --out out/final.mp4 --fps 60 --motion-blur 180

# 2x supersampling (sharper, 3–4× slower)
node scripts/render.mjs --html scene.html --out out/v.mp4 --scale 2

# Transparent background: webp / apng / png are reliable
node scripts/render.mjs --html scene.html --out out/v.webp --format webp --alpha
node scripts/render.mjs --html scene.html --out out/v.apng --format apng --alpha

# Webm alpha depends on ffmpeg build. Local tests show VP8/VP9 both degrade to alpha-less;
# script reads back the first frame to verify — if no transparent pixels, errors out and tells you which format to use instead
node scripts/render.mjs --html scene.html --out out/v.webm --format webm --alpha

# Looping motion graphic
node scripts/render.mjs --html scene.html --out out/v.gif --format gif --fps 15

# Pixel verification
node scripts/audit.mjs --video out/v.mp4 --fps 30 --duration 3 --width 1920 --height 1080
node scripts/audit.mjs --video out/v.gif --loop           # Loop seam check
node scripts/audit.mjs --video out/v.mp4 --json out/audit.json

render.mjs parameters: --html --out --fps --duration --width --height --scale --format(mp4|webm|gif|webp|apng|png) --still --sheet --alpha --file --keep-frames --chrome --check --quiet verify.mjs parameters: --html --select --props --steps --idempotent --all --json --tolerance audit.mjs parameters: --video --fps --duration --width --height --json --loop --verbose

Related Skills