Analyze video → reusable prompting template skill

Analyze video → reusable prompting template — Poster frame: a crumpled plastic bottle with sad eyes lying in a wet gutter under a street lamp (from the novoads/agent-skills README)

Illustration · not a resultFrom the novoads/agent-skills README — shows the whole collection, not only this skill.source ↗

Analyze a reference video and reverse-engineer its style into a reusable Seedance 2.0 prompting template for the Novoads API.

by novoads·MIT license·★ 23 Stars on the repo·GitHub ↗

Use now
Analyze video → reusable prompting template — Poster frame: a crumpled plastic bottle with sad eyes lying in a wet gutter under a street lamp (from the novoads/agent-skills README)

Illustration · not a resultFrom the novoads/agent-skills README — shows the whole collection, not only this skill.view on GitHub

Files of Analyze video → reusable prompting template

novoads/main1 file shown
SKILL.md
Show the full text511 lines

Analyze video → reusable prompting template

Someone found a video style they love. Your job is to deconstruct it into a reusable prompting template — a formula they can plug any product, person or setting into and get that same style back from seedance-2.0.

The output is not a single prompt. It is a formula file saved into skills/novoads-api/prompting/prompt-library/, built the way seedance-2-ugc.md is built: layers, variables, option banks, rules, and a worked example, so the agent can generate unlimited prompts in that style.

Nothing in steps 1 to 6 touches the API. Frame extraction, transcription and analysis are local work on the user's own file: no calls, no credits. Only step 7, the optional test render, spends anything, and it runs through the two gates in SKILL.md like every other generation.

There is a hosted alternative, and it is not the default. POST /v1/analyses reads an uploaded ad into a structured breakdown (hook boundary, beat timeline, on-screen text, casting, layer zones) in one synchronous call, at a flat fee per analysis that does not move with length. Price it through POST /v1/estimates with {"kind":"analysis"} first. Two reasons it is the fallback and not the front door here: the local path costs nothing and reads the whole runtime, while /analyses defaults to the first 20 seconds (120 is the ceiling, and raising it trades hook precision for coverage). Reach for it when ffmpeg is not installed, when the user wants zone labels rather than prose, or when the frame read has already failed. Offer it with the price attached and let the user choose; never substitute it silently for step 1.

The 15-second constraint is the whole design problem. seedance-2.0 and seedance-2.0-mini take any integer 4 to 15 seconds, and the reference video is usually 30 to 60. Your template has to distil the style into what fits one 15-second clip, and say how to recreate the longer arc across a series of clips.

Dependencies

  • ffmpeg / ffprobe — required for frame and audio extraction (step 1). brew install ffmpeg on macOS.
  • whisper — optional, for transcription (step 2). pip3 install openai-whisper. If it is unavailable, ask the user to paste the dialogue instead.

Neither is a Novoads dependency. Both run locally on the user's file.

REST key required. A Novoads MCP connector is not a substitute. If NOVOADS_API_KEY is missing or still the placeholder, stop before any generation work and tell the user: "Before continuing, create an API key at https://novoads.ai/dashboard/settings?tab=api and paste it into .env." That holds even when mcp__novoads__* tools are connected and authenticated in the session. Never call mcp__novoads__* tools from this repo's workflows: they are a different surface with different behavior, including the units they quote costs in. Repo installs verify with ./scripts/check-novoads-env.sh; a solo install checks NOVOADS_API_KEY in the environment.

Pack version. Every /v1 response carries X-Novoads-Pack-Version; mention a newer pack at https://github.com/novoads/agent-skills only when that header names a version NEWER than this file's metadata.packVersion — equal or older is nothing to say, and it is never a reason to stop.

Inputs

  • Video file (required): path to .mp4, .mov, .webm or similar.
  • Style name (optional): what to call the template — car-review, unboxing-hype, skeptic-converted. If they do not give one, name it from what you observe.

Step 1: Extract frames and audio

bash "skills/analyze-video/scripts/extract-frames.sh" \
  "<video_path>" "/tmp/video-analysis" <num_frames>

Frame count by source duration:

Source duration Frames
Under 10s 8
10–20s 12
20–30s 16
Over 30s 20

Read metadata.txt for duration, resolution and fps.

Step 2: Transcribe the audio

Try, in order:

  1. whisper CLI: whisper /tmp/video-analysis/audio.wav --model base --output_format txt --output_dir /tmp/video-analysis
  2. Python whisper inline.
  3. Neither available → ask the user for the dialogue, or to install whisper.

The transcript is where pacing lives: speech rhythm, filler words, how dialogue interleaves with action. All of that defines the style, and all of it has to survive into the template.

Step 3: Study the reference templates

Read the shipped formulas before you write one. They are the standard your output has to meet:

And the platform guide, which every one of them defers to:

  • seedance-2.md — request fields, the grid, prompt craft, what the estimate flags, the adaptation checklist

Notice what they share:

  • They name layers — the structural building blocks of the style.
  • Each layer has a pattern: a repeatable sentence shape with {{VARIABLES}}.
  • Variables come with option banks, not blanks.
  • They state rules that explain why a choice matters.
  • They declare a mode — startImageAssetId or referenceAssetIds — at the top.
  • They carry a worked example that has been priced live.

Your template has to hit that depth to be usable.

Step 4: Analyze the frames — find what defines this style

Read all the extracted frames. You are not describing one video; you are isolating the transferable pattern.

For every dimension ask: "is this specific to THIS VIDEO — the person, the product, the room — or is it THE STYLE?" Only the style goes into the template. The specifics become variables.

