@officexapp/vidfarm-devcli 0.21.28 → 0.21.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/editor-capabilities/SKILL.md +26 -0
- package/.agents/skills/vidfarm/SKILL.md +53 -2
- package/.agents/skills/vidfarm/recipes/bulk-scripting-with-a-regime.md +65 -0
- package/.agents/skills/vidfarm/recipes/cutout-graphics-for-explainers.md +78 -7
- package/.agents/skills/vidfarm/recipes/local-edit-render-approve.md +2 -2
- package/.agents/skills/vidfarm/recipes/retheme-template.md +1 -1
- package/.agents/skills/vidfarm/references/automation-and-local-dev.md +66 -5
- package/.agents/skills/vidfarm/references/editor-workflows.md +94 -1
- package/.agents/skills/vidfarm/references/hooks-and-virality.md +237 -0
- package/.agents/skills/vidfarm/references/onboarding.md +1 -1
- package/.agents/skills/vidfarm/regimes/README.md +77 -0
- package/.agents/skills/vidfarm/regimes/explainer.QA_REGIME.md +82 -0
- package/.agents/skills/vidfarm/regimes/hooks.QA_REGIME.md +117 -0
- package/.agents/skills/vidfarm/regimes/product-demo.QA_REGIME.md +92 -0
- package/.agents/skills/vidfarm/regimes/short-form.QA_REGIME.md +163 -0
- package/.agents/skills/vidfarm/regimes/ugc-testimonial.QA_REGIME.md +82 -0
- package/SKILL.director.md +599 -19
- package/SKILL.md +18 -2
- package/demo/dist/app.js +103 -103
- package/dist/src/cli.js +925 -18
- package/dist/src/devcli/doctor.js +13 -0
- package/dist/src/devcli/handoff.js +162 -0
- package/dist/src/devcli/hyperframes-cli.js +12 -0
- package/dist/src/devcli/interaction-mode.js +154 -0
- package/dist/src/devcli/qa-check.js +173 -0
- package/dist/src/devcli/qa-regime.js +396 -0
- package/dist/src/devcli/sticker-pack.js +396 -0
- package/dist/src/devcli/storyboard.js +243 -0
- package/dist/src/devcli/studio-brand.js +196 -0
- package/package.json +8 -1
package/SKILL.director.md
CHANGED
|
@@ -125,6 +125,30 @@ Then **ask whether to remember the choice**, and save it where it will actually
|
|
|
125
125
|
|
|
126
126
|
If the user hasn't picked yet and you're about to spend, name the cheaper path and the cost, and ask. `vidfarm cost-mode` with no argument prints the current mode + an explanation of each for you to relay.
|
|
127
127
|
|
|
128
|
+
## Ask the SECOND question too: interactive or autonomous?
|
|
129
|
+
|
|
130
|
+
Cost mode answers *how much money may I spend*. It does not answer *how much of the user's own hands may I use* — and that second axis moves quality more than the first. **Ask both.** They are independent: every cost mode (`minimize`, `hybrid`, `rich-ai`, `pure-videogen`) runs in either interaction mode.
|
|
131
|
+
|
|
132
|
+
- **interactive** — the user is willing to do a little manual work at fixed checkpoints, and the video gets better for it. Two checkpoints cover nearly everything: **(1) images** — you write a prompt, they run it in a *free* frontier web generator (meta.ai / ChatGPT / Gemini / a Hugging Face Space) and hand the file back; **(2) raw clips** — you hand over search keywords, they search TikTok/YouTube, download a few with a free online downloader, and point you at the folder.
|
|
133
|
+
- **autonomous** — you finish end-to-end with zero steps from them: source clips yourself (browser control → `raws scan` → public raws), generate within the budget, or do without.
|
|
134
|
+
|
|
135
|
+
**Why interactive usually wins on quality:** the free tiers of the frontier web image models are typically *better* than what an API-key budget buys per image, and a human eye picks better footage than any keyword scan. In `minimize` the gap is not incremental — it's the difference between **no custom art at all** and **a full sticker pack for $0**.
|
|
136
|
+
|
|
137
|
+
**Ask it like this, once, near the start:** *"Two quick settings. First, how much should I spend per video (minimize / hybrid / rich-ai / pure-videogen)? Second — do you want to be in the loop? In interactive mode I hand you an image prompt to run in a free tool (you drop the PNG back) and a keyword list for sourcing clips; it costs you a few minutes and usually looks better. Or I can run fully autonomous."* Then persist it: **`vidfarm mode interactive|autonomous`** (→ `~/.vidfarm/interaction-mode.json`), alongside `vidfarm cost-mode <choice>`. Same memory etiquette as cost mode — offer to record it in `CLAUDE.md`/`AGENTS.md`, ask first. `vidfarm mode` with no argument prints the current pair + the explainer to relay. Per-run override: `--interactive` / `--autonomous`.
|
|
138
|
+
|
|
139
|
+
**The pairings, and what each means in practice:**
|
|
140
|
+
|
|
141
|
+
| | interactive | autonomous |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| **minimize** | **the sweet spot.** $0 *with* custom art: free web image-gen → `vidfarm sticker-pack`, plus hand-sourced clips. | $0 from stock + existing raws + HTML motion only. No custom art. |
|
|
144
|
+
| **hybrid** | Pay for the one or two shots only AI can do; the user's hands on the hero art. | Pay where it clearly wins, skip the rest. |
|
|
145
|
+
| **rich-ai** | Buy the reusable greenscreen cast, but let the user approve/redo the hero sheet by hand. | Budget does the work; you never stop. |
|
|
146
|
+
| **pure-videogen** | Human approval at script + storyboard gates (strongly recommended — a bad storyboard is expensive). | Fully unattended; quote the number first. |
|
|
147
|
+
|
|
148
|
+
**In interactive mode, MANUAL IMAGE WORK DEFAULTS TO STICKER PACKS.** Never ask for one graphic per round trip — each hand-off costs the user a context switch and costs you tokens re-reading a file. Ask for **one sheet holding every graphic**, then split it locally for $0. `vidfarm handoff image --theme "<what>" --items "a,b,c"` mints the whole brief (prompt + steps + the free tools + the follow-up command); `--single` when you really do want one subject. When the file comes back: `vidfarm sticker-pack <sheet> --items "a,b,c"`.
|
|
149
|
+
|
|
150
|
+
**Raw-clip sourcing has a ladder — hand it to the human only at the bottom rung.** In order: (1) **your own browser control**, if you have it — drive the search and download yourself; (2) **Vidfarm cloud** — `vidfarm clipper <url>` / `vidfarm raws scan <url> --cloud` resolves and mines the video for you; (3) the **free public raws catalog** — `vidfarm public-raws --category <shelf>`; (4) **the user**, when you're fully local/keyless or when human taste matters. That last rung is `vidfarm handoff raws --keywords "villa construction,pouring concrete" --platforms tiktok,youtube --purpose "<what the clips are for>"` — it prints the keywords, tells them to google a downloader (a *search*, not a link that rots), and names the import command for when the folder is ready (`vidfarm clipper ./downloads/<file>.mp4`, or `vidfarm raws scan` to mine a long one).
|
|
151
|
+
|
|
128
152
|
## Default stance
|
|
129
153
|
|
|
130
154
|
- Treat the Trackpad Editor as the primary surface. Reach for templates and forks before primitives.
|
|
@@ -134,6 +158,17 @@ If the user hasn't picked yet and you're about to spend, name the cheaper path a
|
|
|
134
158
|
- For heavy edits, read the grounding artifacts before acting: `video-context.json`, `editor-harness.json`, and local `.harness/*` bundles when present.
|
|
135
159
|
- For agentic rewrites, think in the three axes: scenes, audio, text. Decide whether each axis is a SWAP or a REPLACE.
|
|
136
160
|
|
|
161
|
+
## Storyboard first — the plan pass is a core part of the format now
|
|
162
|
+
|
|
163
|
+
**A composition project carries its plan with it.** `STORYBOARD.md` at the project root holds the ordered frames — one key moment each, with `duration`, `status`, an optional `src` pointing at that frame's sub-composition, a one-line `scene` description, and its `voiceover` line — and the optional `SCRIPT.md` beside it holds the full narration. This is a first-class part of the composition format, not a scratch file: **the Vidfarm editor renders it.** The Storyboard/Preview toggle at the top of the editor shows the plan as a contact sheet with a comment box per frame, tracks each frame through `outline → built → animated`, and hands the director's per-frame comments back to you.
|
|
164
|
+
|
|
165
|
+
That makes the storyboard the **cheapest place to be wrong**, and the natural interactive checkpoint: a plan is free to redo, a rendered scene is not.
|
|
166
|
+
|
|
167
|
+
- **Draft it before you build:** `vidfarm storyboard ./work --init --frames "Hook,Problem,Reveal,Proof,CTA"` (frames accept `Title|one-line scene`). Then fill in each frame's `scene` and `voiceover`.
|
|
168
|
+
- **Read it back any time:** `vidfarm storyboard ./work` prints the frames with their status marks and the current stage; `--json` gives an agent the parsed manifest.
|
|
169
|
+
- **Advance `status` as you build** — `outline` (planned) → `built` (a real sub-composition exists at `src:`) → `animated` (motion done). The editor's review stages are derived from exactly those counts, so keeping them honest is what makes the director's view meaningful.
|
|
170
|
+
- **In interactive mode, gate on it.** Show the storyboard, let the director comment or approve in the editor, *then* build. In `pure-videogen` this is not optional — an unapproved storyboard is the single most expensive mistake available (see the pure-videogen ladder above, and `vidfarm sequence` for the AI shot-sheet pipeline, which is a different thing: `sequence` generates storyboard *images*, `storyboard` maintains the plan *file* the editor reads).
|
|
171
|
+
|
|
137
172
|
## Say "create a vidfarm template that…" — reusable by default (the house habit)
|
|
138
173
|
|
|
139
174
|
**Every video a director makes is really a *reusable template*, and Vidfarm's whole leverage comes from treating it that way.** So actively steer the user's language toward it: when someone asks to "make a video" / "create a TikTok" / "cut me an ad," gently reframe and echo it back as **"create a *vidfarm template* that …"** — then build it as a template, not a one-off export. This is a real, common, first-class workflow, not a marketing tic; name it as such.
|
|
@@ -169,8 +204,18 @@ Present both harnesses to the director, recommend (A) unless they've asked for p
|
|
|
169
204
|
|
|
170
205
|
**Explainer/cutout videos — the transparent-sticker workflow.** For explainers (a subject "on stage" while labels, arrows, and props pop in around it), the cheap workhorse is a **transparent cutout sticker**: `vidfarm cutout --generate "<subject>"` AI-generates the graphic on a chroma plate, keys it out, **and trims the canvas down to the subject's true min width/height** — one free local ffmpeg step, no mostly-empty PNG to fight with — then `vidfarm place` + `vidfarm keyframes` scale/position and animate it (zoom, grow, shake, drift). It's the same "generate a reusable element once, then reuse it" thrift as the cheap harness, tuned for stickers. Full guided harness: recipe `recipes/cutout-graphics-for-explainers.md`; placement + zoom/grow/shake/move motion recipes: `references/editor-workflows.md` (“Cutout graphics for explainers”).
|
|
171
206
|
|
|
207
|
+
**"Make me a sticker pack" = ONE greenscreen sheet of many items, then masked apart — and `vidfarm sticker-pack` is that whole loop.** A sticker pack is never one graphic; it's a *set* (props, icons, reactions, characters, backdrops) that must share one art style. Generating them one at a time is both expensive (N image jobs) and inconsistent (N independent styles), so the move is the opposite: **generate a single image holding every item, laid out on a flat greenscreen plate, then cut each item out locally for $0.** `vidfarm sticker-pack --generate "<theme>" --items "a,b,c"` does all of it — one billed image job for the whole set, then a free local key, an **automatic** alpha-segmentation that finds each item (no hand-measured `--crop` rects), a per-item trim to its true bounding box, and a `stickers.json` manifest. Already have a greenscreen sheet? `vidfarm sticker-pack ./sheet.png` cuts it up for **$0**. Use `--dry-run` to eyeball the detected boxes first; `--gap` merges/splits items that came out joined or broken; `vidfarm mask <sheet> --crop …` is the manual fallback for one stubborn item.
|
|
208
|
+
|
|
209
|
+
- **Stickers are not necessarily small.** A sticker is *any* transparent element you place and animate — an icon, a mascot, a prop, a character, and equally **a full-width landscape, skyline, or backdrop** that fills the frame. `sticker-pack` filters speckle only; it has no maximum item size. Ask for the big pieces in the same sheet as the small ones.
|
|
210
|
+
- **Stickers are usually animated, not pasted.** Once placed, animate each one with `vidfarm keyframes` presets (`pop-in`, `float`, `shake`, `grow`, `slide-in-left`, `drift`) — that's HTML/CSS canvas motion, deterministic, free, and identical in preview and render. Layer moves up (pop-in, then idle float) for real life. See `references/editor-workflows.md` → "Cutout graphics for explainers".
|
|
211
|
+
- **A sticker can carry its OWN motion too.** A *moving* subject has no single bounding box, so it isn't a PNG: key the clip with `vidfarm remove-greenscreen <video>` → transparent WebM (browser/editor-playable, the right choice on a composition).
|
|
212
|
+
- **The plate must be a color the ART DOESN'T USE — green is a default, not a law.** A chroma key removes exactly ONE color, so a green plate under green artwork (leaves, frogs, plants, money, an "eco" icon set) keys holes straight through the stickers. `sticker-pack`/`cutout` handle this for you in two ways, and you should still say it out loud when it matters: **(a)** when generating, they read the subject and move the plate off any hue it mentions — green → magenta (`#FF00FF`) → blue (`#0047BB`) → black → white — and print which plate they picked and why; **(b)** when splitting a sheet you already have, they **detect the plate from the sheet's four corners**, so a red/purple/blue sheet handed back from a web generator just works. Pin it yourself with `--key-color "#FF00FF"` / `--preset magenta` (that disables the auto-pick), or `--no-auto-key` to force plain green. If a pack's palette collides with *every* standard plate, split it into two sheets on two different plates rather than fighting the key.
|
|
213
|
+
- **Transparent GIF is supported, for GIF-only surfaces.** `vidfarm sticker-pack … --output-format gif` (stills) and `vidfarm remove-greenscreen <video> --gif` (animated) emit transparent GIFs. GIF alpha is **1-bit**, so edges go hard — fine for chat/forum/Notion sticker surfaces, worse than PNG/WebP/WebM for compositing on a timeline. Prefer PNG/WebP/WebM unless the destination only eats GIF.
|
|
214
|
+
|
|
172
215
|
**Explainer house style — the defaults to build with unless told otherwise.** **White background / light mode** (plain white stage, no gradients, no dark mode, no photo backdrop), **kinetic word-by-word captions** in dark ink on the light stage (`vidfarm captions generate --style word-pop --color "#111111" --active-color "#7C3AED" --background-style plain` — skip outlines/shadows, they're only needed over busy footage), and **female TTS narration** (`vidfarm tts --voice coral` on OpenAI — `nova` for energy, `sage` for calm; `Kore`/`Leda` on Gemini; any ElevenLabs voice via `vidfarm voices`). **Keep it clean and simple** — one idea on screen at a time, two or three cutouts per beat, one accent color, one font, lots of white space; remove before you add. **Illustrations default to simplicity**: flat vector, simple shapes, minimal detail, 2–3 flat colors, no baked-in text — simple art keys cleanly, trims tight, and stays on-style across the whole cast. State the defaults once so the director can override any of them. Full detail: recipe `recipes/cutout-graphics-for-explainers.md` (“House style — the explainer defaults”).
|
|
173
216
|
|
|
217
|
+
**Landscape footage in a fullscreen vertical explainer — use the blurred plate, never bars.** When an explainer is built on **real filmed footage** and the source is 16:9 (or 4:3) on a 9:16 canvas, do not `contain` it (hard black letterbox bars read as an unfinished export) and do not blindly `cover` it (a wide shot loses its left and right thirds). Duplicate the clip: a full-canvas `cover` copy behind, heavily **gaussian-blurred and faded dark**, plus the sharp copy centered as a hero band — optionally zoomed ~1.3× — with its **top and bottom edges feathered** into the blur. Same clip, same timecode, so it reads as one continuous image with a shallow-depth-of-field plane, fullscreen edge to edge, nothing cropped, and clean dark space for the header and captions. Bake it once with ffmpeg into a single 1080×1920 file (free, local) and place it as one ordinary full-canvas layer — layer blur is not an editor property, so the pre-bake is the path that works in the editor, `serve`, and cloud render alike. Copy-paste ffmpeg + HTML recipes, tuning table, and the failure modes: `references/editor-workflows.md` (“The blurred plate — landscape footage, fullscreen, on a vertical canvas”).
|
|
218
|
+
|
|
174
219
|
**Cost-saving move — mask illustrations OUT of a source image the director already has.** (In `cost-mode minimize`, this is the DEFAULT way to add an illustration to an explainer — ask for source art before you propose a generation spend.) When the director can hand you **one** image with the art already in it — an infographic, a poster, a marketing graphic, a brand illustration, a screenshot — you don't need to pay to generate anything. `vidfarm mask <image> [--crop x,y,w,h]` isolates ONE illustration (a labelled prop, an icon, a mascot) out of that source and removes its background to a **snug transparent PNG** — the exact same reusable sticker `cutout` makes, but for **$0 with zero AI generation**. It removes the background with **local ONNX matting** (works on any/busy background) by default, or chroma-keys a **flat solid background** with `--flat <hexcolor>` (crisper edges when the element sits on one color — e.g. the cream paper behind an infographic's icons). Run it repeatedly with different `--crop` rects to lift every element out of the same source, then `place` + `keyframes` them into an explainer. **Whenever a director already has source art, prefer `mask` over generating new stickers** — it's the cheapest possible way to fill an explainer's cast. Same recipe: `recipes/cutout-graphics-for-explainers.md` (“Mask from an image you already have”).
|
|
175
220
|
|
|
176
221
|
**Free manual image-gen — custom art in `minimize` mode for $0, on someone else's tokens.** `mask` only works when the art already exists. When the video needs a **bespoke** graphic and cost mode is `minimize` (or the user said "no spend"), the answer is **not** "we can't" and **not** a silent billed `generate` — it's a **manual handoff**: you write the prompt, the user runs it in a **free** image generator, they hand the PNG back.
|
|
@@ -178,7 +223,7 @@ Present both harnesses to the director, recommend (A) unless they've asked for p
|
|
|
178
223
|
- **Offer it once, up front, and let them set the default.** The first time custom art is needed in a cost-saving session, ask plainly: *"Want me to default to free manual image generation — I write the prompt, you paste it into a free tool and drop the image back — or would you rather I spend credits on `vidfarm generate` for these?"* Honor the answer for the rest of the session (and offer to remember it alongside the cost mode). Don't re-ask per image once they've chosen; just say "here's the next prompt."
|
|
179
224
|
- **Free generators to name:** <https://meta.ai> (no cost, generous), free-tier **ChatGPT** (image gen included), or a free image **Hugging Face Space** (<https://huggingface.co/spaces> — search "flux" or "sdxl"). Any of them; the user picks whichever they're already signed into.
|
|
180
225
|
- **Ask for ONE sheet, not N images — this is the token-efficiency trick.** Each round trip costs the user a manual step and costs you tokens re-reading a new file. So request **one image containing every graphic the scene needs**, laid out as a grid of separate objects on a **flat pure-green background** (`#00FF00`), well spaced, none overlapping or touching an edge. One generation, one hand-back, then you lift each element out locally for free.
|
|
181
|
-
- **Then cut it up locally, for $0:** `vidfarm
|
|
226
|
+
- **Then cut it up locally, for $0 — in ONE command:** `vidfarm sticker-pack ./sheet.png --items "<object one>,<object two>,…" --out-dir ./stickers` keys the flat green out, finds every item in the sheet automatically, and writes one snug transparent sticker per item (named from `--items`) plus a manifest — exactly like `cutout --generate` produces, with **zero AI billing**. `place` + `keyframes` them as usual. Add `--dry-run` first to check the detected boxes. Fallbacks: if two items came out joined, lower `--gap`; if the generator ignored the green-plate instruction, `vidfarm mask ./sheet.png --crop x,y,w,h` (local ONNX matting) grabs elements one at a time off any background.
|
|
182
227
|
- **Prompt template — give the user this, filled in:**
|
|
183
228
|
|
|
184
229
|
> A flat vector illustration sheet on a solid pure green background (#00FF00), no gradient, no shadow, no text, no labels. Arranged in a 3×2 grid with generous even spacing, each object fully separated and not touching any other object or the image edge: (1) `<object one>`, (2) `<object two>`, (3) `<object three>`, … Simple flat shapes, 2–3 flat colors, minimal detail, consistent style and line weight across all objects, front-facing, centered in its own cell. Square image, high resolution.
|
|
@@ -224,7 +269,11 @@ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock
|
|
|
224
269
|
- **Determine the surface before claiming capabilities.** The web chat has only its declared tools and REST routes. It cannot execute arbitrary JavaScript/Python, open a shell, create a local repository script, or use the user's filesystem. Never tell a web-chat user that you ran code or wrote a script unless a dedicated declared tool actually did so. A desktop coding agent has a real shell and filesystem and MAY write/run scripts, perform arbitrary local computations over paginated API results, create reports/CSVs/JSON, edit composition files, and orchestrate long devcli workflows within the user's authorization.
|
|
225
270
|
- **The web AI chat can do all three paintbrushes** — clip raws, author HTML/hyperframe motion, and generate AI media — and it drives edits directly on the live timeline. Keep small-to-medium jobs here: text/caption swaps, a scene or two replaced, single generations, captions, approve/schedule. Just do them.
|
|
226
271
|
- **No HTML slop — a video is not a web page.** Compositions are authored in HTML, so the #1 tell of an AI-edited video is landing-page furniture: gradient CTA "buttons" ("Sign Up for a Free Trial →"), rows of benefit chips/badges ("✓ No Credit Card Needed"), frosted/bordered cards holding a gradient headline + a URL, feature grids, bullet lists, web-default type (Inter/Roboto/Arial at weight 400-600). None of that exists in a real TikTok/Reel — **nothing in a video is clickable**. If you are typing `btn` / `badge` / `chip` / `card` / `rounded-full` / `shadow-lg` / `backdrop-blur` / `bg-gradient-to-r`, stop and rewrite it as timed text on the footage. Arrows, scribble/underline marks, italics, ALL-CAPS, single-word color pops, emoji, transparent cut-out stickers, and mock social UI (iMessage bubbles, comment cards) are all *fine* — they're native to the platform.
|
|
227
|
-
- **
|
|
272
|
+
- **Every video gets the four charges — hook, loop, payoff, bait — and you write them BEFORE you touch the timeline.** This is the largest quality delta in the product and it costs nothing: most agent-made videos fail on structure, not polish, because the timeline is the fun part so it gets built first and the words get retrofitted. Invert it: (1) write the **hook line as text** — a complete clause, subject + verb, no jargon, naming a **situation** ("I've quit six businesses"), never a label ("anonymity") — and put it on screen at `start:0`; (2) name the **curiosity loop** and the timestamp it closes at, *inside this video* (if you can't state the timestamp, there is no loop, and the withheld answer must be one the viewer **can't guess**); (3) name the **payoff** — shown, not summarized, landing before the final beat; (4) write the **bait** — one ask in the final beat and in the post caption. Then build. Chunk 1 is read before any audio (muted autoplay is the default), so the text hook does more work than the spoken one. Full harness — the three gates, banned openers, loop mechanics, compliance, and diagnosis-by-charge — in `references/hooks-and-virality.md`; the checkable form is `vidfarm regime show hooks`.
|
|
273
|
+
- **The first frame IS the thumbnail — compose it on purpose.** Frame 0 is a single frame of ~30 in the first second, but it's the poster every feed, share link, and paused player freezes on, so **more people see that one frame than watch the video**. It must never be black, empty, mid-fade, or caught mid-animation: put a real visual at `start:0`, have the hook words already on screen at t=0, and never hang a `fade-black`/`fade-white`/`flash` *entrance* on the **first** clip (junction transitions between later clips are fine — this rule is only about the opening). Verify it, don't assume: devcli `vidfarm stills ./work --at 0` renders that exact frame, and `vidfarm qa` flags a blank or fading open.
|
|
274
|
+
- **Ask early: one-time video, or bulk?** "Make me a video about X" and "I need to post daily / give me 20 hook variants" are different jobs, and directors often don't know the second one has a name. Ask once, up front: *"One video, or should we set this up as a repeatable batch?"* Bulk = **scripting mode** (a pinned base fork + a loop that varies ONE thing per variant; a public-raws shelf is the cheapest source of the N), and every batch gets a **`QA_REGIME.md`** — because a loop of fifty videos has no human looking at every frame, and the regime is what replaces those eyes. Don't silently ship a one-off when they asked for volume, or drag someone into a harness when they wanted one clip.
|
|
275
|
+
- **`QA_REGIME.md` is the director's own quality contract, and it's a first-class artifact.** `vidfarm qa`'s built-ins are universal (slop, fonts, the thumbnail frame); a regime is what makes *this* format good — audience, hook shape, banned vocabulary, pacing, compliance line. It lives next to the work, they own it, it stacks: `vidfarm regime init short-form --out ./work/QA_REGIME.md` (bundled bases: `short-form`, `hooks`, `ugc-testimonial`, `explainer`, `product-demo` — each a starting point to **edit**, never a house style), then `vidfarm qa ./work --regime hooks --regime ./brand/HOUSE.md`, and any user file anywhere is valid. Its `checks:` front matter is machine-settled; its `- [ ]` checklist comes back as **review items you answer honestly in your report** — never claim a video passed the half the CLI can't judge. When a batch teaches you something, **write it back into the regime**: that's the artifact that compounds. Details in `references/automation-and-local-dev.md` ("Scripting mode"), format in `regimes/README.md`.
|
|
276
|
+
- **On devcli, QA every video you produce: `vidfarm qa ./work`.** Free, instant, local-only — it blocklists exactly the slop above plus first-frame/thumbnail and font-regime/safe-zone drift, and prints a concrete fix per finding. **Feedback, not a gate**: it exits 0 even on findings, never runs automatically, and is a blocklist (unusual/stylized compositions pass untouched), so there's no reason not to run it before every publish. `--json` for scripted batches, `--strict` only if you want a CI failure. **Web-chat copilot: this command does not exist for you** (devcli-only, no REST twin) — apply the standard by hand, and when handing a heavy job to a local coding agent, tell them to run `vidfarm qa`.
|
|
228
277
|
- **Every production adheres to the TikTok-native caption standard.** On-screen text lives inside the readable safe zone (**~8%–85%** of a 9:16 frame — never pinned to the top/bottom edges the phone UI clips), uses the composition's bold font regime (**Montserrat** default / TikTok Sans, weight **700–900**, ~36–64px on a 1080-wide frame), and uses **exactly one of four valid backgrounds**: outline/stroke (`background_style:"outline"`, the default), plain + shadow (`"plain"`), an active-word highlight pill (`set_captions` `spotlight`/`karaoke` — the only legitimate pill), or a tight-hugging solid band (`"highlight-solid"`, radius ≤8px, no border/shadow/gradient/blur). Decomposed forks often inherit the source's edge-pinned caption in an off-regime font — fix it, don't inherit it. Local devcli renders auto-normalize position + font family only (never the slop), so author it correctly. Full rules in `references/editor-workflows.md` ("Social-native visual standard" + "TikTok-native caption standard").
|
|
229
278
|
- **Where the web chat struggles: complex, long, multi-step transformations.** A full multi-scene re-theme, an iterative render-critique-iterate loop, heavy scripted or batch work, or anything needing a real filesystem and many sequential tool calls will hit context limits, turn/timeout ceilings, and the web editor's constraints (CSS/declarative motion only — JS animation adapters are stripped on save). Don't grind a big transformation one layer at a time in a chat turn and stall.
|
|
230
279
|
- **Practical workaround — hand the heavy job to local devcli.** When a task is genuinely large or long-running, **proactively recommend the director run it locally with an AI coding agent** (Claude Code / OpenAI Codex / any capable agent): `vidfarm pull <forkId>` writes the composition + the `.harness/` grounding bundle to disk, the agent edits with the full devcli verb set and JS animation adapters, renders free with `vidfarm serve`, and `vidfarm publish` pushes it back. This is the **best-quality (B) harness's** natural home (adversarial grading with a coding agent). Frame it as "this is a big rebuild — you'll get a better, faster result running it locally with a coding agent; here's how," not as a dead end.
|
|
@@ -241,6 +290,8 @@ Read only the relevant reference file for the current task.
|
|
|
241
290
|
- REST automation, `vidfarm` command surface, local serve loop, skill packs: `references/automation-and-local-dev.md`
|
|
242
291
|
- Getting-started interviews, **consultations**, strategy docs, onboarding flow: `references/onboarding.md`
|
|
243
292
|
- Primitive routes such as TTS, STT, music, overlays, background removal, product placement: `references/primitives.md`
|
|
293
|
+
- **Hooks, retention, and why a video travels — the four charges: `references/hooks-and-virality.md`.** Read it before writing ANY hook, caption script, or re-theme, and before a hook-variant batch. It is the craft harness; the rest of the skill is mechanics.
|
|
294
|
+
- Bulk / N-variant production: recipe `recipes/bulk-scripting-with-a-regime.md`; the `QA_REGIME.md` format and the bundled bases: `regimes/README.md` (+ the regime files beside it, readable as-is)
|
|
244
295
|
- Complete REST API map and raw-HTTP conventions: `references/rest-api.md`. Load it only when the user asks for REST, an endpoint/schema, direct HTTP integration, or exhaustive API coverage. For the entire specification, follow its domain links and load every listed reference; do not preload them into ordinary director conversations.
|
|
245
296
|
|
|
246
297
|
## HyperFrames Skills — Load on Demand
|
|
@@ -971,13 +1022,79 @@ A clip/image whose **native aspect differs from the canvas** — a 16:9 landscap
|
|
|
971
1022
|
- **`object_fit:"cover"`** (default) fills + crops — right for most social footage **when paired with a subject-aware `object_position`**.
|
|
972
1023
|
- **`object_position`** aims WHERE the cover-crop lands: a keyword (`left`, `right`, `top`, `bottom`, `"top left"`, …) or a **percentage pair** (`"30% 50%"` = 30% from the left, 50% down). Landscape clip with the subject on the left → `"left"`/`"25% 50%"`; tall screenshot with key text up top → `"top"`.
|
|
973
1024
|
- **`object_fit:"contain"`** shows the WHOLE media with black letterbox/pillarbox bars — use ONLY when nothing may be cropped (an infographic, a whole screenshot, a chart, a logo); a 16:9 source full-canvas on a 9:16 frame reads unfinished in `contain`.
|
|
974
|
-
- **Blurred
|
|
1025
|
+
- **Blurred plate** — the default answer for landscape footage on a vertical canvas. Full section below; reach for it instead of `contain` whenever the whole frame matters.
|
|
975
1026
|
- Never `object_fit:"fill"` (stretch/distort) unless intentional. You usually know a clip's aspect from how you sourced it (a hunted raw's `--aspect`, a generated clip's `--aspect-ratio`); if you truly need exact dims, `vidfarm probe` / `GET /videos/probe`. When you REPLACE a full-canvas scene, keep it full canvas + `cover` + a focal `object_position` rather than shrinking; when you GENERATE, request the canvas aspect so no crop is needed.
|
|
976
1027
|
|
|
977
1028
|
Two surfaces:
|
|
978
1029
|
- **Editor web/serve copilot** — `editor_action` `action_type=set_layer_media` with `object_fit` and `object_position` (both also seedable on `add_layer`/`generate_layer`). `editor_context.layers[]` reports each clip's current `object_fit`/`object_position` so you can read before re-cropping.
|
|
979
1030
|
- **Desktop agents (devcli)** — `vidfarm place ./work --src <url> --object-fit cover --object-position "25% 50%"` (also on `--replace <layer_key>`).
|
|
980
1031
|
|
|
1032
|
+
### The blurred plate — landscape footage, fullscreen, on a vertical canvas
|
|
1033
|
+
|
|
1034
|
+
**This is the house treatment for real filmed footage in a fullscreen explainer.** The director hands you 16:9 (or 4:3, or 960×540) footage and wants a 9:16 video where the picture reads *fullscreen* — edge to edge, no bars, no dead space — but where nothing important gets cropped away. Neither default fit gets you there: `cover` throws away the left and right thirds of a wide shot (the subject ends up half out of frame), and `contain` leaves hard black letterbox bars that read as "unfinished export" the instant it hits a feed.
|
|
1035
|
+
|
|
1036
|
+
The fix is the **blurred plate**: the *same* clip twice, one behind the other.
|
|
1037
|
+
|
|
1038
|
+
1. **Backdrop** — a duplicate of the clip, full canvas, `cover`, so it's blown up ~3.5× and fills the whole 1080×1920. Heavy **gaussian blur** (`sigma ≈ 60` at 1080 wide), plus a **fade to dark** (brightness ≈ −0.14, saturation ≈ 0.9) so it recedes and never competes with type. Because it's the same frame at the same timecode, the colors, motion, and lighting are continuous with the sharp copy — the eye reads one deep image, not a picture pasted on wallpaper.
|
|
1039
|
+
2. **Hero band** — the sharp copy, centered, at (or slightly above) native aspect. Optionally **zoom it ~1.2–1.4×** past canvas width so the sharp band is taller and the blur does less work; the small side-crop that costs you is almost always cheaper than the vertical dead space it buys back.
|
|
1040
|
+
3. **Feathered seam** — fade the hero band's top and bottom edges out over ~100px instead of butting them against the backdrop. **This is the step that makes or breaks it.** A hard edge is just a letterbox with a fancy background; a feathered edge makes the sharp band read as the in-focus plane of a single continuous shot, like shallow depth of field.
|
|
1041
|
+
|
|
1042
|
+
The result: a fullscreen vertical frame, no bars, the entire landscape composition intact, and generous dark space at top and bottom for a header bar and captions. Reference render: `drafts/real-filming-demos/videos/fruitpoint-explainer/renders/video.mp4`.
|
|
1043
|
+
|
|
1044
|
+
**Bake it once with ffmpeg (recommended).** One 1080×1920 file per clip, produced locally and free, that then behaves like any ordinary full-canvas layer — no dual-track bookkeeping, no double video decode at render, and it works identically in the Trackpad Editor, `vidfarm serve`, and cloud render. Keep the baked plates beside the originals (e.g. `assets/full/` next to `assets/clips/`) so you can re-bake with different numbers later.
|
|
1045
|
+
|
|
1046
|
+
```bash
|
|
1047
|
+
# landscape clip → 1080x1920 blurred plate. Tune: sigma (blur), brightness (fade),
|
|
1048
|
+
# 1404 (= 1080 * 1.3 hero zoom; use 1080 for native), 110 (feather height in px).
|
|
1049
|
+
ffmpeg -y -i clip.mp4 -filter_complex "\
|
|
1050
|
+
[0:v]split=2[bg][fg];\
|
|
1051
|
+
[bg]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,gblur=sigma=60,eq=brightness=-0.14:saturation=0.90[bgb];\
|
|
1052
|
+
[fg]scale=1404:-2,crop=1080:in_h:(in_w-1080)/2:0,format=rgba,geq=r='r(X,Y)':g='g(X,Y)':b='b(X,Y)':a='255*min(1,min(Y,H-1-Y)/110)'[fgs];\
|
|
1053
|
+
[bgb][fgs]overlay=(W-w)/2:(H-h)/2:format=auto,format=yuv420p[v]" \
|
|
1054
|
+
-map "[v]" -map 0:a? -c:v libx264 -crf 18 -preset veryfast -c:a copy clip-plate.mp4
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
For a **still image** the same shape applies — drop `split`/`overlay` timing concerns and swap `-i clip.mp4` for the image with `-loop 1 -t <seconds>`, or just do it in HTML (below), since a still costs nothing to duplicate.
|
|
1058
|
+
|
|
1059
|
+
For **16:9 canvas fed portrait footage** (a phone clip on a wide frame), it's the identical trick rotated: blurred `cover` backdrop, sharp `contain`-width hero band, feathered left/right edges — swap the feather expression to `min(X,W-1-X)`.
|
|
1060
|
+
|
|
1061
|
+
**Or do it in the composition HTML** when you're hand-authoring and want the sharp band to animate independently (a slow push-in on the hero while the backdrop sits still):
|
|
1062
|
+
|
|
1063
|
+
```html
|
|
1064
|
+
<div class="plate">
|
|
1065
|
+
<video class="clip backdrop" src="assets/clip.mp4" muted playsinline data-start="0" data-duration="4"></video>
|
|
1066
|
+
<div class="plate-scrim"></div>
|
|
1067
|
+
<video class="clip hero" src="assets/clip.mp4" muted playsinline data-start="0" data-duration="4"></video>
|
|
1068
|
+
</div>
|
|
1069
|
+
<style>
|
|
1070
|
+
.plate { position: absolute; inset: 0; overflow: hidden; }
|
|
1071
|
+
.backdrop { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
|
|
1072
|
+
filter: blur(60px) brightness(0.86) saturate(0.9); transform: scale(1.1); }
|
|
1073
|
+
.plate-scrim { position: absolute; inset: 0; background: rgba(8,14,6,.18); }
|
|
1074
|
+
.hero { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%) scale(1.3);
|
|
1075
|
+
width: 100%; height: auto; object-fit: contain;
|
|
1076
|
+
-webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 100px,
|
|
1077
|
+
#000 calc(100% - 100px), transparent 100%);
|
|
1078
|
+
mask-image: linear-gradient(to bottom, transparent 0, #000 100px,
|
|
1079
|
+
#000 calc(100% - 100px), transparent 100%); }
|
|
1080
|
+
</style>
|
|
1081
|
+
```
|
|
1082
|
+
|
|
1083
|
+
Both `<video>` elements carry `class="clip"` with the **same `src`, `data-start`, and `data-duration`**, so framework-owned playback keeps them frame-locked and the render stays seek-safe. `transform: scale(1.1)` on the backdrop hides the blur's soft outer edge (a gaussian blur samples past the element bounds and leaves a pale rim otherwise). Mute the backdrop copy — never both.
|
|
1084
|
+
|
|
1085
|
+
**Numbers that work, and what to move:**
|
|
1086
|
+
|
|
1087
|
+
| Knob | Default | Move it when |
|
|
1088
|
+
|---|---|---|
|
|
1089
|
+
| Blur `sigma` / `blur()` | `60` (at 1080 wide) | Under ~40 the backdrop stays legible and fights the hero; over ~90 it flattens to mush. Scale proportionally on other canvas widths. |
|
|
1090
|
+
| Backdrop fade | `brightness −0.14`, `saturation 0.90` | Push darker (−0.20) only if captions sit **on** the blur; a scrim under the type is usually the better fix. Too dark reads as a black bar with texture — the exact thing you were avoiding. |
|
|
1091
|
+
| Hero zoom | `1.3×` | `1.0` when the framing is tight and the sides carry the subject; up to `1.4` for loose wide shots where more picture beats more edge. |
|
|
1092
|
+
| Feather | `~100px` | Bigger (150px) on soft/organic footage; smaller (60px) on graphic footage with strong horizontals that a long fade turns to smear. |
|
|
1093
|
+
|
|
1094
|
+
**Do not** vignette the backdrop, don't hard-edge the seam, don't run the backdrop unblurred-but-dark (you get a visible duplicate ghost), and don't reach for a solid color or a gradient behind the band — a flat backdrop is what makes a repurposed landscape clip look repurposed.
|
|
1095
|
+
|
|
1096
|
+
**Editor-surface caveat:** layer-level blur is not an `editor_action` property, so the two-layer version can't be assembled from the editor alone — **bake the plate with ffmpeg and drop it in as one full-canvas `cover` layer**. That's the path for `set_layer_media` / `vidfarm place` work, and it's why the pre-bake is the recommended default rather than the HTML variant.
|
|
1097
|
+
|
|
981
1098
|
### The opening frame is the post's thumbnail
|
|
982
1099
|
|
|
983
1100
|
**The composition's first frame (t=0) is the still that represents the whole video before anyone presses play** — it's the poster on the approved-post share page, the `/discover` card, the autoplay-off feed preview, and the file/scrubber thumbnail. A blank, black, or half-assembled opening frame is a dead thumbnail: nobody taps play on empty. Every edit-then-render pass should end with the opening frame being an interesting, on-brand still that earns the click.
|
|
@@ -1006,6 +1123,27 @@ Three surfaces:
|
|
|
1006
1123
|
- **Editor UI (human)** — the Inspector's **Animate (Ken Burns)** dropdown on any selected image.
|
|
1007
1124
|
- **Desktop agents (devcli)** — `vidfarm place ./work --src <img> --kind image --ken-burns zoom-in [--ken-burns-intensity 0.3]`, or fused with generation: `vidfarm generate image --prompt "…" --place ./work --ken-burns pan-left`.
|
|
1008
1125
|
|
|
1126
|
+
### The first frame is the thumbnail — compose t=0 on purpose
|
|
1127
|
+
|
|
1128
|
+
**Frame 0 is one frame of ~30 in the first second, and it does more work than the other 29 combined.** Every feed card, share link, embed, DM preview, and paused player freezes on it, and platforms use it as the default poster unless a cover is picked by hand. So it is seen by everyone who scrolls past — including the majority who never press play. Treat it as a still ad for the video, not as "the moment before the video starts."
|
|
1129
|
+
|
|
1130
|
+
**The failure mode is silent.** A timeline that reads perfectly in the editor (where you scrub to an interesting moment) can open on black, because:
|
|
1131
|
+
|
|
1132
|
+
- the opening clip starts at `0.4s` instead of `0`, leaving a black gap at the head;
|
|
1133
|
+
- the first clip carries a `fade-black` / `fade-white` / `flash` **entrance** — the intro fades *up from* a flat frame, so the poster is a solid black or white rectangle;
|
|
1134
|
+
- the hook line is timed to the first spoken word (`start: 0.6`), so the poster frame is footage with no promise on it;
|
|
1135
|
+
- a decomposed fork inherited the source's slate/countdown/blank head.
|
|
1136
|
+
|
|
1137
|
+
**Rules for every production:**
|
|
1138
|
+
|
|
1139
|
+
- **Something real is on screen at `start:0`.** No gap, no black leader. `vidfarm retime <dir> --layer <key> --start 0` pulls the opening clip to the head.
|
|
1140
|
+
- **No entrance transition on the FIRST clip.** `vidfarm transitions set <dir> --layer <key> --in none`. Junction transitions between later clips are unaffected — this is only about the opening. (`vidfarm transitions apply --intro fade-black` deliberately opens from black; only use it when the piece is not being posted to a feed.)
|
|
1141
|
+
- **The hook words are already up at t=0** unless you're deliberately opening on a clean face/product shot. The strongest short-form thumbnails state the promise in text.
|
|
1142
|
+
- **Nothing is mid-animation.** A zoom/slide/blur entrance means the poster catches the frame offset, scaled, or blurred. Author the opening as its resting state and animate *after* frame 0.
|
|
1143
|
+
- **Verify the actual pixels, never the intent:** `vidfarm stills ./work --at 0` renders exactly that frame to a PNG — look at it before you render. `vidfarm qa` flags the structural causes (`thumbnail-blank-open`, `thumbnail-fade-in`, `thumbnail-no-hook-text`), but the still is the ground truth.
|
|
1144
|
+
|
|
1145
|
+
Web copilot: same standard, applied by hand — check the opening layer's `start`, clear any entrance transition on it, and start the hook caption at 0.
|
|
1146
|
+
|
|
1009
1147
|
### Social-native visual standard — no HTML slop, ever
|
|
1010
1148
|
|
|
1011
1149
|
Compositions are authored in HTML, so the single most common way an AI-edited video betrays itself is by **looking like a web page**. A landing-page button, a row of feature chips, a frosted card with a gradient headline — these are everywhere on the web and **nowhere in a real social post**. A director scrolling TikTok/Reels/Shorts never sees them, so the moment one appears the video reads as machine-made and the retention dies. HTML and hyperframes are the right tool; **Bootstrap/Tailwind furniture is not.**
|
|
@@ -1034,6 +1172,8 @@ Compositions are authored in HTML, so the single most common way an AI-edited vi
|
|
|
1034
1172
|
|
|
1035
1173
|
### TikTok-native caption standard (position + font + background) — always adhere
|
|
1036
1174
|
|
|
1175
|
+
> Captions are also the *delivery system* for three of the four charges: the hook is read before any audio, the loop has to stay on screen, and the payoff number needs its own card. What the words should SAY is in `references/hooks-and-virality.md`; this section is how they must LOOK.
|
|
1176
|
+
|
|
1037
1177
|
Short-form is watched on a phone, and the phone's UI eats the frame's edges. **Never pin on-screen text to the extreme top or bottom** — the top ~8% sits under the status bar / "Following · For You" tabs and the bottom ~15% under the username, caption text, music marquee, and action rail. Text there is literally clipped and reads as amateur. Three rules, applied to **every** caption/title/overlay you place or inherit:
|
|
1038
1178
|
|
|
1039
1179
|
- **Position → readable safe zone.** Keep the text box's vertical extent inside **~8%–85%** of canvas height (9:16). A lower-third subtitle lives around **y≈70%** (the `captions generate` default); a "me when…" / "POV:" meme line sits near the **top of the safe zone (y≈8%), not y:0**. Preserve the original top-vs-bottom anchoring — just pull it off the dead-zone edge, don't recenter everything. Also keep wide captions clear of the **right ~12%** action rail (a centered box at `x:10 width:80` is safe).
|
|
@@ -1107,6 +1247,10 @@ Beyond the Ken Burns / transition / animated-caption presets, the copilot can ha
|
|
|
1107
1247
|
|
|
1108
1248
|
**Make the sticker (`vidfarm cutout`).** Local, free, ffmpeg-only, image-only. `vidfarm cutout --generate "<subject>"` AI-generates on a matching plate then keys+trims (only the generation is billed); `vidfarm cutout <file|url>` keys+trims a graphic you already have for $0. Because the output is trimmed tight, its on-frame **width % maps directly to how big the subject reads** — no invisible padding skewing the layout. Knobs: `--preset`/`--key-color` (match the plate), `--pad <px>` (transparent breathing room), `--alpha-threshold <0..255>` (trim faint haloed edges), `--output-format webp`, `--no-trim`. A *moving* subject has no single bounding box → key it as a clip with `vidfarm remove-greenscreen <video>` (→ transparent WebM) instead.
|
|
1109
1249
|
|
|
1250
|
+
**Make a whole PACK at once (`vidfarm sticker-pack`).** When the scene needs a *set* — props, icons, a cast, plus any backdrops — don't run `cutout` N times. Generate ONE greenscreen sheet holding every item and split it locally: `vidfarm sticker-pack --generate "<theme>" --items "a,b,c" --out-dir ./stickers` (one billed image job for the set), or `vidfarm sticker-pack ./sheet.png` for $0 on a sheet you already have. It keys the plate once, **segments the sheet's alpha channel to find each item automatically** (no hand-measured crop rects), trims each to its true box, and writes `stickers.json`. `--dry-run` to inspect the boxes; `--gap` to fix items that merged (lower) or split (raise). **Item size is unbounded** — a full-frame landscape/skyline/backdrop is as legitimate a sticker as a small icon, and belongs in the same sheet so it shares the cast's style. Recipe: `recipes/cutout-graphics-for-explainers.md` → "A sticker pack".
|
|
1251
|
+
|
|
1252
|
+
**Sticker formats.** Still sticker → **PNG** (or `--output-format webp` for smaller files). Sticker whose own artwork moves → **transparent WebM** via `vidfarm remove-greenscreen <video>` (ProRes `.mov` is the local fallback; `--cloud` always returns WebM). Sticker destined for a **GIF-only surface** (chat, forum, Notion, a shareable pack) → `--output-format gif` on `sticker-pack`, or `vidfarm remove-greenscreen <video> --gif` for an animated one; GIF alpha is 1-bit, so edges harden and soft shadows vanish — never the choice for compositing on a timeline.
|
|
1253
|
+
|
|
1110
1254
|
**Place it.** Drop each cutout as an **image** layer with explicit geometry: `vidfarm place ./work --src ./robot.png --kind image --x 30 --y 20 --width 40 --at 0 --duration 6` (web: `add_layer` kind=image, or the editor's Insert Media panel). Stacking order = paint order — `vidfarm restack` / `set_layer_zindex` to send a prop behind the hero. Reuse the same PNG across scenes; a cutout is generated once and reused.
|
|
1111
1255
|
|
|
1112
1256
|
**Animate it — the explainer move vocabulary.** One layer at a time; all preview AND render identically. Devcli `vidfarm keyframes <dir> --layer <k> --preset <name>`; web `set_layer_keyframes`. Sticker-tuned presets:
|
|
@@ -1143,6 +1287,244 @@ For anything bespoke, pass raw stops: `vidfarm keyframes ./work --layer robot --
|
|
|
1143
1287
|
|
|
1144
1288
|
**Pick the store by lifetime.** A throwaway intermediate (cloud render source, a `place` scratch asset) belongs in the 30-day `temp/` store: `vidfarm upload clip.mp4 --folder temp`, `vidfarm place … --folder temp` — quarantined in one place you can periodically purge. Anything you **approve/share is permanent**, so `vidfarm approve --video ./final.mp4` uploads to **durable My Files** by default (the share video would otherwise 404 after the temp store's 30-day deletion). Only add `vidfarm approve --temp` when you deliberately want a disposable 30-day preview.
|
|
1145
1289
|
|
|
1290
|
+
## The hook & virality harness — apply to EVERY video
|
|
1291
|
+
|
|
1292
|
+
Most agent-made videos don't fail on polish. They fail on **structure**: no hook worth stopping for, no reason to stay, a payoff that never lands, and no ask at the end. Polish is what an agent is naturally good at, so it over-invests there and ships a beautiful video nobody watches.
|
|
1293
|
+
|
|
1294
|
+
This is the harness that fixes it. It is not a style — it's the load-bearing anatomy of anything that travels on TikTok/Reels/Shorts, distilled from grading hundreds of hooks against real funnels. **Run it on one-off videos and on batches alike.** It costs no credits, adds no render time, and it is the single largest quality delta available in this product.
|
|
1295
|
+
|
|
1296
|
+
The checkable form of this document is the bundled `hooks` regime (`vidfarm regime show hooks`); this file is the craft behind it.
|
|
1297
|
+
|
|
1298
|
+
---
|
|
1299
|
+
|
|
1300
|
+
## The build order (do it in this order, it is the harness)
|
|
1301
|
+
|
|
1302
|
+
The reason agent videos come out structureless is that the timeline is the fun part, so it gets built first and the words get retrofitted. Invert it:
|
|
1303
|
+
|
|
1304
|
+
1. **Write the hook line as TEXT, before touching the timeline.** One line. Show it to the director. If it doesn't survive being read aloud with no visuals, no clip will save it.
|
|
1305
|
+
2. **Name the open loop and where it closes.** One sentence: *"opens at 0:08 — 'the third one told me why the first two ghosted' — closes at 0:34."* If you can't state the timestamp it closes at, there is no loop.
|
|
1306
|
+
3. **Name the payoff.** What is on screen at that moment, and why does it satisfy the promise?
|
|
1307
|
+
4. **Write the bait.** The final-beat ask, in the video and in the post caption.
|
|
1308
|
+
5. **Only now build the timeline** — and place the hook text at `start:0` so it's on screen at frame 0 (which is also the thumbnail).
|
|
1309
|
+
6. **Verify the frame and the structure:** `vidfarm stills ./work --at 0` (look at the actual poster) and `vidfarm qa ./work --regime hooks` (machine checks + the judgment checklist).
|
|
1310
|
+
|
|
1311
|
+
Steps 1–4 are cheap, reversible, and where the entire outcome is decided. Steps 5–6 are where agents want to start.
|
|
1312
|
+
|
|
1313
|
+
---
|
|
1314
|
+
|
|
1315
|
+
## Charge 1 — THE HOOK (first 3 seconds)
|
|
1316
|
+
|
|
1317
|
+
### The three gates
|
|
1318
|
+
|
|
1319
|
+
A hook clears three gates **in order**, and they fail in order too — each one silently, so the view count never tells you which one killed you:
|
|
1320
|
+
|
|
1321
|
+
| # | Gate | Question | Where it lives | Failure looks like |
|
|
1322
|
+
|---|---|---|---|---|
|
|
1323
|
+
| 1 | **Legibility** | Can a stranger *understand* this? | Caption chunk 1 | Never leaves the test batch |
|
|
1324
|
+
| 2 | **Tension** | Is there something they don't know? | The withheld final clause | Views and likes, no clicks |
|
|
1325
|
+
| 3 | **Relevance** | Is this person a buyer? | Chunks 2–3 and the body | Clicks and opt-ins, no sales |
|
|
1326
|
+
|
|
1327
|
+
**The order is the whole insight.** Everyone writes for gate 3 first, because relevance is what marketing training optimizes for. But the platform's first test batch is a few hundred essentially random viewers, of whom the buyer is 1–2%. **A hook that only works on a perfectly-targeted viewer never gets a perfectly-targeted viewer** — ~98% of the batch swipes without comprehending, watch time craters, and the video never escapes to reach the people who'd have understood it.
|
|
1328
|
+
|
|
1329
|
+
### The unit is caption chunk 1, not "the first 3 seconds"
|
|
1330
|
+
|
|
1331
|
+
Captions render in phrase chunks of ~2–6 words, and the viewer **reads chunk one before hearing any audio**. The real hook unit is **words 1–5 on screen**. This is why the hook text layer starts at 0 and why `hook_words_max` is a machine check.
|
|
1332
|
+
|
|
1333
|
+
> **Chunk 1 must be legible to everyone and relevant to few.**
|
|
1334
|
+
|
|
1335
|
+
Three constraints:
|
|
1336
|
+
|
|
1337
|
+
1. **Complete clause — subject plus verb.** Not a noun phrase, not a bare number, not a label. This one rule fixes most failures, because a clause forces context in.
|
|
1338
|
+
2. **No jargon.** A domain term in chunk 1 kills the cold start even inside a valid clause. Jargon belongs in chunk 2+, where it *qualifies* instead of excluding.
|
|
1339
|
+
3. **The cold-viewer test.** Would a random person with no interest in the category understand what situation is being described? Not *would they care* — *would they understand*.
|
|
1340
|
+
|
|
1341
|
+
### Situations, not labels — the cheapest predictor there is
|
|
1342
|
+
|
|
1343
|
+
The three constraints are checks you run *after* writing. This one tells you whether a hook will survive *before* you write it:
|
|
1344
|
+
|
|
1345
|
+
> **Situations are cold-legible by construction. Labels never are.**
|
|
1346
|
+
|
|
1347
|
+
A **situation** is a thing that happened, with a subject and an outcome: *someone deleted my message · she refused my money · they stopped replying · tracking said delivered · his lot is full.* A stranger parses all of it instantly, because humans are built to parse events.
|
|
1348
|
+
|
|
1349
|
+
A **label** is a concept with a name: *anonymity · distribution · platform risk · seeding · onboarding.* It carries meaning only for someone who already holds the category — exactly the person the cold-start batch does not contain.
|
|
1350
|
+
|
|
1351
|
+
Across four avatars and 100 graded hooks, **every hook cut for illegibility was built on a label**, and the one cluster that lost nothing was built entirely on situations. If your opening names a concept rather than an event, assume it fails until proven otherwise.
|
|
1352
|
+
|
|
1353
|
+
| Fragment (fails) | Complete clause (passes) |
|
|
1354
|
+
|---|---|
|
|
1355
|
+
| `SIX STARTS, ZERO FINISHES` | `I'VE QUIT SIX BUSINESSES` |
|
|
1356
|
+
| `FOURTH APP` | `I SHIPPED MY FOURTH APP` |
|
|
1357
|
+
| `ANONYMITY` | `I'VE NEVER SHOWN MY FACE` |
|
|
1358
|
+
| `$50,000 IN SALES` | `THEY WANTED $50,000 FIRST` |
|
|
1359
|
+
| `A CARTOON FROG` | `A CARTOON FROG OUTSELLS ME` |
|
|
1360
|
+
| `TWO HOURS A WEEK` | `THIS TAKES TWO HOURS A WEEK` |
|
|
1361
|
+
|
|
1362
|
+
The tension survives every fix — it just arrives inside a sentence a stranger can parse. Often the legible version is *stronger*, because a stranger can feel it where the fragment only rewarded someone already in the situation.
|
|
1363
|
+
|
|
1364
|
+
### Banned openers
|
|
1365
|
+
|
|
1366
|
+
*So I was thinking · There's a reason · Here's the thing · A lot of people · If you're a… · Let me tell you* — any throat-clearing. Also banned as an opening beat: a logo, a title card, a fade from black, a beat of silence, and any sentence whose subject arrives in the second half. **Context is beat 2**, and it goes after the hook, never before it.
|
|
1367
|
+
|
|
1368
|
+
### Stack three hooks simultaneously
|
|
1369
|
+
|
|
1370
|
+
Most videos fire only one:
|
|
1371
|
+
|
|
1372
|
+
1. **Verbal** — the spoken line.
|
|
1373
|
+
2. **Text** — a caption already on screen at frame 0, readable before a word is said.
|
|
1374
|
+
3. **Visual** — motion, an unusual angle, a face already mid-sentence, hands already working.
|
|
1375
|
+
|
|
1376
|
+
**Muted autoplay is the default viewing condition, so the text hook does more work than the verbal one.** If the video only works with sound on, it doesn't work.
|
|
1377
|
+
|
|
1378
|
+
⚠️ **Frame 0 is the hook AND the thumbnail** — the one frame more people see than watch the video. No black open, no fade-up, subject in frame, caption legible. Full rules in `references/editor-workflows.md` ("The first frame is the thumbnail").
|
|
1379
|
+
|
|
1380
|
+
---
|
|
1381
|
+
|
|
1382
|
+
## Charge 2 — THE CURIOSITY LOOP (retention)
|
|
1383
|
+
|
|
1384
|
+
**The charge most videos skip entirely**, and the one that separates 200 views from 30,000. The algorithm is measuring whether people stay, and nothing holds attention like an unresolved question.
|
|
1385
|
+
|
|
1386
|
+
### Identification vs curiosity — know which you're writing
|
|
1387
|
+
|
|
1388
|
+
| Type | Triggers | Earns |
|
|
1389
|
+
|---|---|---|
|
|
1390
|
+
| **Identification** | "that's me" · "finally someone said it" | a like, a save, a follow |
|
|
1391
|
+
| **Curiosity** | "wait, what?" · "why?" · "I need to know if that's true" | **a click** |
|
|
1392
|
+
|
|
1393
|
+
Both are legitimate and they are not interchangeable. If the video's job is a click into a funnel, **the loop matters more than the resonance.**
|
|
1394
|
+
|
|
1395
|
+
### The mechanical fix
|
|
1396
|
+
|
|
1397
|
+
Most weak hooks state the insight **and its resolution** in one line, so there's nothing left to find out. **Move the payoff out of the line and into the video body** — same insight, same specificity, the last clause just stops answering itself.
|
|
1398
|
+
|
|
1399
|
+
| Closes (dead) | Opens (loop) |
|
|
1400
|
+
|---|---|
|
|
1401
|
+
| "I paid three creators. I ended up teaching all three how to hook." | "I paid three creators. The third one told me why the first two ghosted." |
|
|
1402
|
+
| "Every customer I have, I'm renting." | "I asked my lead company one question. Their answer told me I don't own a single customer." |
|
|
1403
|
+
| "Launch day: three upvotes, two of them mine." | "Fourth launch, three upvotes. The problem was never the product." |
|
|
1404
|
+
| "Nobody quits a project that's getting attention." | "Nobody quits a project that's getting attention. So your problem was never discipline." |
|
|
1405
|
+
|
|
1406
|
+
> **Tension in the first chunk. Withheld answer in the last chunk.**
|
|
1407
|
+
|
|
1408
|
+
The two constraints pull against each other — the loop withholds at the end, legibility demands the opening earn a read on its own. Solve both. If the line can't carry both, rewrite the line; don't drop one.
|
|
1409
|
+
|
|
1410
|
+
### ⚠️ The answer must be unguessable
|
|
1411
|
+
|
|
1412
|
+
A structurally valid loop whose answer the viewer can supply themselves **is not a loop**. The withhold happens on the page; the resolution happens in their head before their thumb moves.
|
|
1413
|
+
|
|
1414
|
+
This is **the most dangerous failure in this document**, because it passes every mechanical check — complete clause, no jargon, payoff withheld at the end, cold-legible. It looks correct in review and dies in the field. The example that caught it:
|
|
1415
|
+
|
|
1416
|
+
> *"'TikTok's for kids' is exactly what the Yellow Pages guys said about Google. Ask what happened to them next."*
|
|
1417
|
+
|
|
1418
|
+
Formally a loop. Useless — everyone already knows what happened to the Yellow Pages. The answer closes instantly and for free.
|
|
1419
|
+
|
|
1420
|
+
**Test: can the viewer answer your withheld question without watching?** If yes, two ways out:
|
|
1421
|
+
|
|
1422
|
+
1. **Withhold something only you know** — the other side of a transaction, what the gatekeeper actually checks, what the person who ignored them said. That material is unknowable without watching, and it qualifies naturally.
|
|
1423
|
+
2. **Send them to check rather than telling them** — replace the withheld fact with an instruction to go find out: *"You think it's all teenagers on there. Go and look at who's still watching at eleven at night."* The tension is an unresolved *belief*, which they can't close from the sofa.
|
|
1424
|
+
|
|
1425
|
+
### Loop rules
|
|
1426
|
+
|
|
1427
|
+
- **Open it by 0:10.** After that you're re-hooking someone who already decided to leave.
|
|
1428
|
+
- **Say the loop on screen**, not just out loud — caption it, or run it as a visible timer/counter.
|
|
1429
|
+
- **One loop.** Two open questions dilute both.
|
|
1430
|
+
- **It opens and closes inside THIS video.** Withholding for forty seconds is retention; deferring the answer to another upload is a broken video — you don't control feed order, and a breakout video is seen alone. "Part two" is not a loop, it's an unpaid debt.
|
|
1431
|
+
- **Never bait-and-switch the payoff.** If you promised a number, give the real number.
|
|
1432
|
+
- **Don't force it.** When a line lands in one beat and withholding would break it, keep it and *label it an identification hook* — tagged, those become the control group that tells you whether loops actually beat identification for this audience.
|
|
1433
|
+
|
|
1434
|
+
**Mechanics, ranked:** the withheld number (strongest — a real number is a promise you can keep, and an on-screen timer sustains the loop visually for the whole video) · the withheld part ("one part of this nobody explains, and it decides whether it works for you") · the numbered list ("three reasons, the third is the only one that matters" — tells the viewer exactly how long to stay; deliver all three) · the unresolved visual (a half-cut timeline, an export bar, a finished video not yet played — works with zero words, survives muting) · the contradiction/concession ("X pays better, and I'm still going to explain this" — the concession *is* the loop: *so why bother?*).
|
|
1435
|
+
|
|
1436
|
+
---
|
|
1437
|
+
|
|
1438
|
+
## Charge 3 — THE PAYOFF (tap the desire)
|
|
1439
|
+
|
|
1440
|
+
The moment the video pays what the hook promised, landing on **what they actually want** — not on what you're selling.
|
|
1441
|
+
|
|
1442
|
+
- **Show it, don't summarize it.** The result plays, full frame, ≥5 uninterrupted seconds. The real number appears on screen. Description is not payoff.
|
|
1443
|
+
- **The payoff is not the CTA.** No product pitch in this slot. Pitching where the payoff belongs is the most common way a good video converts nothing.
|
|
1444
|
+
- **Land it before the last beat.** Payoff → bait → close. A payoff in the final second gets cut off by the loop-around.
|
|
1445
|
+
- **Go past the functional layer.** Surface desire ("how do I do this?") gets a mechanism; functional gets a demo; **the emotional layer is usually where the real payoff is** — the honest limit named out loud, the concession that says you're not selling them. For a scam-fatigued or pitch-fatigued audience, *refusing to oversell is the payoff*.
|
|
1446
|
+
- **Identity is the deepest layer:** "that's a real skill I now have." When a video can end there, it does.
|
|
1447
|
+
|
|
1448
|
+
---
|
|
1449
|
+
|
|
1450
|
+
## Charge 4 — THE BAIT (comments are distribution)
|
|
1451
|
+
|
|
1452
|
+
A video with replies gets shown again; a video with none dies at its first audience regardless of watch time. **Bait is a deliberate beat, not something you hope for.**
|
|
1453
|
+
|
|
1454
|
+
**One ask, in the final beat, and also in the post caption** — many people reply from the caption without finishing. **Pay off first, then ask**; never make the bait the price of the payoff.
|
|
1455
|
+
|
|
1456
|
+
**Works:**
|
|
1457
|
+
|
|
1458
|
+
- ⭐ **The confession invite** — *"Tell me which one you already quit. I've quit two."* Low-stakes, high-volume, and it makes the comment section self-reinforcing.
|
|
1459
|
+
- ⭐ **The guess** — *"Guess how long it took me. I say it at the end."* Pairs perfectly with a withheld-number loop: one device drives retention *and* comments.
|
|
1460
|
+
- **The named omission** — *"I left one thing out on purpose. Ask me."* Converts what you couldn't fit into the reason to reply.
|
|
1461
|
+
- **The soft disagree** — a true concession people want to argue with. Not ragebait if you actually believe it.
|
|
1462
|
+
- **The specific question** — concrete always beats "thoughts?".
|
|
1463
|
+
|
|
1464
|
+
**⚠️ Bait is the highest-compliance-risk beat in the video.** For money/health/appearance topics the obvious bait is the illegal one:
|
|
1465
|
+
|
|
1466
|
+
- ❌ *"Comment MONEY and I'll send you the link"* — the most scam-coded line available, and a DM funnel.
|
|
1467
|
+
- ❌ *"How much do you want to make a month?"* — invites an earnings-claim thread you now own.
|
|
1468
|
+
- ❌ *"Follow for part two"* — breaks the standalone rule.
|
|
1469
|
+
- ❌ Fake controversy, ragebait, or a deliberate error to farm corrections. Works once, costs the positioning permanently.
|
|
1470
|
+
|
|
1471
|
+
---
|
|
1472
|
+
|
|
1473
|
+
## Gate 3 in depth — relevance and funnel alignment
|
|
1474
|
+
|
|
1475
|
+
- **Offer adjacency.** The hook should be the **"before" of the offer's "after."** A hook that fully solves the problem, or creates a desire the offer doesn't resolve, is *worse* than a weak hook: a weak hook underperforms; this one fills the funnel with people who will never buy. Symptom — great opt-in rate, lead→paid under ~5% — and by the time it's visible you've scaled it.
|
|
1476
|
+
- **Deliverability.** Aggression isn't the problem; undeliverability is. Bold, curiosity-driven, outcome-led hooks are what work — but a promise the offer can't keep buys front-end conversion with churn. **Point the aggression at what you can demonstrate.** A large promise you cash every time beats a larger one you can't.
|
|
1477
|
+
- **Guarantees are proof, not headline.** Leading with the guarantee attracts guarantee-shoppers who churn at the boundary, and draws enforcement on cold organic.
|
|
1478
|
+
- **Sell the entry promise, not the offer.** The hook's job is to match **what the next screen actually says**. If the funnel opens on an eligibility question, sell the eligibility curiosity. A hook that pitches the end offer desyncs the sequence.
|
|
1479
|
+
- **Move the whole awareness ladder together.** Hook = problem-aware · body = bridge to the solution · landing page/quiz = solution-aware · proof = product-aware. Changing the hook's awareness stage without moving the page it points at is the most common self-inflicted conversion loss — the drop-off reads as "bad hook" when the failure was the handoff. Unaware-stage hooks are a trap on short-form: that structure needs seven beats and you have one second.
|
|
1480
|
+
- **TAM comes from identity, not awareness stage.** Widening the stage adds viewers, not buyers. Widening the *identity* — from a narrow role to the broader self-image it sits inside — multiplies the audience while keeping the desire intact.
|
|
1481
|
+
|
|
1482
|
+
---
|
|
1483
|
+
|
|
1484
|
+
## Anti-patterns (they show up hardest at volume)
|
|
1485
|
+
|
|
1486
|
+
- **Never point a generator at your grader.** A model writing hooks scored by a model converges on whatever the rubric measures, not on what works — in practice it discovered that appending a proper noun moved "specificity," and produced 21 variants of one sentence with the city and trade swapped. Scores climbed every round; nothing got better. **The rubric is a check on a hook you already believe in, never a ranking function.** Use it to catch defects, not to pick winners. This applies directly to you: do not generate 50 hooks and then rank them with the same reasoning that wrote them.
|
|
1487
|
+
- **A different noun is not a different hook.** If two variants could be filmed from one script by changing one word, you have one hook — and a batch of twenty of those teaches nothing. The dominant failure when writing for a category that spans verticals.
|
|
1488
|
+
- **Specificity is observed detail, not generated nouns.** `Boston window cleaner` is a generated token; `comment posted 9:14, video finished 9:55` is an observation. One sounds invented, the other sounds witnessed — which is the entire point of the principle.
|
|
1489
|
+
- **Obscurity is not qualification.** A hard-to-parse opening doesn't filter for serious buyers, it filters for nobody, because the video never reaches anyone. Qualification is the body's job.
|
|
1490
|
+
- **Founder-confession drift.** "I paid… / my ads… / I spent…" is relatable but makes the video about you. A loop makes it about information the viewer wants. Bias toward the latter.
|
|
1491
|
+
|
|
1492
|
+
---
|
|
1493
|
+
|
|
1494
|
+
## Measure and diagnose by charge
|
|
1495
|
+
|
|
1496
|
+
**The cluster is the unit, not the hook.** A single hook at low view counts tells you nothing — variance dominates. A cluster is a bet on one buyer insight and one emotional lever; that's what you kill or scale. One attribution slug per cluster (use the render `tracer`), one cluster per account — mixing them stops the algorithm finding the audience.
|
|
1497
|
+
|
|
1498
|
+
**Judge on the metric that pays rent.** In increasing order of truth: views → completions → profile visits → **funnel/quiz completions per post** → **revenue per 1,000 views**. Comments and views are distribution signals, not leads.
|
|
1499
|
+
|
|
1500
|
+
| What the numbers say | Weak charge | Fix |
|
|
1501
|
+
|---|---|---|
|
|
1502
|
+
| Barely any views | 🪝 Hook | Rewrite the first 3s. New opening line, new frame 0 |
|
|
1503
|
+
| Views, mass exit at 3–8s | 🔄 Loop | No open question. Plant one by 0:10 |
|
|
1504
|
+
| Watched to the end, no reaction | 😍 Payoff | Landed on features, not desire — go to the emotional layer |
|
|
1505
|
+
| Good retention, no comments | 🎣 Bait | No ask, or the ask was "thoughts?" |
|
|
1506
|
+
| Comments, but hostile | 🎣 Bait | Ragebait or an over-claim |
|
|
1507
|
+
| Good clicks, no sales | Relevance | Offer adjacency — wrong audience |
|
|
1508
|
+
|
|
1509
|
+
**Vary one charge at a time.** Same body, new hook. Same hook, new bait. A batch where everything changed teaches you nothing. **Then play the power law:** ~2% of videos produce ~90% of results — when one hits, make ten more of exactly it. Iterate on winners; never fix losers.
|
|
1510
|
+
|
|
1511
|
+
---
|
|
1512
|
+
|
|
1513
|
+
## Running this harness in Vidfarm
|
|
1514
|
+
|
|
1515
|
+
| Step | Web copilot | Desktop / devcli |
|
|
1516
|
+
|---|---|---|
|
|
1517
|
+
| Generate hook candidates | `brainstorm/hooks` (the consultation chain — see `references/onboarding.md`) | same, via `vidfarm api` |
|
|
1518
|
+
| Check what the source template's hook actually was | `editor_context` → `viral_dna.hook` / `retention` / `payoff` / `emotional_punch` | `.harness/context.json`, `video-context.json` |
|
|
1519
|
+
| Place the hook at frame 0 | `add_layer` / `set_captions` with `start:0` | `vidfarm set-text ./work --layer hook --text "…"` |
|
|
1520
|
+
| Look at the poster frame | ask the user to scrub to 0 | `vidfarm stills ./work --at 0` |
|
|
1521
|
+
| Grade the structure | by hand, against this file | `vidfarm qa ./work --regime hooks` |
|
|
1522
|
+
| Bulk hook test | hand off to a local agent | `recipes/bulk-scripting-with-a-regime.md` |
|
|
1523
|
+
|
|
1524
|
+
**Re-theming a decomposed template?** `viral_dna` already names the source's hook, retention device, and payoff — that structure is *why the template worked*. Rebuild each charge for the new subject; don't drop the loop because the new topic feels self-explanatory. Flattening a template's loop into a product statement is the single most common way a re-theme kills a format.
|
|
1525
|
+
|
|
1526
|
+
**The checkable version of everything above:** `vidfarm regime show hooks` — the twelve-item pre-flight checklist is the part you answer honestly on every video, and two items carry most of the weight: *situation, not label* (predicts cold-start survival before you write a word) and *unguessable* (the only item a hook can fail while passing every other one, which is why it ships).
|
|
1527
|
+
|
|
1146
1528
|
## Download a video from a website (Vidfarm fetches it for you — paid plans)
|
|
1147
1529
|
|
|
1148
1530
|
**Yes, Vidfarm can download videos off the web for the user.** When someone pastes a YouTube / TikTok / Instagram / X (Twitter) / other supported post URL and wants the actual file — "grab this video", "save this clip", "get me that audio" — you do **not** need yt-dlp, a browser extension, or an apology. One primitive does it and returns a durable Vidfarm-hosted asset:
|
|
@@ -1348,6 +1730,50 @@ Send a stable `tracer` on export so retries are traceable and filterable in job
|
|
|
1348
1730
|
|
|
1349
1731
|
**Scripting mode** is the recommended posture for repeatable template automation. Use it when a director wants to take a template they like, agree on a base fork, and then drive bulk or one-off edits entirely through REST or `vidfarm api` from a script, Lambda, or local machine.
|
|
1350
1732
|
|
|
1733
|
+
**Ask which mode you're in, once, before you build.** These are different jobs and the answer changes everything downstream:
|
|
1734
|
+
|
|
1735
|
+
| | **One-time video** | **Bulk / scripting mode** |
|
|
1736
|
+
|---|---|---|
|
|
1737
|
+
| The deliverable | One MP4 you both look at | A loop that produces N videos nobody watches frame-by-frame |
|
|
1738
|
+
| Quality control | Your eyes on the render | **A `QA_REGIME.md`** — the batch's written standard |
|
|
1739
|
+
| What you optimize | This video | The *variant axis* (one thing changes; everything else is held) |
|
|
1740
|
+
| Cost posture | Per-video decisions are fine | Per-video AI spend × N — reuse assets, prefer clip pools |
|
|
1741
|
+
|
|
1742
|
+
A director who says "make me a video about X" usually wants the first. A director who says "I need to post daily" / "make 20 variants" / "test hooks" wants the second and often doesn't know it has a name. **Offer the upgrade explicitly:** *"Want this as one video, or should we set it up as a repeatable batch? Batches get a QA_REGIME.md so variant #37 is as good as #1."* Don't silently build a one-off when they asked for volume, and don't drag someone into a scripting harness when they wanted one clip.
|
|
1743
|
+
|
|
1744
|
+
### `QA_REGIME.md` — the quality contract for a batch
|
|
1745
|
+
|
|
1746
|
+
`vidfarm qa`'s built-in rules are **universal** (no HTML slop, the font regime, the thumbnail frame) — the same for everyone, so they live in code. A regime is the opposite: it's what makes **this** director's **this** format good — their audience, hook shape, banned vocabulary, pacing, compliance line. It can't be hard-coded, so it lives next to the work as Markdown they own and version.
|
|
1747
|
+
|
|
1748
|
+
**It exists because bulk output loses its human reviewer.** One video gets eyes on every frame; fifty generated in a loop do not. The regime is what the loop grades against.
|
|
1749
|
+
|
|
1750
|
+
```bash
|
|
1751
|
+
vidfarm regime list # the bundled starting points
|
|
1752
|
+
vidfarm regime init short-form --out ./work/QA_REGIME.md # copy, then EDIT it
|
|
1753
|
+
vidfarm qa ./work # auto-picks up ./work/QA_REGIME.md
|
|
1754
|
+
vidfarm qa ./work --regime hooks --regime ./brand/HOUSE.md # built-in + your own file — they STACK
|
|
1755
|
+
```
|
|
1756
|
+
|
|
1757
|
+
Bundled bases (`vidfarm regime list`, files under `.agents/skills/vidfarm/regimes/`): **`short-form`** (the default — the four charges hook/loop/payoff/bait + the standalone rule), **`hooks`** (hook-variant batches: chunk-1 legibility, the unguessable test, the anti-patterns that only show up at volume), **`ugc-testimonial`**, **`explainer`**, **`product-demo`**. Each is a *starting point to edit*, never a house style to conform to — the parts that matter most are the parts the director adds. A regime can also be any file anywhere: `--regime ./campaigns/q3/RULES.md` is fully supported, and `VIDFARM_QA_REGIME=./work/QA_REGIME.md` sets a default for a whole run.
|
|
1758
|
+
|
|
1759
|
+
**The format is two halves, and the split is deliberate:** a front-matter `checks:` block the CLI settles deterministically (duration, aspect, `hook_words_max`, `forbid_text`, `first_frame_text`, … — full key list in `regimes/README.md`), and every `- [ ]` checkbox in the body, which comes back as a **review item for you to answer**. "Is the withheld answer one the viewer can't supply themselves?" is a judgment call; a linter claiming to settle it would be lying. **Answer the review items honestly in your report** — the CLI prints them precisely because it can't.
|
|
1760
|
+
|
|
1761
|
+
**Build on it.** When you learn something from a batch ("the label-framed hooks all died"), write it into the regime as a new rule or checklist line. That is the artifact that compounds across runs; the composition files don't.
|
|
1762
|
+
|
|
1763
|
+
### The bulk loop, with the regime in it
|
|
1764
|
+
|
|
1765
|
+
```bash
|
|
1766
|
+
vidfarm regime init hooks --out ./work/QA_REGIME.md # once, then edit for this account
|
|
1767
|
+
for VARIANT in "${VARIANTS[@]}"; do
|
|
1768
|
+
vidfarm set-text ./work --layer hook --text "$VARIANT"
|
|
1769
|
+
vidfarm qa ./work --json > "qa/$SLUG.json" # regime auto-discovered from ./work
|
|
1770
|
+
jq -e '.ok' "qa/$SLUG.json" >/dev/null || continue # YOUR gate, in YOUR script
|
|
1771
|
+
vidfarm render "$FORK_ID" --dir ./work --out "renders/$SLUG.mp4"
|
|
1772
|
+
done
|
|
1773
|
+
```
|
|
1774
|
+
|
|
1775
|
+
`vidfarm qa` still exits 0 on findings — the gate above is the *script's* choice, made explicit with `jq`, not a behavior change in the tool. Keep it that way: an agent that can't ship a deliberately weird variant will quietly stop trying weird variants.
|
|
1776
|
+
|
|
1351
1777
|
This section is for a **desktop/local coding agent**, not the web copilot. A local Codex/Claude agent may use its shell and filesystem to write JavaScript/TypeScript/Python/shell scripts, fetch every API page, join and score catalog/library data, calculate statistics, emit CSV/JSON/Markdown reports, manipulate composition DOM files, and run iterative render/inspection loops. The web copilot cannot inherit those abilities from this document: it may only call its declared tools and bounded REST routes. If web chat prepares work for this flow, consume its My Files handoff document as input; do not claim the web chat itself executed the script.
|
|
1352
1778
|
|
|
1353
1779
|
Contract:
|
|
@@ -1481,9 +1907,10 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1481
1907
|
| `vidfarm generate <image\|video> --prompt "…"` | `POST /api/v1/primitives/{images,videos}/generate` (polls job) | generate AI media → finished URL |
|
|
1482
1908
|
| `vidfarm inpaint <image> --mask <png> --prompt "…" [--region "label=…"] [--ref …] [--out <f>]` | `POST /api/v1/primitives/images/inpaint` (polls job) | masked image EDIT — replace ONLY the transparent-mask region, keep everything else (devcli twin of the /inpaint page) |
|
|
1483
1909
|
| `vidfarm create-overlay "<subject>" [--key-color #00FF00] [--aspect-ratio 1:1] [--place <dir>] [--out <f>]` | `POST /api/v1/primitives/images/create-overlay` (polls job) | **Vox-style** transparent OVERLAY — AI image on a forced key-color background, chroma-keyed out in one job → ready-to-composite transparent PNG |
|
|
1484
|
-
| `vidfarm remove-greenscreen <image\|video> [--preset green\|blue\|white\|black\|digital-green\|magenta] [--key-color #00FF00] [--tolerance 0.3] [--local] [--out <f>]` | `POST /api/v1/primitives/remove-greenscreen` (polls job) | chroma-key a FLAT solid background → transparent PNG/WebP (image) or WebM/VP9-alpha (video); auto-detects media kind. `--local` runs it FREE in-process (sharp/ffmpeg, no wallet); default cloud is billed at real compute × 1.2. Aliases: `greenscreen`, `remove-background-greenscreen`. |
|
|
1910
|
+
| `vidfarm remove-greenscreen <image\|video> [--preset green\|blue\|white\|black\|digital-green\|magenta] [--key-color #00FF00] [--tolerance 0.3] [--local] [--gif] [--out <f>]` | `POST /api/v1/primitives/remove-greenscreen` (polls job) | chroma-key a FLAT solid background → transparent PNG/WebP (image) or WebM/VP9-alpha (video); auto-detects media kind. `--local` runs it FREE in-process (sharp/ffmpeg, no wallet); default cloud is billed at real compute × 1.2. **`--gif` writes a transparent GIF instead** (ANIMATED for a clip; `--gif-fps`/`--gif-width`/`--gif-alpha`) — local-only, 1-bit alpha, for GIF-only sticker surfaces; prefer PNG/WebP/WebM for compositing. Aliases: `greenscreen`, `remove-background-greenscreen`. |
|
|
1485
1911
|
| `vidfarm cutout <image\|url> [--generate "<prompt>"] [--preset green] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--out <f>]` | **local, free, ffmpeg-only** (no job) — key + `alphaextract`/`cropdetect` trim | **The transparent explainer-STICKER maker.** Keys out the flat plate **and then shrinks the canvas to the cutout's true min width/height** (a 1024² mostly-empty plate → a snug sticker whose pixel size IS the subject) so you can scale/position it precisely. `--generate` AI-generates the graphic first on a matching chroma plate (that step is the billed image primitive), then keys+trims in one shot; without it, keys+trims a file/url you already have. **IMAGE-only** (a moving subject has no single bounding box — key a clip with `remove-greenscreen`). Prefer this over `create-overlay` locally: same idea, but free and auto-trimmed. `--pad` keeps transparent breathing room; `--json` reports final `width`/`height`/`area_reduced_pct`. Alias: `sticker`. See recipe `cutout-graphics-for-explainers.md`. |
|
|
1486
1912
|
| `vidfarm mask <image\|url> [--crop x,y,w,h] [--flat <hex>] [--pad <px>] [--alpha-threshold <n>] [--no-trim] [--output-format png\|webp] [--keep-region <f>] [--out <f>]` | **local, free** (no job) — ffmpeg crop + ONNX matting (or ffmpeg chroma-key) + `cropdetect` trim | **Lift an illustration OUT of an image you already have** (infographic / poster / marketing graphic / brand sheet / screenshot) → snug transparent PNG, the same reusable explainer sticker `cutout` makes but with **$0 and zero AI generation** — the cost-saving move whenever source art exists. `--crop x,y,w,h` (pixels **or** %) isolates ONE element from a multi-illustration source before masking (re-run with different rects to grab each). Background removed by **local ONNX matting** (any/busy background) by default, or **`--flat <hexcolor>`** chroma-keys a solid fill for crisper edges (an infographic's cream/white paper); then trims to the subject's true min width/height. **IMAGE-only** (matte a clip with `remove-background`). Aliases: `isolate`, `extract`. See recipe `cutout-graphics-for-explainers.md` → "Mask from an image you already have". |
|
|
1913
|
+
| `vidfarm sticker-pack [sheet\|url] [--generate "<theme>"] [--items "a,b,c"] [--count <n>] [--dry-run] [--gap <pct>] [--min-area <pct>] [--output-format png\|webp\|gif] [--out-dir <d>]` | **local, free, ffmpeg-only** (no job; only `--generate` bills, ONCE for the whole set) — key + alpha-channel segmentation + per-item trim | **The STICKER-PACK maker — the answer whenever a director asks for "a sticker pack" / prop set / icon set.** A pack is ONE greenscreen sheet holding every item, keyed once and then masked apart: 1/N the cost of N `cutout` calls, and the only way a cast stays on-style. Finds each item **automatically** by segmenting the keyed sheet's alpha into connected islands — no hand-measured `--crop` rects — and writes one snug transparent file per item (named from `--items`, reading order) plus a `stickers.json` manifest. `--dry-run` prints the detected boxes first; `--gap` merges (lower) or splits (raise) items that came out joined/broken; items have **no maximum size** — a full-frame landscape/backdrop is as valid a sticker as a 3% icon. **Plate color is chosen for you:** when generating it reads the subject and moves the plate off any hue the art uses (green → magenta → blue → black → white — a pack of leaves/frogs/money on GREEN would key holes through the art), and when splitting an existing sheet it DETECTS the plate from the sheet's four corners, so a red/purple sheet handed back from a web tool just works. Pin it with `--key-color`/`--preset`, or `--no-auto-key` for plain green. `--output-format gif` emits 1-bit-alpha GIFs for GIF-only surfaces. IMAGE-only. Aliases: `stickers`, `sticker-sheet`. See recipe `cutout-graphics-for-explainers.md` → "A sticker pack". |
|
|
1487
1914
|
| `vidfarm tts "…" [--style "…"] [--voice <v>] [--out <file>]` | (LOCAL-FIRST: your own OPENAI/GEMINI/OPENROUTER_API_KEY → audio file on disk; `--cloud` = `POST /api/v1/primitives/audio/speech` + poll, ElevenLabs on the platform key by default, `--own-key` for yours) | text → narration audio; `--cloud --voice <voice_id>` picks an ElevenLabs voice |
|
|
1488
1915
|
| `vidfarm music "<prompt>" [--length <sec>] [--out <f>] [--own-key]` | `POST /api/v1/primitives/music/generate` (polls job) | prompt → music track (ElevenLabs; platform key + wallet by default, `--own-key` for yours) |
|
|
1489
1916
|
| `vidfarm voices [--own-key] [--limit N]` | `GET /api/v1/primitives/audio/voices` | list ElevenLabs voices (voice_id/name/labels) for `tts --voice`; default a voice + tell the user they can choose |
|
|
@@ -1511,6 +1938,10 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1511
1938
|
| `vidfarm login <email>` / `vidfarm verify <email> <code>` | `POST /api/v1/user/request-otp` · `verify-otp` | get an API key |
|
|
1512
1939
|
| `vidfarm whoami` | `GET /api/v1/user/me` | who am I (also prints the active cost mode) |
|
|
1513
1940
|
| `vidfarm cost-mode [minimize\|hybrid\|rich-ai\|pure-videogen]` | (local `~/.vidfarm/cost-mode.json`) | show/set the money-saving preference every billed command respects; no arg = show + explain each; `--clear` forgets it |
|
|
1941
|
+
| `vidfarm mode [interactive\|autonomous]` | local (`~/.vidfarm/interaction-mode.json`) | **The SECOND preference axis — ask it alongside cost mode.** Is the human in the loop? `interactive` = at checkpoints you hand them a copy-paste brief (an image prompt for a FREE web generator; a keyword list for sourcing clips) and they hand files back — usually BETTER output, and in `minimize` the only way to get custom art at all. `autonomous` = you finish solo within the budget. Independent of cost mode: every cost mode runs either way. No arg prints the pair + the explainer to relay; `--clear` forgets; per-run `--interactive`/`--autonomous`. Aliases: `interaction-mode`, `interaction`. |
|
|
1942
|
+
| `vidfarm handoff image --theme "<what>" [--items "a,b,c"] [--style …] [--single]` | local (pure text) | **The interactive-mode IMAGE brief.** Prints the exact prompt, the numbered steps, the free tools (meta.ai / ChatGPT / Gemini / HF Spaces) and the follow-up command. Defaults to a **sticker pack**: ONE sheet holding every item on a chroma plate → `vidfarm sticker-pack` splits it for $0. Picks a plate the art won't collide with (green art → magenta plate) and carries that `--key-color` into the follow-up. `--single` for one subject. |
|
|
1943
|
+
| `vidfarm handoff raws --keywords "a,b" [--platforms tiktok,youtube] [--count N] [--purpose "…"]` | local (pure text) | **The interactive-mode CLIP-SOURCING brief** — the bottom rung of the sourcing ladder (browser control → `clipper`/`raws scan --cloud` → public raws → the human). Prints what to search, how to download (a Google *search* for a downloader, never a link that rots), and the import command for when the folder is ready. |
|
|
1944
|
+
| `vidfarm storyboard [dir] [--init] [--frames "Title\|scene,…"] [--json]` | local (`STORYBOARD.md` / `SCRIPT.md`) | **The plan pass, and a core part of the composition format.** Scaffolds/reads the project's `STORYBOARD.md`: ordered frames with `duration`/`status`/`src`/`scene`/`voiceover`. The Vidfarm editor renders this file in its **Storyboard** view (contact sheet + per-frame comments + `outline → built → animated` progress), so it's the cheapest place to get a director's approval before building. Not to be confused with `vidfarm sequence` (which GENERATES storyboard images for the pure-videogen pipeline). Alias: `plan`. |
|
|
1514
1945
|
| `vidfarm wallet [--job <id>\|--tracer <t>] [--limit <n>]` | `GET /api/v1/user/me/wallet` | cost log: balance + lifetime spend + recent charges. `--job <renderJobId>` prints **what that one video cost** (sums its charges); `--tracer <t>` sums a tracer. Cloud-only; readable on the free plan too (shows $0.00). Aliases: `spend`, `costs` |
|
|
1515
1946
|
| `vidfarm provider-keys` / `vidfarm add-provider-key <p> <secret>` | `GET`·`POST /api/v1/user/me/provider-keys` | manage AI keys |
|
|
1516
1947
|
| `vidfarm upload <file> [--folder <path>]` | presign → S3 PUT → finalize (`.../temporary-files/presign` + `.../temporary-files`) | upload → durable URL (ephemeral, 30-day TTL; prefer `--folder temp` for scratch). Goes direct to S3, so large files (up to **200 MB**) bypass the ~6 MB Lambda body limit |
|
|
@@ -1531,7 +1962,8 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1531
1962
|
| `vidfarm raws preset list\|run\|save` / `raws export <ids…> --to <dir>` | (local library) | saved queries; copy raw MP4s out |
|
|
1532
1963
|
| `vidfarm lint <dir\|composition.html>` | (local static validation) | pre-publish composition check: timing, overlaps, preset names, media src |
|
|
1533
1964
|
| `vidfarm stills <dir> [--at 0,2.5,…]` | (local in-process render of PNG frames) | visually verify an edit without a full render |
|
|
1534
|
-
| `vidfarm qa <dir\|composition.html> [--json] [--strict]` | (local static QA — **devcli-only**, no cloud/REST twin) | **social-native QA: HTML slop + font regime. Run it on EVERY video you produce.** Free, instant, feedback-only |
|
|
1965
|
+
| `vidfarm qa <dir\|composition.html> [--regime <name\|path>…] [--json] [--strict]` | (local static QA — **devcli-only**, no cloud/REST twin) | **social-native QA: HTML slop + first frame + font regime. Run it on EVERY video you produce.** `--regime` grades against a QA_REGIME.md too (stackable). Free, instant, feedback-only |
|
|
1966
|
+
| `vidfarm regime list\|show <name\|path>\|init <name> [--out <path>]` | (local — **devcli-only**) | QA_REGIME.md: the director's own per-style quality contract. `init` copies a bundled base to edit — the pattern for bulk/scripting mode |
|
|
1535
1967
|
| `vidfarm doctor` | (local environment triage) | check ffmpeg/node/keys/agent CLI/poisoned env + list local serve/preview processes before debugging anything else; `--kill-orphans` reaps dead servers squatting ports (fixes the "Waiting for preview server…" hang) |
|
|
1536
1968
|
| `vidfarm skills list\|add <name>\|update` | `GET /skill-pack/index.json` · `/skill-pack/:name/*` | install/refresh skill packs (see "Skill packs — import on demand") |
|
|
1537
1969
|
| `vidfarm tts "…" --engine local` / `vidfarm stt <file> --engine whisper` | (keyless LOCAL engines: Kokoro-82M TTS, whisper.cpp STT) | narration + word-timestamp transcripts with zero keys and zero accounts |
|
|
@@ -1548,13 +1980,17 @@ The licensed harness also carries the **generative build workflow** guidance (ch
|
|
|
1548
1980
|
## `vidfarm qa` — the social-native QA pass (devcli-only, heavily recommended)
|
|
1549
1981
|
|
|
1550
1982
|
```bash
|
|
1551
|
-
vidfarm qa ./work
|
|
1552
|
-
vidfarm qa ./work --json
|
|
1553
|
-
vidfarm qa ./work --strict
|
|
1983
|
+
vidfarm qa ./work # human-readable findings + verdict
|
|
1984
|
+
vidfarm qa ./work --json # machine-readable: rule / severity / where / fix
|
|
1985
|
+
vidfarm qa ./work --strict # ALSO exit 1 on slop (only if you want a CI gate)
|
|
1986
|
+
vidfarm qa ./work --regime hooks # + grade against a QA_REGIME.md (repeatable; also
|
|
1987
|
+
# auto-discovers ./work/QA_REGIME.md)
|
|
1554
1988
|
```
|
|
1555
1989
|
|
|
1556
1990
|
**Run this on every video you produce.** It is free, instant (pure DOM, no ffmpeg/Chrome/network), and it is the only automated check for the thing that most often ruins an agent-made video: **HTML slop**. Compositions are authored in HTML, so an agent's web-page instincts leak straight onto the frame as landing-page furniture that appears on every website and in **zero** real TikToks.
|
|
1557
1991
|
|
|
1992
|
+
It also judges **one frame on its own terms: t=0**, because that frame becomes the thumbnail every feed and share sheet freezes on (see `references/editor-workflows.md`, "The first frame is the thumbnail"). Pair it with `vidfarm stills ./work --at 0` — QA finds the structural cause, the still shows you the actual poster.
|
|
1993
|
+
|
|
1558
1994
|
**It is feedback, not a gate.** Default exit code is **0** even when it finds slop; nothing in the render or publish path calls it; it never runs automatically. `--strict` exists only if you deliberately want a CI failure. A finding you disagree with is fine to ignore and say so — it is a lint, not a verdict on the work.
|
|
1559
1995
|
|
|
1560
1996
|
**It is a BLOCKLIST, not an allowlist.** It names specific known-bad web patterns. Everything it doesn't name is legal, so a weird, ugly, hand-made, or wildly stylized composition passes untouched. It will never push your videos toward one house style — if it fires on a genuine creative choice, that's a bug in the rule, not in your video.
|
|
@@ -1573,9 +2009,14 @@ What it flags:
|
|
|
1573
2009
|
| `font-regime` | error/warn | A text layer in a website body font (Inter/Roboto/Arial/system-ui → **error**) or any family outside the imported regime (Montserrat, TikTok Sans, Abel, Source Code Pro, Yesteryear → warn, it silently falls back at render) |
|
|
1574
2010
|
| `font-size` / `font-weight` | error/warn | `font-size:0` (invisible) is an error; sub-2.6%-of-canvas-width text and weight <600 warn |
|
|
1575
2011
|
| `caption-safe-zone` | warn | Text outside the 8%–85% band on a **portrait** canvas (landscape/square are exempt) |
|
|
2012
|
+
| `thumbnail-blank-open` | error | Nothing on screen at **t=0** — the opening clip starts late, so the poster frame is black |
|
|
2013
|
+
| `thumbnail-fade-in` | error/warn | An **entrance** transition on the FIRST clip: `fade-black`/`fade-white`/`flash`/`smoke` → **error** (frame 0 is a flat solid); any other preset → warn (frame 0 caught mid-move). Junction transitions on later clips are never flagged |
|
|
2014
|
+
| `thumbnail-no-hook-text` | warn | The composition has text, but none of it is up at t=0 — the poster carries no hook words. Ignorable when you're deliberately opening on a clean face/product shot |
|
|
1576
2015
|
|
|
1577
2016
|
Every finding carries a concrete `fix` line — the answer is always "say it as timed text on the footage", never just "delete it". Fold `--json` into scripted batch runs to QA N variants at once.
|
|
1578
2017
|
|
|
2018
|
+
**The two halves, and why the tool only claims one.** Everything above is universal and mechanical. The half that decides whether a *particular* video is any good — is the hook legible cold, does the loop close, is this variant genuinely different from its siblings — is the director's, and it lives in a **`QA_REGIME.md`** (see "Scripting mode" above). Pass one with `--regime <name|path>` (repeatable, and a `QA_REGIME.md` sitting next to the composition is picked up automatically): its `checks:` front matter is settled deterministically alongside the built-ins, and its `- [ ]` checklist comes back as **review items you must answer yourself**. `vidfarm qa` deliberately never fakes a verdict on those — a "PASS" it couldn't have earned is worse than no check at all.
|
|
2019
|
+
|
|
1579
2020
|
## Cost mode — the devcli's money-saving guardrail
|
|
1580
2021
|
|
|
1581
2022
|
The four modes, quoted as **cost per finished video**. The first two are spend postures; the last two are also *production methods* — `rich-ai` buys reusable greenscreen raws and animates them in HTML, `pure-videogen` generates every frame:
|
|
@@ -1618,6 +2059,8 @@ vidfarm doctor # ✓/⚠/✗ for node, ffmpeg, hy
|
|
|
1618
2059
|
- **`ffmpeg`** — the media engine the devcli drives for every local media op: `remove-greenscreen --local` (chroma-key → transparent PNG/WebP/WebM, ProRes fallback where the local libvpx can't do WebM alpha), STT/caption demux, `raws scan` clip cutting, `stills`, audio trims. **Hard requirement** — `vidfarm doctor` exits non-zero without it.
|
|
1619
2060
|
- **`hyperframes`** — **Vidfarm's own render/animation engine, open-source under a whitelabel.** *Vidfarm renders ARE hyperframes renders.* The devcli bundles it and re-exposes everything it does locally as **`vidfarm hf <subcommand>`** — `vidfarm hf render <dir> -o out.mp4`, `capture`, `validate`, `preview`, `doctor`, plus the ONNX `remove-background` matting, Kokoro TTS, and Whisper STT engines the `tts`/`stt`/`remove-background` commands ride. Installing it standalone makes these run **natively** instead of a slower `npx -y hyperframes` fallback (which `vidfarm doctor` will warn about).
|
|
1620
2061
|
|
|
2062
|
+
**The local studio UI is whitelabeled automatically.** `hyperframes preview` (and `vidfarm hf preview`) serves a studio SPA whose stock shell paints an upstream "HeyGen · HyperFrames" header logo and a "HyperFrames Studio" tab title. Every devcli hyperframes spawn — and `vidfarm doctor` — idempotently patches that shell in place (`src/devcli/studio-brand.ts`) so the header reads **VidFarm**, the tab title is **VidFarm Studio**, and the product name in the studio's own copy (lint panel, feedback prompt, storyboard/agent prompts, clipboard writes) is rewritten. `.hyperframes/` paths and `data-hyperframes-*` DOM hooks are preserved verbatim — they name real files and hooks — and CLI verbs map to their twins (`hyperframes render` → `vidfarm render`). A fresh `npm install` restores the stock shell; the next devcli command (or `vidfarm doctor`) re-applies it. If a director ever screenshots upstream branding, run `vidfarm doctor`.
|
|
2063
|
+
|
|
1621
2064
|
**Treat `hyperframes` as Vidfarm** (it is — just whitelabeled): never point it at an external HeyGen/vendor account, publish service, or telemetry endpoint. Keep `HYPERFRAMES_SKIP_SKILLS=1` + `HYPERFRAMES_NO_TELEMETRY=1` set (the devcli sets these for its children); do not run `hyperframes auth`/`cloud`/`publish`/`play`/`feedback` — Vidfarm's own render/share/telemetry surfaces replace all of them. Everything else `hyperframes` can do locally is fair game and encouraged.
|
|
1622
2065
|
|
|
1623
2066
|
## Local dev loop (`vidfarm serve`)
|
|
@@ -1725,7 +2168,7 @@ The point of onboarding is to build **durable, reusable context** in My Files, n
|
|
|
1725
2168
|
1. **Product context** → **`OFFER.md`** (or `OFFER_<NAME>.md` — e.g. `OFFER_ACME_SKINCARE.md` — when the director runs more than one offer). Drive the interview questions with `brainstorm/coldstart` and **recommend the director save their answers into that file**, either locally next to their project or in cloud My Files. This is the durable artifact every later step reads back; `About.md` / `Interview.md` are legacy names — prefer `OFFER*.md` for anything new.
|
|
1726
2169
|
2. **Awareness level** (Eugene Schwartz — problem-aware, solution-unaware, …) → `awareness-levels.md`. If it's genuinely unknown after thinking it through, note that ads for **every** level should be made and tested. Use `brainstorm/awareness_stages`.
|
|
1727
2170
|
3. **Persuasive angles** → `persuasive-angles.md`, via `brainstorm/angles`.
|
|
1728
|
-
4. **Hooks** → `ad-hooks.md`, via `brainstorm/hooks`.
|
|
2171
|
+
4. **Hooks** → `ad-hooks.md`, via `brainstorm/hooks`. **Grade what comes back against `references/hooks-and-virality.md`** — the three gates, situation-vs-label, and the unguessable test — instead of shipping the raw list. And never rank a generated batch with the same reasoning that wrote it; the rubric catches defects, it doesn't pick winners.
|
|
1729
2172
|
5. **Brand assets & demos** — ask if they have logos/mascots/themes (suggest a `/brand-assets/` folder, e.g. `/brand-assets/logo.png`) or product demos / screen recordings (suggest a `/product-demos/` folder). `browse_files list` / `vidfarm files` first to see what they already uploaded; filenames should be descriptive and every asset worth finding later should get **notes** (`annotate-file` / `browse_files annotate`) so `files --search` works months from now. If they have a recurring character/mascot, set up its `/files/characters/<slug>/` trio now — `<character_id>.json` (e.g. `character_zara.json`) + `character_sprite_card.png` + `character_about.md` (see "Recurring characters are first-class").
|
|
1730
2173
|
6. **Budget** — ask roughly what they want to spend per video, and map it to the Cost spectrum (free reuse+local render → pennies for cloud render → ~$1 for some AI scenes → $10+ for heavy AI gen). This sets which approach you default to and whether AI **video** generation is on the table (ask permission before using it; image gen is cheap and fine). Budget can also be revisited per editor project.
|
|
1731
2174
|
7. **Recommend & adapt a template** — pair what you now know about the offer against the decomposed template catalog (`GET /discover/feed?q=<offer>`, read each result's `promotions`/`keywords`/`summary`), recommend the best 3-6, then fork and **modify** the winner to fit their offer. Prefer already-decomposed templates so the director skips the ~$0.10 decompose cost.
|
|
@@ -2123,7 +2566,7 @@ Use this when the user wants to keep the format’s feel but replace the subject
|
|
|
2123
2566
|
1. Read `references/editor-workflows.md`.
|
|
2124
2567
|
2. Fetch `video-context.json` and `editor-harness.json` first.
|
|
2125
2568
|
3. State the plan in the three axes vocabulary: scenes, audio, text; SWAP vs REPLACE for each.
|
|
2126
|
-
4. Preserve the hook structure, cut rhythm, emotional punch, and important scenes flagged by the harness.
|
|
2569
|
+
4. Preserve the hook structure, cut rhythm, emotional punch, and important scenes flagged by the harness. **Read `references/hooks-and-virality.md` before rewriting the opening line or the retention beat** — `viral_dna.hook`/`retention`/`payoff` name the source's four charges, and rebuilding each one for the new subject (rather than flattening the loop into a product statement) is the whole job.
|
|
2127
2570
|
5. Source footage in this order:
|
|
2128
2571
|
- My Files or existing raws
|
|
2129
2572
|
- a raws hunt from a long-form source
|
|
@@ -2142,8 +2585,8 @@ Use this when a coding agent is doing the work locally or the user wants a repro
|
|
|
2142
2585
|
2. Run `vidfarm pull <forkId> --dir ./work`.
|
|
2143
2586
|
3. Read `./work/.harness/agent-guide.md` and `./work/.harness/context.json` before editing.
|
|
2144
2587
|
4. Make deterministic edits to `composition.html` and optionally `composition.json`.
|
|
2145
|
-
5. Validate with `vidfarm lint` or `vidfarm stills` when useful.
|
|
2146
|
-
6. **QA before you render: `vidfarm qa ./work`.** Free, instant, devcli-only. It blocklists HTML slop (CTA buttons, benefit chip rows, frosted cards, gradient text, web-page classes/fonts)
|
|
2588
|
+
5. Validate with `vidfarm lint` or `vidfarm stills` when useful. **Always look at `vidfarm stills ./work --at 0`** — that frame becomes the thumbnail, so it must not be black, empty, or mid-fade.
|
|
2589
|
+
6. **QA before you render: `vidfarm qa ./work`.** Free, instant, devcli-only. It blocklists HTML slop (CTA buttons, benefit chip rows, frosted cards, gradient text, web-page classes/fonts), checks the caption font regime + safe zone, and flags a blank/fading first frame (the thumbnail). Feedback only — exit 0 even on findings, never automatic — but it catches the #1 tell of an agent-made video, so run it on every production. Fix what's real, ignore what's a deliberate style call, then render.
|
|
2147
2590
|
7. Render with `vidfarm render <forkId> --dir ./work --wait`.
|
|
2148
2591
|
8. Approve the finished MP4 with `vidfarm approve --video <url|./final.mp4> --caption "..."`. This prints the shareable `share_url`.
|
|
2149
2592
|
|
|
@@ -2151,6 +2594,72 @@ Use this when a coding agent is doing the work locally or the user wants a repro
|
|
|
2151
2594
|
|
|
2152
2595
|
Prefer this path for batch work, CI-like edits, or when the user wants free local rendering through `vidfarm serve`.
|
|
2153
2596
|
|
|
2597
|
+
## Recipe: Bulk Video Generation (Scripting Mode) with a QA_REGIME.md
|
|
2598
|
+
|
|
2599
|
+
Use this when the director wants **volume** — daily posting, hook tests, one video per clip in a pool, N variants of a template. Ask first if you're not sure: *"One video, or should we set this up as a repeatable batch?"* If they want volume, this is the shape.
|
|
2600
|
+
|
|
2601
|
+
The thing that makes bulk work is not the loop — loops are easy. It's that **nobody is going to watch variant #37 as carefully as variant #1**, so the standard has to be written down before the loop runs. That's the `QA_REGIME.md`.
|
|
2602
|
+
|
|
2603
|
+
### 0. Read the craft harness first
|
|
2604
|
+
|
|
2605
|
+
`references/hooks-and-virality.md` — the four charges (hook / loop / payoff / bait), the three gates, and the anti-patterns that only bite at volume. Two of them decide whether this batch is worth running at all: **a different noun is not a different hook** (twenty variants of one sentence with the nouns swapped is one video), and **never point a generator at your grader** (a model writing hooks scored by the same model converges on the rubric, not on what works — scores climb, nothing improves). The regime catches defects; it does not rank winners.
|
|
2606
|
+
|
|
2607
|
+
### 1. Agree the variant axis — before any code
|
|
2608
|
+
|
|
2609
|
+
One thing changes per variant; everything else is held constant. Hook line, opening clip, opening pain, avatar — pick one and say so out loud. A batch where several things changed at once teaches the director nothing, which defeats the point of running a batch instead of making one good video. **The cluster is the unit, not the video**: one attribution `tracer` per cluster, so a bad audience can't be mistaken for a bad hook.
|
|
2610
|
+
|
|
2611
|
+
### 2. Pin the base
|
|
2612
|
+
|
|
2613
|
+
```bash
|
|
2614
|
+
vidfarm pull <forkId> --dir ./work # one canonical base fork per batch
|
|
2615
|
+
```
|
|
2616
|
+
|
|
2617
|
+
Read `./work/.harness/agent-guide.md` first, as always.
|
|
2618
|
+
|
|
2619
|
+
### 3. Install and EDIT the regime
|
|
2620
|
+
|
|
2621
|
+
```bash
|
|
2622
|
+
vidfarm regime list # short-form | hooks | ugc-testimonial | explainer | product-demo
|
|
2623
|
+
vidfarm regime init hooks --out ./work/QA_REGIME.md
|
|
2624
|
+
```
|
|
2625
|
+
|
|
2626
|
+
Then **edit it with the director**. The bundled file is a starting point; the parts that matter are the ones they add — who the viewer is, their banned vocabulary, the compliance line, the pacing this account actually uses. A regime nobody edited isn't about their videos. Existing regime somewhere else on disk? Just point at it: `--regime ./brand/HOUSE_RULES.md`. They stack.
|
|
2627
|
+
|
|
2628
|
+
### 4. Source the N cheaply
|
|
2629
|
+
|
|
2630
|
+
```bash
|
|
2631
|
+
vidfarm public-raws --categories --json # live shelf vocabulary
|
|
2632
|
+
vidfarm public-raws --category scroll-stoppers --limit 20 --json > pool.json
|
|
2633
|
+
```
|
|
2634
|
+
|
|
2635
|
+
A curated shelf is a pre-tagged, free, already-hosted clip pool — the cheapest way to get N distinct variants without N downloads or N generation calls.
|
|
2636
|
+
|
|
2637
|
+
### 5. Loop: edit → QA against the regime → render
|
|
2638
|
+
|
|
2639
|
+
```bash
|
|
2640
|
+
for VARIANT in "${VARIANTS[@]}"; do
|
|
2641
|
+
SLUG="$(echo "$VARIANT" | tr ' ' '-' | cut -c1-40)"
|
|
2642
|
+
vidfarm set-text ./work --layer hook --text "$VARIANT"
|
|
2643
|
+
vidfarm qa ./work --json > "qa/$SLUG.json" # ./work/QA_REGIME.md auto-discovered
|
|
2644
|
+
jq -e '.ok' "qa/$SLUG.json" >/dev/null || { echo "skipped $SLUG"; continue; }
|
|
2645
|
+
vidfarm render "$FORK_ID" --dir ./work --out "renders/$SLUG.mp4" --tracer "batch-$SLUG"
|
|
2646
|
+
done
|
|
2647
|
+
```
|
|
2648
|
+
|
|
2649
|
+
`vidfarm qa` still exits 0 on findings — the `jq -e` line is **your** gate, in your script, made explicit. Keep it that way; a hard gate inside the tool would quietly train the loop to stop trying anything unusual.
|
|
2650
|
+
|
|
2651
|
+
### 6. Answer the review items — don't skip this
|
|
2652
|
+
|
|
2653
|
+
The regime's `- [ ]` checklist comes back on every run because the CLI *can't* settle it. Machine checks catch a 13-word hook or a black first frame; only you can answer "is this variant genuinely different from its siblings?" or "can the viewer guess the withheld answer?" **Report both halves honestly**: what the machine checked, and what you judged. A batch report claiming a clean pass on the judgment half is worse than no report.
|
|
2654
|
+
|
|
2655
|
+
### 7. Feed what you learn back into the regime
|
|
2656
|
+
|
|
2657
|
+
When the director says "the label-framed hooks all died" or "anything over 30s tanked", write it into `QA_REGIME.md` as a rule or a checklist line — with the reason attached, so the next agent doesn't argue it away. The compositions are disposable; **the regime is the artifact that compounds across batches.**
|
|
2658
|
+
|
|
2659
|
+
### Cost note
|
|
2660
|
+
|
|
2661
|
+
Per-video AI spend multiplies by N. Bulk belongs in `minimize`/`hybrid` cost mode with reused assets: clip pools and HTML/CSS motion over a fixed asset set, not per-variant generation. Generate a reusable element once, then vary text and media around it.
|
|
2662
|
+
|
|
2154
2663
|
## Recipe: Cutout graphics for explainer videos
|
|
2155
2664
|
|
|
2156
2665
|
Use this when the director wants an **explainer** — a subject "on stage" while labels, arrows, icons, and props pop in around it — and the pieces should be **transparent cutout stickers** you can scale, place, and animate freely. This is the cheap workhorse for explainers: generate a reusable graphic **once**, cut it out, and reuse/animate it. Everything except the optional AI generation runs **locally and free** through `ffmpeg` (no wallet, no account).
|
|
@@ -2175,11 +2684,77 @@ The mechanical trio — **generate on a chroma plate → key it out → trim to
|
|
|
2175
2684
|
|
|
2176
2685
|
**In cost-saving mode, don't generate illustrations at all — mask them out of images the director already has.** If `vidfarm cost-mode` is `minimize` (or the director says "without burning credits"), the default for adding an illustration is `vidfarm mask <their-image> --crop …` — lifting art out of an infographic, poster, deck slide, brand sheet, or screenshot for **$0 and zero AI calls**. Ask for source art before you ask for a generation budget; the guided loop is **"Mask from an image you already have"** below. **If no source art exists and the graphic must be custom, you still don't have to spend** — hand the director a prompt for a **free** image generator (meta.ai / free ChatGPT / a Hugging Face Space) and cut the returned sheet into stickers locally: **"Free manual image-gen"** below.
|
|
2177
2686
|
|
|
2687
|
+
### "A sticker pack" — what it means, and the one command for it
|
|
2688
|
+
|
|
2689
|
+
**When a director asks for a sticker pack (or "a set of stickers", "a prop pack", "an icon set", "assets for this video"), that is a specific pipeline, not N separate cutouts:**
|
|
2690
|
+
|
|
2691
|
+
> **Generate ONE image containing every item, laid out on a flat greenscreen plate → key that plate out once → mask each item apart into its own transparent file.**
|
|
2692
|
+
|
|
2693
|
+
Do it that way every time, for two reasons: **cost** (one billed image job for the whole set instead of one per sticker) and, more importantly, **style consistency** — items drawn in the same image share line weight, palette, perspective and lighting, which N independent generations never do.
|
|
2694
|
+
|
|
2695
|
+
`vidfarm sticker-pack` is that entire loop in one command. It also does the part that used to be manual: **finding the items**. Instead of measuring a `--crop x,y,w,h` rect per item by eye, it segments the keyed sheet's **alpha channel** into connected islands of opaque pixels — one island per item — and cuts each one out, trimmed to its true bounding box.
|
|
2696
|
+
|
|
2697
|
+
```
|
|
2698
|
+
# Generate the sheet AND split it (one image job for the whole pack)
|
|
2699
|
+
vidfarm sticker-pack --generate "cozy farm props, flat vector, 2-3 flat colors" \
|
|
2700
|
+
--items "red barn,tractor,hay bale,sun,fence,rolling hills landscape" \
|
|
2701
|
+
--out-dir ./stickers
|
|
2702
|
+
|
|
2703
|
+
# Or split a greenscreen sheet you already have — $0, no account
|
|
2704
|
+
vidfarm sticker-pack ./sheet.png --items "barn,tractor,hay bale" --out-dir ./stickers
|
|
2705
|
+
|
|
2706
|
+
# Look before you cut: prints the detected boxes, writes nothing
|
|
2707
|
+
vidfarm sticker-pack ./sheet.png --dry-run
|
|
2708
|
+
```
|
|
2709
|
+
|
|
2710
|
+
Out comes `./stickers/sticker-01-red-barn.png`, `sticker-02-tractor.png`, … each trimmed tight, plus **`stickers.json`** (name, file, pixel size, sheet crop, area share) so a later step — or another agent — can pick props by name without re-deriving anything.
|
|
2711
|
+
|
|
2712
|
+
**Knobs that matter:**
|
|
2713
|
+
|
|
2714
|
+
- `--items "a,b,c"` — the exact cast. It both steers the generation and **names the outputs** in reading order (top-to-bottom rows, left-to-right). `--count <n>` instead if you don't care what the items are.
|
|
2715
|
+
- `--dry-run` — list the detected crop boxes and stop. Cheap sanity check before writing a dozen files.
|
|
2716
|
+
- `--gap <pct>` (default 1.2) — how far apart two islands must be to count as separate items. **Two items came out as one sticker → lower it** (or ask for wider spacing on the sheet). **One item came out split in two → raise it** (its parts, e.g. a floating antenna or a dotted arrow, weren't bridged).
|
|
2717
|
+
- `--min-area <pct>` (default 0.15) — drops key speckle. There is **no maximum** — see the size note below.
|
|
2718
|
+
- `--preset`/`--key-color` — match the plate (default `#00FF00`). `--pad`, `--alpha-threshold`, `--output-format png|webp|gif`, `--prefix`, `--max-items`, `--keep-plate`/`--keep-sheet` behave like `cutout`'s.
|
|
2719
|
+
- Stubborn item? Fall back to one hand-measured `vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00"` for that one; the rest of the pack still comes from `sticker-pack`.
|
|
2720
|
+
|
|
2721
|
+
**Pick a plate the ART doesn't use — this is the #1 way a pack comes out broken.** A chroma key removes exactly ONE color. A sticker pack of leaves, frogs, plants, money, or "eco" iconography on a GREEN plate keys holes straight through the artwork, and it looks like the cut failed when really the plate was wrong. The commands handle it two ways:
|
|
2722
|
+
|
|
2723
|
+
- **Generating?** `sticker-pack --generate` / `cutout --generate` read the subject text and move the plate off any hue it mentions — green (`#00FF00`) → magenta (`#FF00FF`) → blue (`#0047BB`) → black → white — then print which plate they chose and why. Say it back to the director when it moves: *"Your items are mostly green, so I generated them on a magenta plate."*
|
|
2724
|
+
- **Splitting a sheet you already have?** The plate is **detected from the sheet's four corners** (a sticker sheet always has plate in the corners), so a red/purple/blue sheet handed back from a free web generator just works with no flags.
|
|
2725
|
+
|
|
2726
|
+
Override whenever you know better: `--key-color "#FF00FF"` or `--preset magenta` pins the plate (and turns the auto-pick off); `--no-auto-key` forces plain green. If a pack's palette collides with *every* standard plate — a rainbow icon set — split it across two sheets on two different plates rather than fighting `--tolerance`. And when a sticker is a *green* subject, never let the user generate it on green: the hand-off brief (`vidfarm handoff image`) already picks the plate for them and carries the matching `--key-color` into the follow-up command.
|
|
2727
|
+
|
|
2728
|
+
**Generation is the failure point, not the cutting.** The sheet prompt is auto-appended with the important instruction — *every item fully separated by clear plate-colored background, nothing touching or overlapping, wide margins, no text, no shadows, one consistent style* — because **touching items segment as one sticker**. If a pack comes back merged, re-run the generation asking for more spacing before you fight the `--gap` knob.
|
|
2729
|
+
|
|
2730
|
+
### Stickers are not necessarily small — and they usually move
|
|
2731
|
+
|
|
2732
|
+
Two things directors assume that aren't true:
|
|
2733
|
+
|
|
2734
|
+
- **A sticker can be huge.** "Sticker" here means *a transparent element you can place, scale and animate independently* — nothing about size. A full-width **landscape, skyline, cityscape, room interior, or backdrop** is a perfectly good sticker, and belongs in the same sheet as the icons so it shares their style. `sticker-pack` has no maximum item size (only a speckle floor), so a background plate that covers 60% of the sheet cuts out just like a 3% icon does. Ask for the big pieces explicitly in `--items` (e.g. `"rolling hills landscape (wide)"`).
|
|
2735
|
+
- **A sticker is normally animated, not pasted.** The default animation surface is the **canvas/HTML itself**: place the PNG as an image layer, then animate it with `vidfarm keyframes` presets — `pop-in`, `float`, `shake`, `grow`, `slide-in-left`, `drift` — or raw keyframe stops. That's CSS/declarative motion, deterministic, free, and identical in preview and final render, and it's what gives an explainer its life. Layer moves for realism: a callout that `pop-in`s and then `float`s idle; an arrow that `slide-in-left`s and then `shake`s at its target. Full vocabulary: `references/editor-workflows.md` → "Cutout graphics for explainers".
|
|
2736
|
+
- **When the sticker's OWN artwork moves** (a waving mascot, a spinning coin, a looping animation), it isn't a still at all — there's no single bounding box to trim to. Key it as a clip: `vidfarm remove-greenscreen <video>` → **transparent WebM** (browser- and editor-playable; a ProRes `.mov` alpha master is the local fallback when ffmpeg can't encode WebM alpha, and `--cloud` always returns WebM).
|
|
2737
|
+
|
|
2738
|
+
**Transparent GIF stickers.** Some destinations only take GIF — chat apps, forums, Notion, older web overlays, sticker packs meant to be shared rather than rendered. Both paths can emit one:
|
|
2739
|
+
|
|
2740
|
+
```
|
|
2741
|
+
vidfarm sticker-pack ./sheet.png --output-format gif --out-dir ./stickers # still stickers as GIF
|
|
2742
|
+
vidfarm remove-greenscreen ./mascot.mp4 --gif --gif-fps 12 --gif-width 480 # ANIMATED transparent GIF
|
|
2743
|
+
```
|
|
2744
|
+
|
|
2745
|
+
GIF alpha is **1-bit** — a pixel is fully opaque or fully gone, so antialiased edges go hard and semi-transparent shadows/glows disappear (`--gif-alpha <0..255>` moves where that line falls). That's the format, not the key. **For anything going onto a composition, prefer PNG/WebP (still) or transparent WebM (clip);** reach for GIF only when the destination demands it.
|
|
2746
|
+
|
|
2178
2747
|
### The guided sequence (prompt harness)
|
|
2179
2748
|
|
|
2180
|
-
**Step 0 — Decide the cast of stickers.** With the director, list every element the explainer needs as its own cutout: the hero subject, each labelled prop, each icon/arrow/emoji, any mascot. Each becomes one transparent PNG. Stickers are reusable — generate once, reuse across scenes.
|
|
2749
|
+
**Step 0 — Decide the cast of stickers.** With the director, list every element the explainer needs as its own cutout: the hero subject, each labelled prop, each icon/arrow/emoji, any mascot, any full-frame backdrop. Each becomes one transparent PNG. Stickers are reusable — generate once, reuse across scenes. **If the cast is more than two or three items, make it a PACK** (one sheet, split locally — see "A sticker pack" above) rather than N separate `cutout` calls.
|
|
2750
|
+
|
|
2751
|
+
**Step 1 — Make each cutout.** Four paths, cheapest first:
|
|
2181
2752
|
|
|
2182
|
-
**
|
|
2753
|
+
- **Cut a whole pack out of ONE sheet** (`vidfarm sticker-pack`) — the default for a *set*, and the only path that keeps a cast on-style. See "A sticker pack" above.
|
|
2754
|
+
```
|
|
2755
|
+
vidfarm sticker-pack --generate "<theme>, flat vector" --items "a,b,c,d" --out-dir ./stickers
|
|
2756
|
+
vidfarm sticker-pack ./sheet.png --out-dir ./stickers # free, sheet you already have
|
|
2757
|
+
```
|
|
2183
2758
|
|
|
2184
2759
|
- **Mask it out of a source image the director already has** (FREE, no account, no generation) — *the cheapest path, and the one to prefer whenever source art exists.* If the director hands you one image with the art already in it — an infographic, a poster, a marketing graphic, a brand illustration, a screenshot — lift each illustration straight out of it with `vidfarm mask`:
|
|
2185
2760
|
```
|
|
@@ -2250,10 +2825,12 @@ This is the biggest cost saver in the whole explainer workflow: **if the directo
|
|
|
2250
2825
|
|
|
2251
2826
|
Everything here is **local and free** — crop, matte/key, and trim are all `ffmpeg` + the bundled ONNX engine. No wallet, no account, no generation call.
|
|
2252
2827
|
|
|
2253
|
-
### Free manual image-gen (the $0 path when NO source art exists)
|
|
2828
|
+
### Free manual image-gen (the $0 path when NO source art exists — and the DEFAULT in interactive mode)
|
|
2254
2829
|
|
|
2255
2830
|
`mask` needs art that already exists. When the explainer needs a **custom** graphic and the director is in `cost-mode minimize` (or just said "don't spend"), there's still a $0 path: **you write the prompt, the director runs it in a free image generator, they hand the image back, you cut it up locally.** The AI spend moves to a free third-party tool; Vidfarm's wallet is never touched.
|
|
2256
2831
|
|
|
2832
|
+
**This is what `vidfarm mode interactive` means in practice** — and `vidfarm handoff image --theme "<what>" --items "a,b,c"` mints the whole brief for you (the prompt, the numbered steps, the free tools, the plate color, and the follow-up `sticker-pack` command with the matching `--key-color`). Don't hand-write these packets; print the brief.
|
|
2833
|
+
|
|
2257
2834
|
**Offer it once, then make it the default.** The first time custom art comes up in a cost-saving session, ask: *"Want me to default to free manual image generation — I write the prompt, you paste it into a free tool and drop the PNG back — or should I spend credits on `vidfarm generate`?"* Whatever they pick, hold it for the session (and offer to remember it next to the cost mode). After that, don't re-ask per graphic — just hand over the next prompt.
|
|
2258
2835
|
|
|
2259
2836
|
**Free generators to name:** <https://meta.ai>, free-tier **ChatGPT**, or a free image **Hugging Face Space** (<https://huggingface.co/spaces>, search "flux" / "sdxl"). Let the director use whichever they're already logged into.
|
|
@@ -2268,12 +2845,15 @@ Everything here is **local and free** — crop, matte/key, and trim are all `ffm
|
|
|
2268
2845
|
|
|
2269
2846
|
**No text in the image, ever** — rendered text never keys or scales cleanly; add copy as a caption or HTML layer instead.
|
|
2270
2847
|
3. **Bring the sheet in:** `vidfarm put-file ./sheet.png` (or drag into **My Files** in the web app).
|
|
2271
|
-
4. **Split it into stickers — one
|
|
2848
|
+
4. **Split it into stickers — one command, all free:**
|
|
2849
|
+
```
|
|
2850
|
+
vidfarm sticker-pack ./sheet.png --items "<object one>,<object two>,…" --out-dir ./stickers
|
|
2851
|
+
```
|
|
2852
|
+
It keys the flat green, finds each object in the alpha channel, and writes one trimmed transparent sticker per object (named from `--items`) plus `stickers.json`. Run it with `--dry-run` first to check the boxes. Two objects merged into one? The generator drew them too close — lower `--gap`, or grab that pair by hand:
|
|
2272
2853
|
```
|
|
2273
|
-
vidfarm mask ./sheet.png --crop 4%,4%,29%,45%
|
|
2274
|
-
vidfarm mask ./sheet.png --crop 36%,4%,29%,45% --flat "#00FF00" --out prop-b.png
|
|
2854
|
+
vidfarm mask ./sheet.png --crop 4%,4%,29%,45% --flat "#00FF00" --out prop-a.png
|
|
2275
2855
|
```
|
|
2276
|
-
|
|
2856
|
+
If the generator ignored the background instruction and returned white/busy art, `vidfarm mask` **without** `--flat` falls back to local ONNX matting — same command, no re-prompt needed.
|
|
2277
2857
|
5. **Stage, animate, render** exactly as in Steps 3–5 above. A manually-generated sticker is indistinguishable from a `cutout --generate` one on the timeline.
|
|
2278
2858
|
|
|
2279
2859
|
**Grid sizing note:** a 3×2 grid on a square image puts each cell at roughly `33%` wide × `50%` tall; start crops a couple of percent inside the cell and use `--keep-region` on the first run to verify before masking the rest.
|