Skip to content
Back to skills

Ship Formats

ASecurity

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'.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 7, 2026
designgobashnodespringawsgit

Works with

  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add yazzang-homelab/motion-studio --skill ship-formats --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ship Formats?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ship Formats
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yazzang-homelab-ship-formats/badge)](https://www.skillsdirectory.com/skills/yazzang-homelab-ship-formats)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ship-formats
description: "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'."
argument-hint: "[formats | skill <brand> | offer]"
---

# 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:

<request>
$ARGUMENTS
</request>

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.

| Format | Size | Label |
|---|---|---|
| `9x16` | 1080x1920 | Reels, TikTok, Shorts |
| `1x1` | 1080x1080 | X / feed |
| `16x9` | 1920x1080 | YouTube, site |
| `4x5` | 1080x1350 | feed portrait |

| Tool | Use |
|---|---|
| `L.u` | size unit: 1 at a 1080 px short side; `120 * L.u` reads the same size in every format |
| `L.S` | the 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) |

```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.

| Step | Command | Output |
|---|---|---|
| everything, in order | `npm run build` | score (if missing), sfx, final render of all formats, mix, deliver |
| final render (gated) | `npm run render:final` | `out/<fmt>/silent.mp4`, `poster.png`, `render.json` for every format in `studio.json` `formats` |
| mix and mux | `npm run mix` | `out/score.wav`, `out/<fmt>/final.mp4`, `out/final.mp4` (primary), `out/poster.png` |
| checks and package | `npm run deliver` | `out/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 check | Fix |
|---|---|
| frames or duration off, or `stale render` | re-run `npm run render:final`; never deliver a partial render |
| loudness or true peak | `/motion-studio:sound-design` (mix report table) |
| `silence` | a stem ends early or a bed plays under nothing: regenerate it (`npm run score`, `sfx`, `voice`), `npm run mix`, deliver again |
| size over the limit | `node tools/render.mjs --format <f> --final --crf 18` then `npm run mix` again |
| poster missing | set `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](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](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.

Files in this skill

  • SKILL.md9.1 KB
  • assets/brand-skill-template.md4.4 KB
  • assets/service-offer.md2.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.