Structure and pacing
  • How long is the source? How many distinct beats?
  • Which 2–3 beats are essential — the ones without which it stops being this style?
  • What is the arc? Hook → demo → proof → verdict, or something else?
  • Fast cuts or held shots? How long is each beat?
  • Silent beats, or wall-to-wall dialogue?
  • The compression question: if you had to carry the whole feel in 3 beats and 2–3 spoken lines, which moments survive?
Camera and framing
  • Filming perspective: selfie, propped phone, second operator, screen recording?
  • How does framing change between beats — tighter, wider, same angle throughout?
  • Is there a signature move that is the style?
Edit style
  • Jump cuts, continuous take, time-lapse, split screen?
  • Transitions: hard cuts, dissolves, text?
  • Recurring motifs: close-up product inserts, reaction face, before/after?
Dialogue and script structure
  • Hook format: question, bold claim, mid-action, reaction?
  • Scripted, improvised, voiceover, text-on-screen?
  • Speech patterns that carry the tone: filler words, sentence length, vocabulary.
  • How lines relate to what the hands are doing.
Tone and energy
  • 3–4 emotion words for the vibe.
  • Energy arc: builds, flat, peaks then drops?
  • Relationship to the viewer: friend, expert, skeptic, fan?
Lighting and technical quality
  • Light source and direction: natural, ring light, moody, blown out?
  • Phone or polished? Which technical "flaws" are load-bearing?
  • Audio character: phone mic, lapel, voiceover, room tone?
What makes this style DIFFERENT

The most important pass. After cataloguing everything above, name the 2–3 things that separate this from a generic UGC clip or a generic product review — the pacing, the hook format, the way the product enters frame, the edit rhythm. Those become the core of the template; everything else is scaffolding.

The 15-second plan

Before building anything, map the style onto one clip:

  • What is the minimum viable version? Which beats are essential, which are nice to have. Fifteen seconds has to carry the thing that makes someone say "oh, that kind of video".
  • Does it need a series? If the power is in a narrative arc or a feature rundown, it needs 2–3 clips. If it is a vibe or a single moment, one clip is enough.
  • How many spoken lines fit? Delivery measures 2.0 words per second (~13 characters a second, spaces included) after ~0.5s of leading silence. Fifteen seconds is 2–3 short sentences, and the slack is what leaves room for a silent beat. Count the source's lines and keep the ones carrying the voice.
  • What is the beat skeleton? 15 seconds is 2–3 beats: hook → core moment → kicker.
Which mode the style wants

Every formula in this library declares one, because they are mutually exclusive on the API and the choice follows from the style:

If the style… Mode Why
opens on the product itself, held up or sitting on a surface startImageAssetId the product photo is literally the first frame
builds a scene the product was never photographed in — a void, a splash, a studio set referenceAssetIds the model has to composite it, not animate a flat photo
holds one person across a series of clips referenceAssetIds @Image1 the product, @Image2 the person — Seedance re-casts on every cut, and a repeated description does not hold a face

Write the answer into the template. A formula that leaves the mode open produces prompts whose @Image1 tokens point at nothing.

Step 5: Build the template

Create a self-contained markdown file. Someone should be able to read it and write prompts in this style without ever seeing the source video.

Template structure
# [Style name] — Seedance 2.0

**Use when:** [the kind of video this produces]

**Model guide:** read [seedance-2.md](../novoads-api/prompting/prompt-library/seedance-2.md) first for the request fields, the grid,
and the platform rules.

**Mode:** `startImageAssetId` or `referenceAssetIds` — say which, and what each `@ImageN`
slot holds. The two are separate modes and a body carrying both is a `400`.

## What defines this style

[2–3 paragraphs of theory. This is what lets a prompt writer make good variable choices
instead of filling blanks.]

## Things to know before you write a word

[The route-specific traps. If the style speaks, say that the line is rendered and
lip-synced in this same call and that gate 1 applies. If it is silent, say that silence has
to be declared in the prose.]
mandatory.]

## The structure

[The layers of THIS style. Do not force-fit the 9-layer UGC model. Five layers, twelve
layers — let the video decide.]

## Layer-by-layer formula

### Layer N: [Name]

[What this layer does and why it matters here.]

