Communitygithub.com

yazzang-homelab/motion-studio

Ship every format from one timeline, then package it: layout() reframing (never crop), render all formats, per-format checks, deliver.mjs, a brand skill, a service offer. Use for 'export 9:16 and 16:9', 'deliver', 'make a skill'.

Qu'est-ce que motion-studio ?

motion-studio is a Claude Code agent skill that ship every format from one timeline, then package it: layout() reframing (never crop), render all formats, per-format checks, deliver.mjs, a brand skill, a service offer. Use for 'export 9:16 and 16:9', 'deliver', 'make a skill'.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/yazzang-homelab/motion-studio/tree/HEAD/skills/ship-formats

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Ship formats, a skill and a service (course step 12)

Three moves turn a good clip into a repeatable business: export every format from one timeline, package the pipeline as a skill so the next video is a sentence, and, if the user wants, sell it. Work in the film project root. Arguments, shown once:

Files of this skill live in ${CLAUDE_SKILL_DIR} (templates in assets/).

1. Reframe per format, never crop

Scenes draw against the layout L (ctx.L in the factory, c.L in a draw), so one timeline renders 9:16, 1:1, 16:9 (and 4:5) with type and UI placed for each shape.

FormatSizeLabel
9x161080x1920Reels, TikTok, Shorts
1x11080x1080X / feed
16x91920x1080YouTube, site
4x51080x1350feed portrait
ToolUse
L.usize unit: 1 at a 1080 px short side; 120 * L.u reads the same size in every format
L.Sthe safe rect {x, y, w, h} from studio.json safe[fmt] (platform UI margins)
L.pos(ax, ay)a point inside the safe rect, ax, ay from 0 to 1
L.pick({ '9x16': a, '1x1': b, '16x9': c, portrait, landscape, square, default })per-format values: exact key, then orientation, then default
L.split('auto', ratio, gap)two regions: stacked in portrait and square, side by side in landscape
L.cols(n, gap) / L.rows(n, gap)columns or rows inside the safe rect
fitFontSize(g, text, maxW, (s) => font(s, family, weight), lo, hi, { tracking, step })the largest size that fits a width (lib/layout.js + lib/draw.js); step: 16 for bitmap fonts
wrapText(g, text, maxW, font(size, family), { maxLines, ellipsis })lines for multi-line copy, never cutting inside a Hangul syllable (lib/draw.js)
draw(g, lt, c) {
  const { L, brand } = c;
  const { a, b } = L.split('auto', L.pick({ landscape: 0.45, default: 0.4 }), 32 * L.u);   // title | product
  const size = Math.min(L.pick({ '9x16': 170, '1x1': 140, '16x9': 150 }) * L.u,
                        fitFontSize(g, 'MAKE IT MOVE.', a.w, (s) => font(s, brand.fonts.display, 400)));
  kinetic(g, 'MAKE IT MOVE.', a.x, a.y + a.h * 0.6, lt, { size, family: brand.fonts.display, weight: 400, color: brand.colors.fg, spring: 'default' });
  drawProduct(g, b, lt, c);                                         // the product UI fills region b in every format
}

Rules: no literal pixels; key content inside L.S; the same moment happens at the same time in every format (only placement and size change); a format that looks like a crop of another is a P1 in the critique. Tune safe margins in studio.json, not in code. Preview each format with npm run preview (F cycles formats, G shows the safe guides).

2. Review every format

The critique loop runs on the primary format; before the final, run at least one round per other format, each with its own evidence (about 115 s of tool time per live pass and 85 s per --video pass, measured on a 12 to 20 s film):

node tools/stills.mjs --format 1x1
node tools/render.mjs --format 16x9 --draft --sub 1 --scale 0.5     # about 20 s; then: npm run sfx, node tools/mix.mjs --format 16x9
node tools/critique.mjs --format 16x9                                 # live film -> out/review/16x9/
node tools/critique.mjs --format 16x9 --video out/16x9/final.mp4      # mixed render -> out/review/16x9/video/

Log each as its own round (## Round <n> — 16x9 — ...). The gate reads the last round, so finish with a passing round.

3. Render, mix, deliver

These take minutes: the 12 s demo renders in about 4 min for three formats at 60 fps (about 20 s per second of film), mix takes 40 to 80 s and deliver about 40 s. Start npm run build, render:final, mix and deliver with the Bash tool's run_in_background option and a log, poll the log (tail -n 3), read the result after the exit line, and never wait in the foreground for more than about 2 minutes; add --chunk 10 to a final of three formats or more than about 30 s. The motion-reel runbook ("Long commands") has the pattern.

StepCommandOutput
everything, in ordernpm run buildscore (if missing), sfx, final render of all formats, mix, deliver
final render (gated)npm run render:finalout/<fmt>/silent.mp4, poster.png, render.json for every format in studio.json formats
mix and muxnpm run mixout/score.wav, out/<fmt>/final.mp4, out/final.mp4 (primary), out/poster.png
checks and packagenpm run deliverout/deliver/<slug>_<fmt>.mp4 + poster + every page of the contact sheet (<slug>_<fmt>_contact.png, then _contact-2.png ...), out/deliver/manifest.json, docs/production.json

deliver.mjs checks, per format, out/<fmt>/final.mp4 against the CURRENT studio.json: frame count (duration x fps), the format's size, fps and exact duration (a cut rendered before an edit of duration or fps fails as stale render; a draft or scaled render fails too), integrated loudness and true peak against audio.lufs +- audio.lufsTolerance and audio.truePeak; silent spans (a span of 1 s or more below -50 dB fails as silence, for example silence 2.2 s at 00:02.1, unless it starts in the first 0.3 s, starts in the last second or lies inside a critique.allowSilence interval); file size against deliver.maxBytes (default x: 536,870,912 bytes = X's 512 MB upload limit; add other platforms' limits there); poster present; the loop seam for loop films; and the critique gate. It warns (no failure) when film sources are newer than the render. It exits 1 and names every failed check; an unreadable final.mp4 fails too. With --json a failure prints one line { ok: false, error, failed: [...] }, also when the run cannot start (an invalid studio.json, an unknown format, a bad flag). --format 1x1 re-delivers only that format and keeps the others' delivered files and manifest entries (carriedOver); a failing gate withdraws every delivered file. docs/production.json records title, formats, durations, loudness, rounds, last scores, the effort level (from CLAUDE_EFFORT when set) and tool versions.

Failed checkFix
frames or duration off, or stale renderre-run npm run render:final; never deliver a partial render
loudness or true peak/motion-studio:sound-design (mix report table)
silencea stem ends early or a bed plays under nothing: regenerate it (npm run score, sfx, voice), npm run mix, deliver again
size over the limitnode tools/render.mjs --format <f> --final --crf 18 then npm run mix again
poster missingset studio.json poster (seconds) or drop --no-poster
gate/motion-studio:critique-loop

Report to the user: every file in out/deliver/, the per-format numbers from the manifest, the scores of the last round, and what you would improve next.

4. Package the pipeline as the user's own skill

So the next video is one sentence (/acme-reel launch video for dark mode, 20 s, vertical):

  1. Pick the name: <brand>-reel, lowercase letters, digits and hyphens.
  2. Create .claude/skills/<brand>-reel/SKILL.md in the brand's repository (project scope, shared through git) or in ~/.claude/skills/<brand>-reel/SKILL.md (personal). Start from ${CLAUDE_SKILL_DIR}/assets/brand-skill-template.md (assets/brand-skill-template.md).
  3. Fill it from this project, not from memory: studio.json brand values, docs/style_guide.md, the approved docs/shotlist.md as the story-beat pattern, and the lessons in docs/review_log.md (what the critic kept flagging becomes a hard rule). Copy the logo into .claude/skills/<brand>-reel/assets/.
  4. Keep the description under about 230 characters with concrete trigger phrases, the body under about 200 lines.
  5. Test it in a fresh session with a real request and check that it asks only for what changes per video.
  6. To share beyond one repository, publish it as a plugin (a repository with .claude-plugin/plugin.json and skills/<name>/SKILL.md), the way buildwithhanif/claude-animation-skill and this plugin are published.

5. Turn it into a service (optional)

${CLAUDE_SKILL_DIR}/assets/service-offer.md (assets/service-offer.md) is a fill-in offer: what is included (music, a mascot in any style, product features, an offer at the end, any language, up to 3 edits), formats, turnaround, what the client provides, rights and disclosure, and pricing. Price anchor: Tony Dinh reported paying about $1,000+ for a similar launch video a year before making one with Opus 5.5 in under 30 minutes (https://x.com/tdinh_me/status/2103703135902740699). Help the user fill it; do not publish or send it for them.

Next: start the next film with /motion-studio:motion-reel, or the user's new /<brand>-reel skill.

Individual skills in this repo

This repo contains 11 individual skills — each has its own dedicated page.

yazzang-homelab/motion-studio

Make Claude watch its own frames: critique.mjs evidence, harsh 7-axis scores, timestamped P0/P1/P2 problems in docs/review_log.md, fix the 3 worst, re-check, pass the gate. Use for 'critique it', 'review the frames', 'is it good'.

yazzang-homelab/motion-studio

Plan long-form or overnight films: director's brief (PLAN FIRST or GO, budget, definition of done), ANIMATION_GUIDE + STORYBOARD, chapter subagents, gates, chunked render. Use for 'overnight film', 'music video', '2-minute film'.

yazzang-homelab/motion-studio

Make a launch video, app or product reel, explainer or motion ad from code, end to end: brand assets from a URL, beat-grid shot list, synced sound, critique loop, all formats. Default for new video requests; not footage edits.

yazzang-homelab/motion-studio

Brand-asset stage of a product film (motion-reel runs the whole film): capture real screenshots, logo, colors and fonts from a URL, never redraw UI; brand story beats; voice + mascot. Use to grab or fix a brand's assets.

yazzang-homelab/motion-studio

Turn a reference (a frame, a video or an image library) into docs/style_guide.md and a beat-grid shot list: measure cuts, palette and pace, take grammar not content, wait for OK. Use when the user supplies or names a look.

yazzang-homelab/motion-studio

Build, debug or speed up the seek(t) render engine: render contract, canvas vs page capture, workers/subframes/chunks, determinism fixes, Remotion or HyperFrames handoff. Use for 'render is slow', 'frames differ', 'use Remotion'.

yazzang-homelab/motion-studio

The viral one-line 'showreel for a résumé' prompt: why it works (anatomy), the credited quote, and seeded variants that avoid look-alike reels; runs it as an engine test. Use for that prompt, its variations, or a showreel.

yazzang-homelab/motion-studio

Score a film to its beat grid: measure a track (beats.mjs) or synthesize one (score.mjs), cue SFX from the film, add voice, mix to -14 LUFS / -1 dBTP at exact length. Use for 'add music', 'sync to the beat', 'SFX', 'loudness'.

yazzang-homelab/motion-studio

Make motion feel physical with closed-form springs from lib/motion.js: presets, springFromFeel, track() for multi-target values, loopTrack loops, indicators, text swaps. Use for 'feels linear', 'add bounce', 'easing to springs'.

yazzang-homelab/motion-studio

Scaffold a motion-studio film project (seek(t) renderer, springs, beat grid, synthesized sound, critique tools), install it, set up the browser, run doctor. Use for 'set up the motion studio', 'new film project', 'init studio'.

yazzang-homelab/motion-studio

Write a six-part XML state spec for a one-shape UI morph film (inputs, direction, structure, build, gotchas, start): states on downbeats, cursor-driven changes, seamless loop, approval gate. Use for 'UI morph', 'state list'.

Skills associés