**Pattern:**
\```
[The repeatable sentence shape with {{VARIABLES}}]
\```

| Variable | Options | Notes |
|---|---|---|
| `VARIABLE_NAME` | option 1, option 2, option 3 | [guidance] |

### [... more layers ...]

## Beat structure (one 15-second clip)

[The 3-beat framework: hook, core, kicker. Which beats speak and which are silent. Two to
three spoken lines total.]

## Multi-clip strategy (if applicable)

[How to split across 2–3 clips, what each one covers, and what holds identity across them:
the same `referenceAssetIds` in every call plus the actor tag repeated verbatim.]

## Tone and pacing guide

[Energy, speech patterns, rhythm, with a pacing-cue bank specific to this style.]

## Technical specs

[Lighting, camera quality, audio character — including the flaws that make it authentic.]

## Complete template

[One copy-paste block for ONE 15-second clip, every variable marked {{PLACEHOLDER}}.
Max 3 beats, max 2–3 spoken lines. This is the unit.]

## Example prompt

[The template filled in for a DIFFERENT product, person and setting than the source video.
Price it at POST /v1/estimates before shipping it.]

## Adaptation checklist

[The style-specific checks, then the standard ones from seedance-2.md.]

## Generating from this template

[The call sequence — see step 7 of analyze-video for the block to paste here.]
Rules the template has to teach

These are not style preferences. Each one is a defect found in a shipped formula, and a template that omits them manufactures the same defect in every prompt written from it.

  • Every prompt is one clip of 4 to 15 seconds. If the style needs more, the template ships a multi-clip strategy — never a longer prompt.

  • Keep prompts between 100 and 260 words. Shorter prompts produce vague results; longer ones overwhelm the model and cause it to lose focus on key details. Do not pad to hit the floor and do not cut a beat's framing to stay under the ceiling.

  • Variables are curated choices, not blanks. "Any lighting" is useless. "Natural window light, overhead kitchen light, golden-hour balcony light" is a decision the writer can actually make.

  • Declare the mode and address references as @Image1, @Image2, … The tokens resolve positionally against referenceAssetIds in the order the array is sent, and a token pointing past the end of the array is refused before the charge. If the style uses a start frame instead, say so and use no tokens at all.

  • If the style is silent, say so in prose — a silent product film with no spoken dialogue, silent b-roll. Seedance renders audio from the prompt, so a film that never declares silence can come back with an invented voice on it. Do not put on-screen text in double quotes and call it done — the model reads a quoted string as a line to speak.

  • No forbidden words: cinematic, professional, stunning, 8k, studio, perfect. Nothing on the API rejects or reports them — this is craft advice, and the reason to drop them is the render. Replace one with the real thing: the light source, the surface, the flaw.

  • Prose, never a bulleted prompt. A run of Label: value pairs or - lines comes back rendered as literal text on screen. Use timestamps — [00:00], [00:05] — for multi-beat pacing instead.

  • One primary action per shot, with two or three comma-joined cues on it. A second action chained with then / and then / followed by renders as a smear. Split it into two shots.

  • Repeat the actor tag verbatim. the same woman resolves to nobody; identity does not carry across a cut.

  • The example must use different content than the source. If the source was a woman reviewing a serum in her bedroom, the example is a guy reviewing a protein bar in his kitchen. That is what proves the template generalises.

  • Dialogue has to fit. Count the words: 2.0 per second measured, so a D-second clip holds about 2.0 × (D − 0.5) once the leading silence is paid for. If the source talks fast, say so in the template and keep each line punchy.

  • No credit numbers anywhere in the template. Prices come from a live POST /v1/estimates in the session that spends them. A template that quotes a number teaches the next agent to skip the call.

Step 6: Save, register and present

  1. Save to skills/novoads-api/prompting/prompt-library/seedance-2-<style-name>.md.
  2. Add a row to the style directory in seedance-2.md so the new formula is reachable — user goal, file link, key trait, matching the rows already there. A formula nothing points at is a formula nobody reads.
  3. Summarise in chat: the style you identified, its layers, what makes it distinct, and the path you saved it to.
  4. Ask: "Want me to price a test prompt from this template and render it, to prove it works?"

If they say yes, write the prompt for a different product, person and setting than the source — that is the actual test — and run step 7.

Step 7: Render a test clip (optional, and it costs credits)

The template is the deliverable; this step only proves it. It runs the full sequence from SKILL.md, and both gates apply.

  1. Upload the product photo if the prompt references one:

    curl -sS -X POST https://api.novoads.ai/v1/uploads \
      -H "Authorization: Bearer $NOVOADS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"contentType":"image/jpeg","sizeBytes":248193}'
    

    then PUT the raw bytes to the uploadUrl it returns, sending back exactly the headers it returned. The assetId is durable and reusable across calls, models and sessions — upload once and keep the id.

  2. Price it — gate 2. POST /v1/estimates with kind: "video", the prompt, the model and durationSeconds. It charges nothing and it is the only source of a price. It also returns an advisory warnings array of craft notes (verified live 2026-08-04) — read them, but they are substring matches that false-positive, and the checklist above is still the real quality gate. Show the number and get a yes before spending.

  3. Confirm the spoken line — gate 1, if the clip speaks. Numbered beats, word count against the duration, the language you are sending, an explicit yes. Approving the template is not approving the sentence.

  4. Generate:

    curl -sS -X POST https://api.novoads.ai/v1/videos \
      -H "Authorization: Bearer $NOVOADS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "seedance-2.0",
        "prompt": "<the filled-in prompt>",
        "durationSeconds": 15,
        "aspectRatio": "9:16",
        "language": "en",
        "referenceAssetIds": ["<assetId>"],
        "productId": "<uuid>"
      }'
    

    Swap referenceAssetIds for startImageAssetId if that is the template's mode. Never both. Returns 202 with jobId and creditsCharged.

  5. Poll GET /v1/generations/{jobId} every 15 seconds until a terminal status — succeeded, failed, blocked or canceled. Not until succeeded: a loop waiting for success never returns on a job that died. seedance-2.0 usually takes 3 to 8 minutes, most often around 5; seedance-2.0-mini 2 to 3.

  6. Download GET /v1/generations/{jobId}/watch, save under outputs/<descriptive-subfolder>/, and open the folder so the user can watch it.

Draft on seedance-2.0-mini first. It is the same grid, the same fields and the same prompt at half the price, back in 2 to 3 minutes — and since the assetId is durable, the final render on seedance-2.0 reuses the same upload. A template validated on mini is a template validated.

Paste this block into every template you generate, so the formula carries its own call sequence:

## Generating from this template

1. Upload the product photo: `POST /v1/uploads` → `assetId` (durable, reuse it).
2. Price it: `POST /v1/estimates` with `kind: "video"`, `model`, `durationSeconds`,
   `prompt`. It charges nothing, it is mandatory, and it is the only source of a price — show the number out loud
   before spending.
3. Confirm the spoken line with the user if the clip speaks (`SKILL.md` gate 1).
4. Generate: `POST /v1/videos`

\```json
{
  "model": "seedance-2.0",
  "prompt": "<your filled-in prompt>",
  "durationSeconds": 15,
  "aspectRatio": "9:16",
  "language": "en",
  "referenceAssetIds": ["<assetId>"]
}
\```

   `startImageAssetId` instead if that is this formula's mode — never both, that is a `400`.

5. Poll `GET /v1/generations/{jobId}` every 15s until a terminal status, then download from
   `GET /v1/generations/{jobId}/watch`.

Every clip in a series is its own call and its own charge. Five generations per
organization may be in flight at once.
  • clone-video-ad/SKILL.md — the sibling: same analysis, but the output is a generated video for the user's product instead of a template file.
  • scripts/extract-frames.sh — frame and audio extraction, shared by both skills.
  • seedance-2.md — the platform guide every generated template defers to. The other two live video models carry their own grids, and you need one the moment a template targets them rather than Seedance: sora-2.md when the first spoken word has to land immediately (it measured no leading silence, where Seedance front-loads 3–5s), veo-3-1.md when the user names Veo or the shot has to evolve over its runtime. Neither takes referenceAssetIds.
  • ugc-selfie-style.md — the cross-model UGC guide. Its Core principles transfer to Seedance; its per-model formulas do not — never port one across by find-and-replace.
  • ../../SKILL.md — the call sequence, the two gates, polling, download.
  • ../../reference.md — every endpoint, field, limit and error code.

File map

skills/novoads-api/
├── SKILL.md                                  ← router: decision tree, gates, full sequence
├── reference.md                              ← endpoints, fields, limits, errors
└── prompting/
    ├── guide.md                              ← marketing brief → API
    ├── brand-voice-starter.md                ← template to copy into MASTER_CONTEXT.md
    ├── analyze-video/
    │   ├── SKILL.md                          ← THIS FILE — video → reusable template
    │   └── scripts/extract-frames.sh         ← ffmpeg frame + audio extraction
    ├── clone-video-ad/
    │   └── SKILL.md                          ← video → adapted video for the user's product
    └── prompt-library/
        ├── seedance-2.md                     ← Seedance 2.0 platform guide (read first)
        ├── seedance-2-ugc.md                 ← 9-layer UGC formula
        ├── seedance-2-premium-reveal.md      ← dark-void premium reveal
        ├── seedance-2-product-hero.md        ← elemental product hero
        ├── seedance-2-studio-lookbook.md     ← studio lookbook with voiceover
        ├── seedance-2-feature-walkthrough.md ← feature walkthrough demo
        ├── ugc-product-selfie.md             ← image formulas (product selfie)
        ├── product-showcase.md               ← image formulas (product showcase)
        ├── influencer-recreation.md          ← image formulas (likeness)
        ├── character-sheet.md                ← image formulas (character consistency)
        ├── character-sheet-gpt-image-2.md    ← the same on gpt-image-2
        ├── nano-banana.md                    ← image formulas (Nano Banana Pro)
        ├── sora-2.md · veo-3-1.md            ← the other two LIVE video models' grids
        └── kling-3.md · ugc-selfie-style.md  ← Kling 3.0 is on the API as `kling-v3-pro` (2.38.0); the selfie guide is cross-model

New formulas you write land in prompt-library/ beside the Seedance files, and get a row in seedance-2.md's style directory.

1---
2name: analyze-video
3metadata: {packVersion: 1.3.0}
4description: >
5 Analyze a reference video and reverse-engineer its style into a reusable Seedance 2.0
6 prompting template for the Novoads API. The output is a new formula file — like
7 seedance-2-ugc.md — that captures the video's structure, pacing, camera work, edit style
8 and tone so it can be recreated with any product, any person, in any setting. Use this
9 whenever someone provides a video they want to use as a style reference, says "I want to
10 make videos like this", "deconstruct this video", "turn this into a template", "analyze
11 this style", or drops a video file and wants to recreate that format repeatedly.
12---
13 
14# Analyze video → reusable prompting template
15 
16Someone found a video style they love. Your job is to deconstruct it into a reusable
17prompting template — a formula they can plug any product, person or setting into and get
18that same style back from `seedance-2.0`.
19 
20The output is **not** a single prompt. It is a **formula file** saved into
21`skills/novoads-api/prompting/prompt-library/`, built the way
22[seedance-2-ugc.md](../novoads-api/prompting/prompt-library/seedance-2-ugc.md) is built: layers, variables,
23option banks, rules, and a worked example, so the agent can generate unlimited prompts in
24that style.
25 
26**Nothing in steps 1 to 6 touches the API.** Frame extraction, transcription and analysis
27are local work on the user's own file: no calls, no credits. Only step 7, the optional test
28render, spends anything, and it runs through the two gates in [SKILL.md](../novoads-api/SKILL.md)
29like every other generation.
30 
31**There is a hosted alternative, and it is not the default.** `POST /v1/analyses` reads an
32uploaded ad into a structured breakdown (hook boundary, beat timeline, on-screen text,
33casting, layer zones) in one synchronous call, at a **flat fee per analysis** that does not move
34with length. Price it through `POST /v1/estimates` with `{"kind":"analysis"}` first. Two reasons it
35is the fallback and not the front door here: the local path costs nothing and reads the whole
36runtime, while `/analyses` defaults to the first 20 seconds (120 is the ceiling, and raising
37it trades hook precision for coverage). Reach for it when ffmpeg is not installed, when the
38user wants zone labels rather than prose, or when the frame read has already failed. Offer it
39with the price attached and let the user choose; never substitute it silently for step 1.
40 
41**The 15-second constraint is the whole design problem.** `seedance-2.0` and
42`seedance-2.0-mini` take any integer 4 to 15 seconds, and the reference video is usually
4330 to 60. Your template has to distil the style into what fits one 15-second clip, and say
44how to recreate the longer arc across a series of clips.
45 
46## Dependencies
47 
48- **ffmpeg** / **ffprobe** — required for frame and audio extraction (step 1).
49 `brew install ffmpeg` on macOS.
50- **whisper** — optional, for transcription (step 2). `pip3 install openai-whisper`. If it
51 is unavailable, ask the user to paste the dialogue instead.
52 
53Neither is a Novoads dependency. Both run locally on the user's file.
54 
55> **REST key required. A Novoads MCP connector is not a substitute.** If
56> `NOVOADS_API_KEY` is missing or still the placeholder, stop before any
57> generation work and tell the user: "Before continuing, create an API key at
58> <https://novoads.ai/dashboard/settings?tab=api> and paste it into `.env`."
59> That holds even when `mcp__novoads__*` tools are connected and authenticated in
60> the session. Never call `mcp__novoads__*` tools from this repo's workflows: they
61> are a different surface with different behavior, including the units they quote
62> costs in. Repo installs verify with `./scripts/check-novoads-env.sh`; a solo
63> install checks `NOVOADS_API_KEY` in the environment.
64 
65**Pack version.** Every `/v1` response carries `X-Novoads-Pack-Version`; mention a newer pack at <https://github.com/novoads/agent-skills> only when that header names a version NEWER than this file's `metadata.packVersion` — equal or older is nothing to say, and it is never a reason to stop.
66 
67## Inputs
68 
69- **Video file** (required): path to `.mp4`, `.mov`, `.webm` or similar.
70- **Style name** (optional): what to call the template — `car-review`, `unboxing-hype`,
71 `skeptic-converted`. If they do not give one, name it from what you observe.
72 
73## Step 1: Extract frames and audio
74 
75```bash
76bash "skills/analyze-video/scripts/extract-frames.sh" \
77 "<video_path>" "/tmp/video-analysis" <num_frames>
78```
79 
80Frame count by source duration:
81 
82| Source duration | Frames |
83|---|---|
84| Under 10s | 8 |
85| 10–20s | 12 |
86| 20–30s | 16 |
87| Over 30s | 20 |
88 
89Read `metadata.txt` for duration, resolution and fps.
90 
91## Step 2: Transcribe the audio
92 
93Try, in order:
94 
951. `whisper` CLI: `whisper /tmp/video-analysis/audio.wav --model base --output_format txt --output_dir /tmp/video-analysis`
962. Python whisper inline.
973. Neither available → ask the user for the dialogue, or to install whisper.
98 
99The transcript is where pacing lives: speech rhythm, filler words, how dialogue interleaves
100with action. All of that defines the style, and all of it has to survive into the template.
101 
102## Step 3: Study the reference templates
103 
104Read the shipped formulas before you write one. They are the standard your output has to
105meet:
106 
107- [seedance-2-ugc.md](../novoads-api/prompting/prompt-library/seedance-2-ugc.md) — 9-layer UGC formula, the richest example
108- [seedance-2-premium-reveal.md](../novoads-api/prompting/prompt-library/seedance-2-premium-reveal.md) — dark-void reveal, no person
109- [seedance-2-product-hero.md](../novoads-api/prompting/prompt-library/seedance-2-product-hero.md) — elemental product hero, no person
110- [seedance-2-studio-lookbook.md](../novoads-api/prompting/prompt-library/seedance-2-studio-lookbook.md) — studio lookbook with voiceover
111- [seedance-2-feature-walkthrough.md](../novoads-api/prompting/prompt-library/seedance-2-feature-walkthrough.md) — feature demo, multi-clip series
112 
113And the platform guide, which every one of them defers to:
114 
115- [seedance-2.md](../novoads-api/prompting/prompt-library/seedance-2.md) — request fields, the grid, prompt craft, what the estimate flags, the adaptation checklist
116 
117Notice what they share:
118 
119- They name **layers** — the structural building blocks of the style.
120- Each layer has a **pattern**: a repeatable sentence shape with `{{VARIABLES}}`.
121- Variables come with **option banks**, not blanks.
122- They state **rules** that explain why a choice matters.
123- They declare a **mode** — `startImageAssetId` or `referenceAssetIds` — at the top.
124- They carry a **worked example** that has been priced live.
125 
126Your template has to hit that depth to be usable.
127 
128## Step 4: Analyze the frames — find what defines this style
129 
130Read **all** the extracted frames. You are not describing one video; you are isolating the
131transferable pattern.
132 
133For every dimension ask: "is this specific to THIS VIDEO — the person, the product, the
134room — or is it THE STYLE?" Only the style goes into the template. The specifics become
135variables.
136 
137### Structure and pacing
138 
139- How long is the source? How many distinct beats?
140- Which 2–3 beats are essential — the ones without which it stops being this style?
141- What is the arc? Hook → demo → proof → verdict, or something else?
142- Fast cuts or held shots? How long is each beat?
143- Silent beats, or wall-to-wall dialogue?
144- **The compression question:** if you had to carry the whole feel in 3 beats and 2–3
145 spoken lines, which moments survive?
146 
147### Camera and framing
148 
149- Filming perspective: selfie, propped phone, second operator, screen recording?
150- How does framing change between beats — tighter, wider, same angle throughout?
151- Is there a signature move that *is* the style?
152 
153### Edit style
154 
155- Jump cuts, continuous take, time-lapse, split screen?
156- Transitions: hard cuts, dissolves, text?
157- Recurring motifs: close-up product inserts, reaction face, before/after?
158 
159### Dialogue and script structure
160 
161- Hook format: question, bold claim, mid-action, reaction?
162- Scripted, improvised, voiceover, text-on-screen?
163- Speech patterns that carry the tone: filler words, sentence length, vocabulary.
164- How lines relate to what the hands are doing.
165 
166### Tone and energy
167 
168- 3–4 emotion words for the vibe.
169- Energy arc: builds, flat, peaks then drops?
170- Relationship to the viewer: friend, expert, skeptic, fan?
171 
172### Lighting and technical quality
173 
174- Light source and direction: natural, ring light, moody, blown out?
175- Phone or polished? Which technical "flaws" are load-bearing?
176- Audio character: phone mic, lapel, voiceover, room tone?
177 
178### What makes this style DIFFERENT
179 
180The most important pass. After cataloguing everything above, name the 2–3 things that
181separate this from a generic UGC clip or a generic product review — the pacing, the hook
182format, the way the product enters frame, the edit rhythm. Those become the core of the
183template; everything else is scaffolding.
184 
185### The 15-second plan
186 
187Before building anything, map the style onto one clip:
188 
189- **What is the minimum viable version?** Which beats are essential, which are nice to
190 have. Fifteen seconds has to carry the thing that makes someone say "oh, *that* kind of
191 video".
192- **Does it need a series?** If the power is in a narrative arc or a feature rundown, it
193 needs 2–3 clips. If it is a vibe or a single moment, one clip is enough.
194- **How many spoken lines fit?** Delivery measures **2.0 words per second** (~13 characters a
195 second, spaces included) after ~0.5s of leading silence. Fifteen seconds is 2–3 short
196 sentences, and the slack is what leaves room for a silent beat. Count the source's lines and keep the ones
197 carrying the voice.
198- **What is the beat skeleton?** 15 seconds is 2–3 beats: hook → core moment → kicker.
199 
200### Which mode the style wants
201 
202Every formula in this library declares one, because they are mutually exclusive on the API
203and the choice follows from the style:
204 
205| If the style… | Mode | Why |
206|---|---|---|
207| opens on the product itself, held up or sitting on a surface | `startImageAssetId` | the product photo is literally the first frame |
208| builds a scene the product was never photographed in — a void, a splash, a studio set | `referenceAssetIds` | the model has to composite it, not animate a flat photo |
209| holds one person across a series of clips | `referenceAssetIds` | `@Image1` the product, `@Image2` the person — Seedance re-casts on every cut, and a repeated description does not hold a face |
210 
211Write the answer into the template. A formula that leaves the mode open produces prompts
212whose `@Image1` tokens point at nothing.
213 
214## Step 5: Build the template
215 
216Create a self-contained markdown file. Someone should be able to read it and write prompts
217in this style without ever seeing the source video.
218 
219### Template structure
220 
221```markdown
222# [Style name] — Seedance 2.0
223 
224**Use when:** [the kind of video this produces]
225 
226**Model guide:** read [seedance-2.md](../novoads-api/prompting/prompt-library/seedance-2.md) first for the request fields, the grid,
227and the platform rules.
228 
229**Mode:** `startImageAssetId` or `referenceAssetIds` — say which, and what each `@ImageN`
230slot holds. The two are separate modes and a body carrying both is a `400`.
231 
232## What defines this style
233 
234[2–3 paragraphs of theory. This is what lets a prompt writer make good variable choices
235instead of filling blanks.]
236 
237## Things to know before you write a word
238 
239[The route-specific traps. If the style speaks, say that the line is rendered and
240lip-synced in this same call and that gate 1 applies. If it is silent, say that silence has
241to be declared in the prose.]
242mandatory.]
243 
244## The structure
245 
246[The layers of THIS style. Do not force-fit the 9-layer UGC model. Five layers, twelve
247layers — let the video decide.]
248 
249## Layer-by-layer formula
250 
251### Layer N: [Name]
252 
253[What this layer does and why it matters here.]
254 
255**Pattern:**
256\```
257[The repeatable sentence shape with {{VARIABLES}}]
258\```
259 
260| Variable | Options | Notes |
261|---|---|---|
262| `VARIABLE_NAME` | option 1, option 2, option 3 | [guidance] |
263 
264### [... more layers ...]
265 
266## Beat structure (one 15-second clip)
267 
268[The 3-beat framework: hook, core, kicker. Which beats speak and which are silent. Two to
269three spoken lines total.]
270 
271## Multi-clip strategy (if applicable)
272 
273[How to split across 2–3 clips, what each one covers, and what holds identity across them:
274the same `referenceAssetIds` in every call plus the actor tag repeated verbatim.]
275 
276## Tone and pacing guide
277 
278[Energy, speech patterns, rhythm, with a pacing-cue bank specific to this style.]
279 
280## Technical specs
281 
282[Lighting, camera quality, audio character — including the flaws that make it authentic.]
283 
284## Complete template
285 
286[One copy-paste block for ONE 15-second clip, every variable marked {{PLACEHOLDER}}.
287Max 3 beats, max 2–3 spoken lines. This is the unit.]
288 
289## Example prompt
290 
291[The template filled in for a DIFFERENT product, person and setting than the source video.
292Price it at POST /v1/estimates before shipping it.]
293 
294## Adaptation checklist
295 
296[The style-specific checks, then the standard ones from seedance-2.md.]
297 
298## Generating from this template
299 
300[The call sequence — see step 7 of analyze-video for the block to paste here.]
301```
302 
303### Rules the template has to teach
304 
305These are not style preferences. Each one is a defect found in a shipped formula, and a
306template that omits them manufactures the same defect in every prompt written from it.
307 
308- **Every prompt is one clip of 4 to 15 seconds.** If the style needs more, the template
309 ships a multi-clip strategy — never a longer prompt.
310 
311- **Keep prompts between 100 and 260 words.** Shorter prompts produce vague results; longer
312 ones overwhelm the model and cause it to lose focus on key details. Do not pad to hit the
313 floor and do not cut a beat's framing to stay under the ceiling.
314 
315- **Variables are curated choices, not blanks.** "Any lighting" is useless. "Natural window
316 light, overhead kitchen light, golden-hour balcony light" is a decision the writer can
317 actually make.
318 
319- **Declare the mode and address references as `@Image1`, `@Image2`, …** The tokens resolve
320 **positionally** against `referenceAssetIds` in the order the array is sent, and a token
321 pointing past the end of the array is refused before the charge. If the style uses a
322 start frame instead, say so and use no tokens at all.
323 
324 
325- **If the style is silent, say so in prose** — `a silent product film with no spoken
326 dialogue`, `silent b-roll`. Seedance renders audio from the prompt, so a film that never
327 declares silence can come back with an invented voice on it. Do not put on-screen text in
328 double quotes and call it done — the model reads a quoted string as a line to speak.
329 
330- **No forbidden words:** `cinematic`, `professional`, `stunning`, `8k`, `studio`,
331 `perfect`. Nothing on the API rejects or reports them — this is craft advice, and the
332 reason to drop them is the render. Replace one with the real thing: the light source,
333 the surface, the flaw.
334 
335- **Prose, never a bulleted prompt.** A run of `Label: value` pairs or `-` lines comes back
336 **rendered as literal text on screen**. Use timestamps — `[00:00]`, `[00:05]` — for
337 multi-beat pacing instead.
338 
339- **One primary action per shot**, with two or three comma-joined cues on it. A second
340 action chained with `then` / `and then` / `followed by` renders as a smear. Split it into
341 two shots.
342 
343- **Repeat the actor tag verbatim.** `the same woman` resolves to nobody; identity does not
344 carry across a cut.
345 
346- **The example must use different content than the source.** If the source was a woman
347 reviewing a serum in her bedroom, the example is a guy reviewing a protein bar in his
348 kitchen. That is what proves the template generalises.
349 
350- **Dialogue has to fit.** Count the words: **2.0 per second** measured, so a `D`-second
351 clip holds about `2.0 × (D − 0.5)` once the leading silence is paid for. If the source
352 talks fast, say so in the template and keep each line punchy.
353 
354- **No credit numbers anywhere in the template.** Prices come from a live
355 `POST /v1/estimates` in the session that spends them. A template that quotes a number
356 teaches the next agent to skip the call.
357 
358## Step 6: Save, register and present
359 
3601. Save to `skills/novoads-api/prompting/prompt-library/seedance-2-<style-name>.md`.
3612. **Add a row to the style directory** in
362 [seedance-2.md](../novoads-api/prompting/prompt-library/seedance-2.md) so the new formula is reachable —
363 user goal, file link, key trait, matching the rows already there. A formula nothing
364 points at is a formula nobody reads.
3653. Summarise in chat: the style you identified, its layers, what makes it distinct, and the
366 path you saved it to.
3674. Ask: *"Want me to price a test prompt from this template and render it, to prove it
368 works?"*
369 
370If they say yes, write the prompt for a different product, person and setting than the
371source — that is the actual test — and run step 7.
372 
373## Step 7: Render a test clip (optional, and it costs credits)
374 
375The template is the deliverable; this step only proves it. It runs the full sequence from
376[SKILL.md](../novoads-api/SKILL.md), and **both gates apply**.
377 
3781. **Upload the product photo** if the prompt references one:
379 
380 ```bash
381 curl -sS -X POST https://api.novoads.ai/v1/uploads \
382 -H "Authorization: Bearer $NOVOADS_API_KEY" \
383 -H "Content-Type: application/json" \
384 -d '{"contentType":"image/jpeg","sizeBytes":248193}'
385 ```
386 
387 then PUT the raw bytes to the `uploadUrl` it returns, sending back **exactly** the
388 headers it returned. The `assetId` is durable and reusable across calls, models and
389 sessions — upload once and keep the id.
390 
3912. **Price it — gate 2.** `POST /v1/estimates` with `kind: "video"`, the `prompt`, the
392 `model` and `durationSeconds`. It charges nothing and it is the only source of a price. It also
393 returns an advisory `warnings` array of craft notes (verified live 2026-08-04) — read them,
394 but they are substring matches that false-positive, and the checklist above is still the
395 real quality gate. Show the number and get a yes before spending.
396 
3973. **Confirm the spoken line — gate 1**, if the clip speaks. Numbered beats, word count
398 against the duration, the `language` you are sending, an explicit yes. Approving the
399 template is not approving the sentence.
400 
4014. **Generate:**
402 
403 ```bash
404 curl -sS -X POST https://api.novoads.ai/v1/videos \
405 -H "Authorization: Bearer $NOVOADS_API_KEY" \
406 -H "Content-Type: application/json" \
407 -d '{
408 "model": "seedance-2.0",
409 "prompt": "<the filled-in prompt>",
410 "durationSeconds": 15,
411 "aspectRatio": "9:16",
412 "language": "en",
413 "referenceAssetIds": ["<assetId>"],
414 "productId": "<uuid>"
415 }'
416 ```
417 
418 Swap `referenceAssetIds` for `startImageAssetId` if that is the template's mode. Never
419 both. Returns `202` with `jobId` and `creditsCharged`.
420 
4215. **Poll** `GET /v1/generations/{jobId}` every 15 seconds until a **terminal** status —
422 `succeeded`, `failed`, `blocked` or `canceled`. Not until `succeeded`: a loop waiting
423 for success never returns on a job that died. `seedance-2.0` usually takes 3 to 8
424 minutes, most often around 5; `seedance-2.0-mini` 2 to 3.
425 
4266. **Download** `GET /v1/generations/{jobId}/watch`, save under
427 `outputs/<descriptive-subfolder>/`, and open the folder so the user can watch it.
428 
429**Draft on `seedance-2.0-mini` first.** It is the same grid, the same fields and the same
430prompt at half the price, back in 2 to 3 minutes — and since the `assetId` is durable, the
431final render on `seedance-2.0` reuses the same upload. A template validated on mini is a
432template validated.
433 
434Paste this block into every template you generate, so the formula carries its own call
435sequence:
436 
437```markdown
438## Generating from this template
439 
4401. Upload the product photo: `POST /v1/uploads` → `assetId` (durable, reuse it).
4412. Price it: `POST /v1/estimates` with `kind: "video"`, `model`, `durationSeconds`,
442 `prompt`. It charges nothing, it is mandatory, and it is the only source of a price — show the number out loud
443 before spending.
4443. Confirm the spoken line with the user if the clip speaks (`SKILL.md` gate 1).
4454. Generate: `POST /v1/videos`
446 
447\```json
448{
449 "model": "seedance-2.0",
450 "prompt": "<your filled-in prompt>",
451 "durationSeconds": 15,
452 "aspectRatio": "9:16",
453 "language": "en",
454 "referenceAssetIds": ["<assetId>"]
455}
456\```
457 
458 `startImageAssetId` instead if that is this formula's mode — never both, that is a `400`.
459 
4605. Poll `GET /v1/generations/{jobId}` every 15s until a terminal status, then download from
461 `GET /v1/generations/{jobId}/watch`.
462 
463Every clip in a series is its own call and its own charge. Five generations per
464organization may be in flight at once.
465```
466 
467## Related files
468 
469- [clone-video-ad/SKILL.md](../clone-video-ad/SKILL.md) — the sibling: same analysis, but the output is
470 a generated video for the user's product instead of a template file.
471- [scripts/extract-frames.sh](scripts/extract-frames.sh) — frame and audio extraction,
472 shared by both skills.
473- [seedance-2.md](../novoads-api/prompting/prompt-library/seedance-2.md) — the platform guide every generated template defers to. The other two live video models carry their own grids, and you need one the moment a template targets them rather than Seedance: [sora-2.md](../novoads-api/prompting/prompt-library/sora-2.md) when the first spoken word has to land immediately (it measured no leading silence, where Seedance front-loads 3–5s), [veo-3-1.md](../novoads-api/prompting/prompt-library/veo-3-1.md) when the user names Veo or the shot has to evolve over its runtime. Neither takes `referenceAssetIds`.
474- [ugc-selfie-style.md](../novoads-api/prompting/prompt-library/ugc-selfie-style.md) — the cross-model UGC guide. Its *Core principles* transfer to Seedance; its per-model formulas do not — never port one across by find-and-replace.
475- [../../SKILL.md](../novoads-api/SKILL.md) — the call sequence, the two gates, polling, download.
476- [../../reference.md](../novoads-api/reference.md) — every endpoint, field, limit and error code.
477 
478## File map
479 
480```
481skills/novoads-api/
482├── SKILL.md ← router: decision tree, gates, full sequence
483├── reference.md ← endpoints, fields, limits, errors
484└── prompting/
485 ├── guide.md ← marketing brief → API
486 ├── brand-voice-starter.md ← template to copy into MASTER_CONTEXT.md
487 ├── analyze-video/
488 │ ├── SKILL.md ← THIS FILE — video → reusable template
489 │ └── scripts/extract-frames.sh ← ffmpeg frame + audio extraction
490 ├── clone-video-ad/
491 │ └── SKILL.md ← video → adapted video for the user's product
492 └── prompt-library/
493 ├── seedance-2.md ← Seedance 2.0 platform guide (read first)
494 ├── seedance-2-ugc.md ← 9-layer UGC formula
495 ├── seedance-2-premium-reveal.md ← dark-void premium reveal
496 ├── seedance-2-product-hero.md ← elemental product hero
497 ├── seedance-2-studio-lookbook.md ← studio lookbook with voiceover
498 ├── seedance-2-feature-walkthrough.md ← feature walkthrough demo
499 ├── ugc-product-selfie.md ← image formulas (product selfie)
500 ├── product-showcase.md ← image formulas (product showcase)
501 ├── influencer-recreation.md ← image formulas (likeness)
502 ├── character-sheet.md ← image formulas (character consistency)
503 ├── character-sheet-gpt-image-2.md ← the same on gpt-image-2
504 ├── nano-banana.md ← image formulas (Nano Banana Pro)
505 ├── sora-2.md · veo-3-1.md ← the other two LIVE video models' grids
506 └── kling-3.md · ugc-selfie-style.md ← Kling 3.0 is on the API as `kling-v3-pro` (2.38.0); the selfie guide is cross-model
507```
508 
509New formulas you write land in `prompt-library/` beside the Seedance files, and get a row
510in `seedance-2.md`'s style directory.
511 

Discussion