@nodetool-ai/agents 0.8.4-a24.2 → 0.8.4
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/dist/.nodetool-build-stamp +1 -1
- package/dist/_skills/LICENSE-iart-motion-design-skills +21 -0
- package/dist/_skills/README.md +211 -0
- package/dist/_skills/api-agents/SKILL.md +58 -0
- package/dist/_skills/api-apps/SKILL.md +54 -0
- package/dist/_skills/api-assets/SKILL.md +66 -0
- package/dist/_skills/api-collections/SKILL.md +33 -0
- package/dist/_skills/api-games/SKILL.md +81 -0
- package/dist/_skills/api-media/SKILL.md +131 -0
- package/dist/_skills/api-memory/SKILL.md +72 -0
- package/dist/_skills/api-models/SKILL.md +78 -0
- package/dist/_skills/api-scripts/SKILL.md +79 -0
- package/dist/_skills/api-settings/SKILL.md +49 -0
- package/dist/_skills/api-sketches/SKILL.md +61 -0
- package/dist/_skills/api-storyboards/SKILL.md +94 -0
- package/dist/_skills/api-timelines/SKILL.md +151 -0
- package/dist/_skills/api-web/SKILL.md +57 -0
- package/dist/_skills/api-workflows/SKILL.md +123 -0
- package/dist/_skills/beat-sync-editing/SKILL.md +208 -0
- package/dist/_skills/caption-titles/SKILL.md +94 -0
- package/dist/_skills/color-motion/SKILL.md +185 -0
- package/dist/_skills/commercial-beat-sheet/SKILL.md +338 -0
- package/dist/_skills/elevenlabs-audio-prompting/SKILL.md +163 -0
- package/dist/_skills/explainer-storyboard/SKILL.md +63 -0
- package/dist/_skills/flux-2-klein-prompting/SKILL.md +107 -0
- package/dist/_skills/frame-composition/SKILL.md +291 -0
- package/dist/_skills/game-direction/SKILL.md +99 -0
- package/dist/_skills/gpt-image-2-prompting/SKILL.md +161 -0
- package/dist/_skills/hailuo-prompting/SKILL.md +107 -0
- package/dist/_skills/kling-video-prompting/SKILL.md +188 -0
- package/dist/_skills/launch-commercial/SKILL.md +267 -0
- package/dist/_skills/launch-kit/SKILL.md +120 -0
- package/dist/_skills/logo-reveal/SKILL.md +225 -0
- package/dist/_skills/manimgx/SKILL.md +123 -0
- package/dist/_skills/minimax-h3-prompting/SKILL.md +134 -0
- package/dist/_skills/motion-ad/SKILL.md +281 -0
- package/dist/_skills/motion-background/SKILL.md +145 -0
- package/dist/_skills/motion-curves/SKILL.md +302 -0
- package/dist/_skills/motion-direction/SKILL.md +259 -0
- package/dist/_skills/motion-graphics/SKILL.md +217 -0
- package/dist/_skills/motion-principles/SKILL.md +255 -0
- package/dist/_skills/music-video-treatment/SKILL.md +40 -0
- package/dist/_skills/nano-banana-pro-prompting/SKILL.md +133 -0
- package/dist/_skills/native-game/SKILL.md +310 -0
- package/dist/_skills/native-game-reference/SKILL.md +879 -0
- package/dist/_skills/nodetool-3d-scene/SKILL.md +110 -0
- package/dist/_skills/nodetool-api-reference/SKILL.md +338 -0
- package/dist/_skills/nodetool-app-builder/SKILL.md +291 -0
- package/dist/_skills/nodetool-browser-agent/SKILL.md +119 -0
- package/dist/_skills/nodetool-chat-cli/SKILL.md +184 -0
- package/dist/_skills/nodetool-custom-node-developer/SKILL.md +294 -0
- package/dist/_skills/nodetool-deployment/SKILL.md +195 -0
- package/dist/_skills/nodetool-js-scripting/SKILL.md +223 -0
- package/dist/_skills/nodetool-model-provider-config/SKILL.md +348 -0
- package/dist/_skills/nodetool-rag-indexing/SKILL.md +153 -0
- package/dist/_skills/nodetool-sketch/SKILL.md +138 -0
- package/dist/_skills/nodetool-skill-author/SKILL.md +125 -0
- package/dist/_skills/nodetool-troubleshooter/SKILL.md +370 -0
- package/dist/_skills/nodetool-video-post/SKILL.md +149 -0
- package/dist/_skills/nodetool-workflow-builder/SKILL.md +295 -0
- package/dist/_skills/product-commercial/SKILL.md +132 -0
- package/dist/_skills/qwen-image-prompting/SKILL.md +122 -0
- package/dist/_skills/script-video/SKILL.md +106 -0
- package/dist/_skills/seedance-2-prompting/SKILL.md +194 -0
- package/dist/_skills/seedream-prompting/SKILL.md +117 -0
- package/dist/_skills/short-film/SKILL.md +123 -0
- package/dist/_skills/stable-audio-prompting/SKILL.md +130 -0
- package/dist/_skills/storyboard-core/SKILL.md +716 -0
- package/dist/_skills/timeline-edit-ops/SKILL.md +736 -0
- package/dist/_skills/trailer-template/SKILL.md +291 -0
- package/dist/_skills/ugc-video/SKILL.md +186 -0
- package/dist/_skills/veo-3-prompting/SKILL.md +140 -0
- package/dist/_skills/video-audio-continuity/SKILL.md +90 -0
- package/dist/_skills/video-clone/SKILL.md +96 -0
- package/dist/_skills/video-workflow/SKILL.md +119 -0
- package/dist/_skills/wan-2-6-prompting/SKILL.md +107 -0
- package/dist/app-build/bridge.d.ts.map +1 -1
- package/dist/app-build/bridge.js +44 -17
- package/dist/app-build/bridge.js.map +1 -1
- package/dist/capabilities/apps.d.ts.map +1 -1
- package/dist/capabilities/apps.js +55 -13
- package/dist/capabilities/apps.js.map +1 -1
- package/dist/capabilities/apps.specs.d.ts.map +1 -1
- package/dist/capabilities/apps.specs.js +5 -3
- package/dist/capabilities/apps.specs.js.map +1 -1
- package/dist/capabilities/game-asset-browser.d.ts.map +1 -1
- package/dist/capabilities/game-asset-browser.js +6 -1
- package/dist/capabilities/game-asset-browser.js.map +1 -1
- package/dist/capabilities/game.d.ts +1 -1
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +71 -56
- package/dist/capabilities/game.js.map +1 -1
- package/dist/capabilities/game.specs.js +1 -1
- package/dist/capabilities/game.specs.js.map +1 -1
- package/dist/capabilities/game3d.d.ts.map +1 -1
- package/dist/capabilities/game3d.js +14 -19
- package/dist/capabilities/game3d.js.map +1 -1
- package/dist/capabilities/gameAssertions.d.ts +52 -0
- package/dist/capabilities/gameAssertions.d.ts.map +1 -0
- package/dist/capabilities/gameAssertions.js +32 -0
- package/dist/capabilities/gameAssertions.js.map +1 -0
- package/dist/capabilities/models.d.ts.map +1 -1
- package/dist/capabilities/models.js +9 -0
- package/dist/capabilities/models.js.map +1 -1
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +36 -11
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +11 -1
- package/dist/capabilities/storyboards.specs.js.map +1 -1
- package/dist/capabilities/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +1 -0
- package/dist/capabilities/workflows.js.map +1 -1
- package/dist/capabilities/workflows.specs.d.ts.map +1 -1
- package/dist/capabilities/workflows.specs.js +4 -3
- package/dist/capabilities/workflows.specs.js.map +1 -1
- package/dist/evals/surfaces/game.d.ts.map +1 -1
- package/dist/evals/surfaces/game.js +29 -1
- package/dist/evals/surfaces/game.js.map +1 -1
- package/dist/game-authoring.d.ts +120 -0
- package/dist/game-authoring.d.ts.map +1 -1
- package/dist/game-build-integrity.d.ts +12 -0
- package/dist/game-build-integrity.d.ts.map +1 -0
- package/dist/game-build-integrity.js +78 -0
- package/dist/game-build-integrity.js.map +1 -0
- package/dist/game-examples.d.ts +44 -3
- package/dist/game-examples.d.ts.map +1 -1
- package/dist/game-examples.js +60 -10
- package/dist/game-examples.js.map +1 -1
- package/dist/system-skills.d.ts +1 -1
- package/dist/system-skills.js +1 -1
- package/dist/tools/builtin-tools.d.ts.map +1 -1
- package/dist/tools/builtin-tools.js +2 -0
- package/dist/tools/builtin-tools.js.map +1 -1
- package/package.json +19 -19
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: launch-commercial
|
|
3
|
+
description: Turn a product page URL into a finished launch commercial — research, storyboard, rendered stills and clips, voice, music, an assembled timeline — delivered as an editable NodeTool project with the real cost. Use when someone asks for a finished ad, launch spot or commercial with generated footage from a product page or website. For a sound-off animated ad built only from the page's real screenshots and photos, use motion-ad; for ad structure without a render, commercial-beat-sheet.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Product Page → Launch Commercial Agent
|
|
7
|
+
|
|
8
|
+
Take a product page URL and deliver a finished launch commercial — rendered clips, voice, music, an assembled cut — as an **editable NodeTool project** (storyboard + script + timeline), plus the real cost of making it from the ledger. Call the capability modules directly; build no workflow.
|
|
9
|
+
|
|
10
|
+
This skill owns the outer loop: research → brief → entities → board → renders → voice → music → cut → cost report. The dramaturgy inside the board — hooks, beat budget, timecodes, VO discipline — belongs to the `commercial-beat-sheet` skill. Load it at phase 3 and follow it there. Do not duplicate its rules; do not skip it.
|
|
11
|
+
|
|
12
|
+
When the user wants a sound-off motion-graphics ad (a 15s feed ad, kinetic type, the product's real screenshots animated on the timeline, no generated footage), this is the wrong skill: load `motion-ad`.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Role
|
|
17
|
+
|
|
18
|
+
Work as an integrated producer: a URL and a runtime in, a shot spot out. Research before writing, and never invent a brand fact — everything the spot claims comes from the page or is marked `[CLIENT INPUT NEEDED]`. Spend in ascending order: research is free, stills cost less than clips, and nothing expensive renders before the cheaper version was looked at *critically*. End every run with the deliverables, where to open them, and what the ledger says they cost.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## The contract
|
|
23
|
+
|
|
24
|
+
The user gives you at minimum:
|
|
25
|
+
|
|
26
|
+
```text
|
|
27
|
+
PRODUCT PAGE URL: [required]
|
|
28
|
+
RUNTIME: [15s / 30s / 60s — default 30s]
|
|
29
|
+
PLATFORM: [default: 9:16 for ≤30s, 16:9 above]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Everything else — brand, category, audience, pain, USP, proof, tone, CTA — you **derive from the page** and present back as a brief for sign-off. A field the page cannot answer is `[CLIENT INPUT NEEDED]`, never a guess.
|
|
33
|
+
|
|
34
|
+
The user gets back: a **storyboard** (still + clip on every shot), a **script** (every line voiced), a **timeline** that validates, and a **cost report** from the ledger. All four persist. The render is not the deliverable; the editable project is.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Tool contract — read this before writing any code
|
|
39
|
+
|
|
40
|
+
The storyboard, entity and render call shapes live in `storyboard-core` § Tool contract: return fields, all fifteen `edit_storyboard` ops, every key `set_board` accepts, and what text each generator reads. `load_skill` it before phase 3. What this skill adds:
|
|
41
|
+
|
|
42
|
+
**Headless only.** `ui_*` tools need a browser with that document open. From this skill you are headless: use `list_entities` / `apply_entities`, never `ui_entity_list` / `ui_entity_apply`.
|
|
43
|
+
|
|
44
|
+
**Read ids by name.** `create_storyboard` → `.storyboard_id`. `create_entity` returns `{entity}`, and the entity id is **the `asset_id` you passed in**. `nodetool.media.generateImage` → `.asset_id`. `extract_script_from_storyboard` → `.script_id`. Never write `result.id || result.asset_id` and proceed. If an id is `undefined`, stop and read it back with `list_*` — do not pass it onward.
|
|
45
|
+
|
|
46
|
+
**The board holds the default models.** `image_model` / `video_model` on `set_board` take the `.ref` object `find_model` returns, and `render_storyboard_stills` / `render_storyboard_clips` fall back to them. With no board model and no `provider` + `model` on the call, the render fails and says so — it never picks one. Set them on the board once in phase 3 and pass `provider` + `model` only for a shot that needs a different line.
|
|
47
|
+
|
|
48
|
+
**Measure with `ffprobe`.** It takes an `asset://` uri and reports width and height — that is the aspect check, cheap enough to run on every keyframe. Grade the picture itself with `view_image` and `score_image_adherence`.
|
|
49
|
+
|
|
50
|
+
**`take_screenshot` renders the page in headless Chrome on the machine NodeTool runs on**, or through the remote browser service when one is configured. If it fails, report the error once and fall back to the page's own downloaded images.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Spend gates
|
|
55
|
+
|
|
56
|
+
| Gate | After | You show | Money spent |
|
|
57
|
+
| --- | --- | --- | --- |
|
|
58
|
+
| **G1 Brief** | Research | Derived brief, entity plan | $0 |
|
|
59
|
+
| **G2 Stills** | Keyframes + your own adversarial grade | Every keyframe, defects named | the stills (from the ledger) |
|
|
60
|
+
| **G3 Cut** | Clips + voice + music + assembly | Timeline, validation, ledger cost | the real number |
|
|
61
|
+
|
|
62
|
+
Never render clips before G2 is approved. Never pick a model silently. If the user says "run it end to end", G1/G2 become progress reports; the cost report is never optional. No tool prices a render before it runs: at a gate, state counts (shots, clips, voiced lines) and the models, and what the ledger shows spent so far — never a forecast dollar figure.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Phase 1 — Research the page
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import { browser, take_screenshot, download_file } from "@nodetool-ai/sandbox-nodetool/web";
|
|
70
|
+
|
|
71
|
+
const page = await browser({ url: productPageUrl });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Extract into the brief:
|
|
75
|
+
|
|
76
|
+
- **Facts** — name, what it does, price, materials, claims. Verbatim or `[CLIENT INPUT NEEDED]`.
|
|
77
|
+
- **Voice** — how the brand writes. The VO inherits it.
|
|
78
|
+
- **Visual identity** — read hex values out of the raw HTML (design tokens beat eyeballing a screenshot), plus a live screenshot from `take_screenshot`.
|
|
79
|
+
- **Product imagery** — download the hero photos. They become the prop entity's reference. The real product is the one thing the model must not reimagine.
|
|
80
|
+
- **The one pain** — pages list five benefits; the spot escalates one. Take the one the page leads with.
|
|
81
|
+
|
|
82
|
+
Then fill the beat-sheet skill's brief format and present it at **G1** with the entity plan.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Phase 2 — Entities from the page
|
|
87
|
+
|
|
88
|
+
Follow the beat-sheet skill's entity rules (kinds, descriptors, style-first, roster size). What this skill adds:
|
|
89
|
+
|
|
90
|
+
**Priority per entity:** library (`list_entities`) → page asset → generated.
|
|
91
|
+
|
|
92
|
+
**The style entity comes from the page whenever the page has a look.** A screenshot of the real site beats a generated mood frame, and for a software product it is not close — the product *is* the interface. Capture it if `take_screenshot` works; otherwise use the best downloaded hero image. Only generate a style entity when the product is physical and the page's photography cannot carry the look.
|
|
93
|
+
|
|
94
|
+
**But a screenshot style anchor has one failure mode, and it is severe.** Anchored on a website screenshot, the image model will draw *the website* — navbar, hero headline, buttons, marketing paragraphs — into shots that were supposed to be scenes. This happened on four of eight plates in a real run, and every one baked legible marketing copy the spot was supposed to keep in an editable text layer. So the descriptor separates look from content, and **every scene prompt carries hard negatives**:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
ABSOLUTELY NO website page, NO navbar, NO marketing headline, NO gradient
|
|
98
|
+
title text, NO buttons, NO paragraphs, NO wordmark or logo. No readable
|
|
99
|
+
text of any kind.
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Two shot classes, prompted differently:
|
|
103
|
+
|
|
104
|
+
- **Product shots** (the mechanism, the proof) — the interface *is* the subject, edge to edge, no page around it. UI chrome (a status pill, a label) is fine; marketing copy is not.
|
|
105
|
+
- **Scene shots** (hook, problem, CTA, closing) — describe the scene only. Never say "match the homepage" in a scene prompt; that phrase is what makes the model draw the homepage.
|
|
106
|
+
|
|
107
|
+
Entity ids: pass `asset_id`, and use that same asset id as the entity id afterward.
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
import { create_entity, list_entities } from "@nodetool-ai/sandbox-nodetool/entities";
|
|
111
|
+
|
|
112
|
+
const styleAssetId = shot.asset_id; // the screenshot
|
|
113
|
+
await create_entity({ asset_id: styleAssetId, kind: "style", name: "...", descriptor: "...", palette: [...] });
|
|
114
|
+
const styleId = styleAssetId; // entity id === asset id
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Phase 3 — Beat sheet and board
|
|
120
|
+
|
|
121
|
+
Load the **commercial-beat-sheet** skill and run its execution path with the phase-1 brief and phase-2 roster: three hooks, a recommendation, the full beat sheet. Then:
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
import { create_storyboard, edit_storyboard } from "@nodetool-ai/sandbox-nodetool/storyboards";
|
|
125
|
+
|
|
126
|
+
const board = await create_storyboard({ name, brief, style, aspect_ratio });
|
|
127
|
+
const boardId = board.storyboard_id;
|
|
128
|
+
|
|
129
|
+
await edit_storyboard({
|
|
130
|
+
storyboard_id: boardId,
|
|
131
|
+
ops: [{ op: "set_board", entity_ids: roster }] // set_board, not set_entities
|
|
132
|
+
});
|
|
133
|
+
for (const shot of shots) {
|
|
134
|
+
await edit_storyboard({ storyboard_id: boardId, ops: [{ op: "add_shot", ...shot }] });
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Set `render_mode` per shot: `"keyframe"` where the product or a face must hold steady, `"direct"` for the one or two heavy-motion shots. A launch spot usually has exactly one direct shot.
|
|
139
|
+
|
|
140
|
+
Resolve the model slate **before the shots are written**, with `find_model` for `text_to_image` and `image_to_video`. Name what you picked, put each result's `.ref` on the board with `set_board {image_model, video_model}`, and `load_skill` the `prompting_skill` each result names: the shot `action` and `motion` you are about to write are the prompt, and each model line wants them shaped differently. On-screen copy stays out of the prompt and goes on as a text layer in phase 7.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Phase 4 — Stills, then grade them adversarially (G2)
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
import { render_storyboard_stills, get_storyboard } from "@nodetool-ai/sandbox-nodetool/storyboards";
|
|
148
|
+
|
|
149
|
+
await render_storyboard_stills({ storyboard_id: boardId }); // uses the board's image_model
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Then grade. `get_cost_summary` shows what the stills cost. **This is the step most likely to fail, and it fails as flattery.** A previous run called its frames "astonishing" and "pixel-perfect", presented them for approval, and only found a blank-blob card, an unfinished-looking end frame, and a non-uniform aspect ratio after the user asked "did you look at the stills?".
|
|
153
|
+
|
|
154
|
+
The discipline:
|
|
155
|
+
|
|
156
|
+
1. **Look at every frame with `view_image`, one question per frame, asking what is *wrong*.** "Critique honestly: what is crude, off-brand, or unfinished here?" — never "does this look good?".
|
|
157
|
+
2. **Score adherence** with `score_image_adherence({image, prompt})` where the shot prompt is specific enough to score against.
|
|
158
|
+
3. **Compare frames to each other**, not just to their prompts. The defect that killed two frames in the real run was only visible next to a third that got it right.
|
|
159
|
+
4. **Report per frame: keep, or the specific defect.** Banned words in a grade: stunning, astonishing, gorgeous, perfect, night-and-day. If you cannot name a defect, write "checked: card fidelity, baked text, palette, framing — none found".
|
|
160
|
+
5. **Never claim a property you did not verify.** Measure the aspect ratio with `ffprobe` on each keyframe's `asset://` uri rather than eyeballing it; a plate that came back in the wrong ratio is a defect to name at G2, and it is still standardized at assembly.
|
|
161
|
+
|
|
162
|
+
Two failure classes, two different fixes — never confuse them:
|
|
163
|
+
|
|
164
|
+
- **Entity drift** (wrong product, wrong face, off-palette) → fix the *entity*: sharpen its descriptor or replace its reference, then re-render every shot using it.
|
|
165
|
+
- **Shot failure** (composition, unreadable action, baked marketing copy) → fix the *shot*: rewrite its action line, re-render that shot.
|
|
166
|
+
|
|
167
|
+
Present all keyframes at G2 with the clip count and video model of phase 5. No tool prices the clips in advance; do not quote a number.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Phase 5 — Clips
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
await render_storyboard_clips({ storyboard_id: boardId }); // uses the board's video_model
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
A clip wrong where its keyframe was right is a **motion** problem: one `revise_storyboard_clip`, never a board-wide re-render. Cap at one revision per shot before flagging. Video models overshoot the requested length and can ignore a 9:16 source: `analyze_video` on each clip reports duration and frame before you assemble (`video-audio-continuity` § Check what came back).
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Phase 6 — Voice
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
import { extract_script_from_storyboard } from "@nodetool-ai/sandbox-nodetool/storyboards";
|
|
185
|
+
import { get_script, voice_script_lines } from "@nodetool-ai/sandbox-nodetool/scripts";
|
|
186
|
+
|
|
187
|
+
import { find_model } from "@nodetool-ai/sandbox-nodetool/models";
|
|
188
|
+
import { edit_script } from "@nodetool-ai/sandbox-nodetool/scripts";
|
|
189
|
+
|
|
190
|
+
const script = await extract_script_from_storyboard({ storyboard_id: boardId });
|
|
191
|
+
const scriptId = script.script_id;
|
|
192
|
+
const tts = await find_model({ capability: "text_to_speech" });
|
|
193
|
+
// The extracted cast has no usable voice: Narrator comes back with none, a
|
|
194
|
+
// character with at most its entity's voice_id and no provider or model. Bind
|
|
195
|
+
// one per speaker, or every line reads no_voice.
|
|
196
|
+
const cast = (await get_script({ script_id: scriptId })).cast;
|
|
197
|
+
await edit_script({ script_id: scriptId, ops: cast.map((speaker) => ({
|
|
198
|
+
op: "set_speaker_voice", target: speaker.id,
|
|
199
|
+
provider: tts.ref.provider, model: tts.ref.id, voice: "<the provider's voice name>"
|
|
200
|
+
})) });
|
|
201
|
+
await voice_script_lines({ script_id: scriptId });
|
|
202
|
+
const voiced = await get_script({ script_id: scriptId });
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Pick a voice per speaker that fits the brand voice from phase 1. `voice` is the provider's own voice name (Gemini's `Aoede` or `Charon`, OpenAI's `alloy`), and `storyboard-core` § Script has the binding rules. Every line must come back `voiced`. A `stale` or `no_voice` line is a defect, not a warning. If a voiced line overruns its beat, cut the line — never speed the read.
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Phase 7 — Assemble the cut
|
|
210
|
+
|
|
211
|
+
```js
|
|
212
|
+
import { assemble_storyboard_timeline } from "@nodetool-ai/sandbox-nodetool/storyboards";
|
|
213
|
+
import { edit_timeline, preview_timeline_frame, validate_timeline } from "@nodetool-ai/sandbox-nodetool/timelines";
|
|
214
|
+
import { generate_music } from "@nodetool-ai/sandbox-nodetool/media";
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
1. Assemble the clips.
|
|
218
|
+
2. **Standardize aspect here** — this is where mixed plate ratios get resolved. Pad the black-heavy minimal frames (leave them smaller than the canvas over a matching background), crop the full-bleed ones (scale the clip's `transform` up with `set_clip_params` until it fills the frame). Say which you did to which.
|
|
219
|
+
3. Voice track, aligned to beats.
|
|
220
|
+
4. Music bed at the spot's exact runtime, ducked under VO, with the beat sheet's deliberate pre-CTA silence kept silent.
|
|
221
|
+
5. **Motion and text.** Supers, the CTA card and the end mark are a text layer over the cut, never a render prompt — no model is asked to letter them. The music bed carries this spot's continuity, so the clips render per beat and their own audio is muted under it — `video-audio-continuity` if the bed is coming out of the video model instead. `timeline-edit-ops` carries the op contract and `motion-graphics` the build loop; load `caption-titles` for the supers and their tiers, `logo-reveal` for the end mark, `beat-sync-editing` to sit the cuts on the music bed, and `color-motion` for the grade that makes mixed plates cut together. `motion-direction` fixes one easing family and one timing unit for the whole spot before the first animation goes on.
|
|
222
|
+
6. **Overrun check.** Video models overshoot requested durations. If the cut runs long, trim the **last** clip — trimming an earlier one opens a gap and leaves the runtime unchanged.
|
|
223
|
+
7. `validate_timeline` must come back ok. Fix and re-validate.
|
|
224
|
+
8. Look at frames, not at the document: `preview_timeline_frame` at each cut and each text entry. A change nobody looked at is not done.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## Phase 8 — Report (G3)
|
|
229
|
+
|
|
230
|
+
```js
|
|
231
|
+
import { get_cost_summary } from "@nodetool-ai/sandbox-nodetool/costs";
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
```text
|
|
235
|
+
DELIVERED
|
|
236
|
+
Storyboard / Script / Timeline — each openable and editable
|
|
237
|
+
|
|
238
|
+
COST (from the ledger, not an estimate)
|
|
239
|
+
Stills / Clips / Voice / Music / Agent / TOTAL
|
|
240
|
+
|
|
241
|
+
OPEN QUESTIONS
|
|
242
|
+
Every [CLIENT INPUT NEEDED] still in the project
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Report ledger figures, never a forecast.** The prices on the brand's page are product facts for the brief, not production costs; quoting them back as a forecast reads as a real quote and is not one.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Craft rules (on top of the beat-sheet skill's)
|
|
250
|
+
|
|
251
|
+
* Every claim in VO or on-screen text traces to the page, or is `[CLIENT INPUT NEEDED]`.
|
|
252
|
+
* The product on screen is the page's product. Drift is fixed at the entity.
|
|
253
|
+
* The palette is the page's palette, read from its own tokens.
|
|
254
|
+
* **No lettering the model must invent.** Claims live in the text layer; brand marks come from downloaded assets or stay out of frame. Generated legible marketing copy is a defect even when it looks good.
|
|
255
|
+
* Spend ascending: read → still → clip → revision.
|
|
256
|
+
* Name what was wrong before spending to fix it.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## When it goes wrong
|
|
261
|
+
|
|
262
|
+
* **Page unreachable or thin** → report what you could and could not read, fill the brief with `[CLIENT INPUT NEEDED]`, stop at G1. Never fabricate a product from the URL's words.
|
|
263
|
+
* **A capability errors on argument shape** → re-read the tool contract above before retrying. Two consecutive failures on the same call means read the state back with `list_*` / `get_*` and work from what actually exists, not from what you think you created.
|
|
264
|
+
* **A partial action left orphans** (a board with no shots, an entity with no roster) → find them with `list_*` and finish or delete them. Do not create a second one alongside.
|
|
265
|
+
* **A model is unavailable or unpriced** → say so at the gate it blocks, with alternatives. Never substitute silently.
|
|
266
|
+
* **A shot will not converge** (three renders, still failing adherence) → stop spending, show the best take with what is wrong, offer: accept, rewrite, or change `render_mode`.
|
|
267
|
+
* **Budget cap set** → check the ledger with `get_cost_summary` before each render phase. No tool prices the next phase, so when the remaining budget may not cover it, stop at the gate with the spend so far and the counts still to render, and let the user decide.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: launch-kit
|
|
3
|
+
description: "Produce a NodeTool campaign with shared product and cast entities, stills, and multiple video cuts. Use for a coordinated asset set."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
**Load `/storyboard-core` first.** It carries the loop, the tool contract and
|
|
7
|
+
the gating this skill assumes. Everything below is what changes when the deliverable is
|
|
8
|
+
a set rather than a cut.
|
|
9
|
+
|
|
10
|
+
## What makes this job type its own
|
|
11
|
+
|
|
12
|
+
Keep the grade and cast consistent across the requested campaign deliverables.
|
|
13
|
+
Use the passes below as an organizing pattern, scaled to the brief.
|
|
14
|
+
|
|
15
|
+
So: one entity set, one board `style` string, one image model, one video model, fixed
|
|
16
|
+
at phase A and reused verbatim through phase D. Changing the image model mid-kit is how
|
|
17
|
+
a kit ends up looking like two kits.
|
|
18
|
+
|
|
19
|
+
## Produce the requested campaign
|
|
20
|
+
|
|
21
|
+
Use phases as progress checkpoints. Continue within the authorized deliverables
|
|
22
|
+
and budget. Pause for a user-requested review or a missing decision that changes
|
|
23
|
+
the output or spending scope. Do not reconfirm authorization already given. When
|
|
24
|
+
the brief does not fix the deliverable counts (frames, clips, cuts), confirm them
|
|
25
|
+
with the user before the first clip pass.
|
|
26
|
+
|
|
27
|
+
### A — Cast and lock
|
|
28
|
+
|
|
29
|
+
1. Product entity from the flat: save the asset, `create_entity {kind: "prop"}` with a
|
|
30
|
+
descriptor of geometry and finish. Keep the other angles (three-quarter, back, top,
|
|
31
|
+
on-figure) as plain assets. An entity is one image asset, so it carries one
|
|
32
|
+
reference into a prompt.
|
|
33
|
+
2. Character entity from the on-body photo. Descriptor is face, hair and build, with
|
|
34
|
+
**no wardrobe** — wardrobe belongs in shot text so the same person can change
|
|
35
|
+
outfits across the kit.
|
|
36
|
+
3. Optionally a style entity: `create_entity {kind: "style", palette}`. A style entity
|
|
37
|
+
applies to every shot on every board, which is exactly what a kit wants.
|
|
38
|
+
4. `find_model` once each for `text_to_image` and `image_to_video`. Write them and the
|
|
39
|
+
entities onto the board with `set_board`.
|
|
40
|
+
5. `memory_save` the grade, the model ids, the board id and the entity ids, with the
|
|
41
|
+
ids in `resources`. This is what makes the kit resumable next week — `share_result`
|
|
42
|
+
would be discarded with the run.
|
|
43
|
+
|
|
44
|
+
Report entity ids, the grade, and model ids. Continue with the requested assets.
|
|
45
|
+
|
|
46
|
+
### B — Still campaign frames
|
|
47
|
+
|
|
48
|
+
Produce the requested number and mix of stills. Use take selection when the user
|
|
49
|
+
reserved that choice. A stills-only request ends here. Otherwise continue to the
|
|
50
|
+
authorized video deliverables with the selected frames.
|
|
51
|
+
|
|
52
|
+
Vary the crop in `camera.framing`; the still prompt reads it, with the angle and lens,
|
|
53
|
+
so it needs no repeat in `action`. Give product-only frames an explicit `entity_ids` naming the product
|
|
54
|
+
alone, so the model's descriptor does not season a frame she is not in.
|
|
55
|
+
|
|
56
|
+
Report the selected asset ids and continue within the requested scope.
|
|
57
|
+
|
|
58
|
+
### C — Social cuts
|
|
59
|
+
|
|
60
|
+
Use the brief's clip count, durations, and ratios, animated from the selected
|
|
61
|
+
phase-B stills. Honor any requested approval of those stills. Reuse them to keep
|
|
62
|
+
the look consistent.
|
|
63
|
+
|
|
64
|
+
A separate board per aspect ratio — `aspect_ratio` is a board field, not a shot field,
|
|
65
|
+
so one board cannot serve 9:16 and 16:9. Same entities, same style, same models on each.
|
|
66
|
+
To animate an approved phase-B still on the new board without re-rendering it,
|
|
67
|
+
`edit_storyboard` → `{op: "add_keyframe_version", target, asset_id}` attaches it as the
|
|
68
|
+
shot's selected still. It only fits when that still was framed for the new ratio.
|
|
69
|
+
|
|
70
|
+
Report clip asset ids. Continue if the requested kit includes a longer film.
|
|
71
|
+
|
|
72
|
+
### D — The long film
|
|
73
|
+
|
|
74
|
+
Build the longer piece at the requested duration and aspect ratio, with stable slugs and timing. Assemble it, then finish the sound:
|
|
75
|
+
`edit_timeline` → `get_state`, then `set_clip_params {muted: true}` on the "Shot Audio"
|
|
76
|
+
clips under any voiceover, and add music with `generate_music` + `add_track` +
|
|
77
|
+
`add_media_clip` at a low `volumeDb`.
|
|
78
|
+
|
|
79
|
+
`validate_timeline` before calling it done.
|
|
80
|
+
|
|
81
|
+
## Spend
|
|
82
|
+
|
|
83
|
+
Report shot and frame counts per phase before each render — never a dollar figure, as
|
|
84
|
+
nothing prices a render. Afterwards, `get_cost_summary` gives what the kit actually
|
|
85
|
+
cost.
|
|
86
|
+
|
|
87
|
+
A twelve-frame still pass is cheap; three clip passes plus a 30-second film is not.
|
|
88
|
+
If the user is deciding, that is the ratio to tell them.
|
|
89
|
+
|
|
90
|
+
## Recovering a kit later
|
|
91
|
+
|
|
92
|
+
`list_storyboards` shows shot counts and how many shots have a still and a clip.
|
|
93
|
+
`memory_search` finds the phase-A note with the grade and the entity ids. Together
|
|
94
|
+
those are enough to resume without re-deriving the look.
|
|
95
|
+
|
|
96
|
+
## Brief
|
|
97
|
+
|
|
98
|
+
Adapt these examples to the user's brief. Example deliverables, style choices,
|
|
99
|
+
and approval checkpoints apply only when the user adopts them.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Launch kit for [BRAND] [SKU] from the attached flat and on-body photos.
|
|
103
|
+
Stop after each phase and wait for me.
|
|
104
|
+
|
|
105
|
+
A. Cast and lock. Product entity from the flat (descriptor = geometry and
|
|
106
|
+
finish). Character entity from the on-body photo (descriptor = face, hair and
|
|
107
|
+
build, no wardrobe). Pick one image model and one video model with find_model
|
|
108
|
+
and write both onto the board. Grade: [cold film / warm daylight / black
|
|
109
|
+
studio]. memory_save the grade, both model ids and every entity id.
|
|
110
|
+
B. Twelve still campaign frames, mixed crops, on-figure and product-only. Stills
|
|
111
|
+
only — stop before clips. Product-only frames get entity_ids naming the
|
|
112
|
+
product alone.
|
|
113
|
+
C. Three 9:16 clips, 6-8s, animated from stills I approved in B. Do not
|
|
114
|
+
re-render those stills. A separate board for the vertical ratio.
|
|
115
|
+
D. One 30s 16:9 film, slugged and timed. Assemble, mute the Shot Audio clips
|
|
116
|
+
under the voiceover, add a music bed low, then validate_timeline.
|
|
117
|
+
|
|
118
|
+
Same grade and the same two models from A through D. Report counts before each
|
|
119
|
+
render, not dollars.
|
|
120
|
+
```
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: logo-reveal
|
|
3
|
+
description: Animate a brand mark on a NodeTool timeline — stroke draw-on, mask wipe, staggered build, wordmark type, an idle loop, and landing the settle on a sound logo. Use for an intro sting, a sign-off, an end card, a splash, or a loader loop. Not for designing the mark itself.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Logo Reveal → the sting
|
|
7
|
+
|
|
8
|
+
One idea per logo. Draw it, build it, or wipe it — never all three. The whole
|
|
9
|
+
reveal runs 800–2500ms and ends on the clean mark, held.
|
|
10
|
+
|
|
11
|
+
`motion-graphics` is the build loop; `timeline-edit-ops` carries the op
|
|
12
|
+
contract. `motion-direction` decides which personality the sting belongs to;
|
|
13
|
+
a logo is usually the one place a piece is allowed its overshoot.
|
|
14
|
+
|
|
15
|
+
## The same three patterns, in code
|
|
16
|
+
|
|
17
|
+
**Draw-on** — a path element and `el.draw()`, which is `enter({from: {trimEnd: 0}})`:
|
|
18
|
+
|
|
19
|
+
```js
|
|
20
|
+
// fragment
|
|
21
|
+
const mark = s.path([["M", -40, 30], ["L", 0, -30], ["L", 40, 30]], { stroke: "#111111" });
|
|
22
|
+
mark.draw({ dur: 1.1, ease: "cubic-bezier(0.65,0,0.35,1)" });
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Wordmark stagger** — `by` and `staggerMs` on `el.enter()`, per character for
|
|
26
|
+
a short mark:
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
// fragment
|
|
30
|
+
const wordmark = s.text("Northwind", { size: 64, weight: 700 });
|
|
31
|
+
wordmark.enter({ from: { scale: 0.9, opacity: 0 }, dur: 0.38, by: "character", staggerMs: 45 });
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Lockup row** — mark beside wordmark, one flex container (`frame-composition`
|
|
35
|
+
covers the container vocabulary in full):
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
// fragment
|
|
39
|
+
s.row([mark, wordmark], { gap: 16, align: "center" });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Reveal the container first if the mark and wordmark should feel like one
|
|
43
|
+
build, or stagger the row's own children through `s.stagger()` for a build-on
|
|
44
|
+
that still keeps its lockup spacing.
|
|
45
|
+
|
|
46
|
+
## Pick one technique
|
|
47
|
+
|
|
48
|
+
| Technique | Best for | How, on the timeline |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| Stroke draw-on | Line marks, monograms, signatures | A `path` shape clip, `trimStart`/`trimEnd` animated |
|
|
51
|
+
| Mask wipe | Filled shapes, gradient marks | `wipe` preset, or a `set_mask` rect whose `mask.width` (or `x`) a document `styleTracks` entry animates |
|
|
52
|
+
| Build-on | Multi-part marks, grids | One clip per part, in a group, `delayMs` staggered |
|
|
53
|
+
| Wordmark | Logotypes | A text clip with a per-character or per-word `stagger` |
|
|
54
|
+
| Idle loop | Loaders, ambient splash | `breathe` or `rotate` as a `loop` |
|
|
55
|
+
| Scale settle | Anything, when in doubt | `pop` with a small `overshoot` |
|
|
56
|
+
|
|
57
|
+
## Stroke draw-on
|
|
58
|
+
|
|
59
|
+
`trimStart` and `trimEnd` stroke only a sub-range of a path, 0..1. Drawing is
|
|
60
|
+
`trimEnd` running 0 to 1 with the stroke visible and the fill arriving after:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{"op": "add_shape_clip", "shape": {"kind": "path", "d": "M0.1 0.7 L0.3 0.2 L0.5 0.7",
|
|
64
|
+
"stroke": "#111111", "strokeWidthPx": 6, "lineCap": "round",
|
|
65
|
+
"trimStart": 0, "trimEnd": 0}, "durationMs": 2000}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{"op": "animate_clip", "target": "Path", "animations": [
|
|
70
|
+
{"role": "in", "preset": "custom", "durationMs": 1100,
|
|
71
|
+
"easing": "cubic-bezier(0.65,0,0.35,1)",
|
|
72
|
+
"curves": [{"property": "trimEnd", "keyframes": [
|
|
73
|
+
{"t": 0, "value": 0}, {"t": 1, "value": 1}]}]}]}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Both trim channels fold as **replace**: one animation drives one of them, and two
|
|
77
|
+
overlapping in time means the later wins and the earlier is discarded, reported
|
|
78
|
+
as `replace_curves_overlap`. Draw with `trimEnd` alone; use `trimStart` chasing
|
|
79
|
+
it only for a stroke that draws and erases, and put the two in separate windows.
|
|
80
|
+
|
|
81
|
+
Stagger several strokes by stepping each path clip's `delayMs` by the
|
|
82
|
+
clip-to-clip stagger in `motion-principles`, in the order a hand would draw
|
|
83
|
+
them.
|
|
84
|
+
|
|
85
|
+
`add_shape_clip` stores a path's `d` without parsing it. A `d` the renderer
|
|
86
|
+
cannot parse reports `shape_path_invalid` (an error) from `validate_timeline`,
|
|
87
|
+
and an unknown `kind` reports `unknown_shape_kind`. Either draws nothing — a
|
|
88
|
+
mark that vanished is usually one of those two.
|
|
89
|
+
|
|
90
|
+
## Mask wipe
|
|
91
|
+
|
|
92
|
+
For a filled mark, reveal rather than draw. `{"role": "in", "preset": "wipe",
|
|
93
|
+
"durationMs": 700, "params": {"direction": "left", "softness": 0.05}}` sweeps a
|
|
94
|
+
feathered edge across the layer. `softness` 0 is a hard edge, which reads as
|
|
95
|
+
graphic; 0.1 reads as light passing over.
|
|
96
|
+
|
|
97
|
+
A `custom` curve driving `wipeProgress` needs a `mask` on the same animation —
|
|
98
|
+
`{direction, softness}` — because a wipe with no edge to run against cannot be
|
|
99
|
+
inferred. `wipeProgress` folds as **min**, so two wipes on one layer keep the
|
|
100
|
+
tighter one.
|
|
101
|
+
|
|
102
|
+
## Build-on
|
|
103
|
+
|
|
104
|
+
One clip per part, each on its own overlay track, all parented to one group with
|
|
105
|
+
`add_group`. Reveal the container first, then accents, then the wordmark last so
|
|
106
|
+
the eye lands on the name.
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{"op": "animate_clip", "target": "Mark part 2", "animations": [
|
|
110
|
+
{"role": "in", "preset": "pop", "durationMs": 500, "delayMs": 80,
|
|
111
|
+
"params": {"overshoot": 1.12}}]}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Step by the clip-to-clip stagger in `motion-principles`, as the 80ms
|
|
115
|
+
`delayMs` on the second part does. Then animate the **group** for anything that moves the whole
|
|
116
|
+
assembly — one `fade` out on the group beats three chances for the parts to
|
|
117
|
+
drift apart. The group's opacity multiplies into each child.
|
|
118
|
+
|
|
119
|
+
When the parts already have animations, `{"op": "stagger_animations",
|
|
120
|
+
"clip_ids": ["mark-id", "wordmark-id", "tagline-id"], "offset_ms": 80}`
|
|
121
|
+
adds delay in that order without moving their media. Use clip IDs, with at
|
|
122
|
+
least two, and give every selected clip an animation first. The first keeps
|
|
123
|
+
its delay. For a repeating field of identical marks, the document's
|
|
124
|
+
`repeater` sets copy count, position step, and time step on one clip. Use a
|
|
125
|
+
repeater for a pattern, and separate clips for parts that need distinct art.
|
|
126
|
+
|
|
127
|
+
Keep a follower aligned by storing `animationLinks` on its clip when it should
|
|
128
|
+
read the mark's position, scale, rotation, or opacity. A link follows the
|
|
129
|
+
source's authored animation and does not chain through other links. Put the mark and the wordmark in a flex row (`layout: {display: "flex",
|
|
130
|
+
flexDirection: "row", gap, alignItems: "center"}` on a group clip, both as
|
|
131
|
+
its real children via `parentId`) so the wordmark's position follows the
|
|
132
|
+
mark's changing box; a backing plate is a third child with `flexItem:
|
|
133
|
+
{position: "absolute", inset: 0}` behind the wordmark. These are document
|
|
134
|
+
fields, so read and write the full document with `get_timeline` and
|
|
135
|
+
`set_timeline_document`.
|
|
136
|
+
`set_clip_params` refuses `repeater`, `animationLinks`, and `layout`.
|
|
137
|
+
|
|
138
|
+
## Wordmark
|
|
139
|
+
|
|
140
|
+
A text clip with a stagger. Per character for a short mark, per word for a
|
|
141
|
+
lockup with a tagline:
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{"role": "in", "preset": "pop", "durationMs": 380,
|
|
145
|
+
"stagger": {"unit": "character", "offsetMs": 45, "from": "start"}}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Work the span out — `durationMs + offsetMs × (units − 1)` — and compare it
|
|
149
|
+
against the clip. A span that does not fit is compressed silently, and a
|
|
150
|
+
compressed wordmark is the reason a reveal "does not look staggered".
|
|
151
|
+
`stagger_compressed` is the warning.
|
|
152
|
+
|
|
153
|
+
Set the family to a bundled one — `Inter`, `Space Grotesk`, `Bebas Neue`,
|
|
154
|
+
`Playfair Display`, `Lora`, `JetBrains Mono` — or the render and the editor
|
|
155
|
+
resolve it differently and the lockup's width changes under you.
|
|
156
|
+
|
|
157
|
+
## What not to do to a mark
|
|
158
|
+
|
|
159
|
+
Scale, opacity, rotation about the mark's own anchor, and position are safe.
|
|
160
|
+
`scaleX` and `scaleY` on their own are not: `squash` distorts the geometry, and a
|
|
161
|
+
distorted logo is a brand problem rather than a taste one. Keep motion out of the
|
|
162
|
+
clearspace — a support element sliding through the mark's margin cheapens it.
|
|
163
|
+
|
|
164
|
+
## Landing on a sound logo
|
|
165
|
+
|
|
166
|
+
Decide the audio first. A sound logo that does not exist yet is a 1–3 s
|
|
167
|
+
effects brief — source, action, space — on ElevenLabs sound effects
|
|
168
|
+
(`elevenlabs-audio-prompting`) or a `TrackType: SFX` prompt on Stable Audio
|
|
169
|
+
(`stable-audio-prompting`); a 30-second request for a hit is a hit followed by
|
|
170
|
+
room tone. Then `detect_audio_events` on the sting reports `onsets.times` in
|
|
171
|
+
**seconds**; multiply by 1000. Time the reveal so the mark reaches full opacity
|
|
172
|
+
and scale on that millisecond:
|
|
173
|
+
|
|
174
|
+
`delayMs` on the `in` animation = onset ms − the clip's `startMs` −
|
|
175
|
+
`durationMs`. Then add the accent on the hit itself, as a separate emphasis:
|
|
176
|
+
`{"role": "emphasis", "preset": "pulse", "durationMs": 220, "delayMs": <onset −
|
|
177
|
+
startMs>, "params": {"intensity": 0.04}}`.
|
|
178
|
+
|
|
179
|
+
A settle that floats free of the sound reads as cheap even when both are good.
|
|
180
|
+
`snap_to_beats` is the blunt version when the whole end card has to sit on the
|
|
181
|
+
grid.
|
|
182
|
+
|
|
183
|
+
For a mark that should stay on beat if the sequence tempo changes, put a
|
|
184
|
+
`beat: {index, scope: "sequence", offsetMs?}` anchor on its animation in the
|
|
185
|
+
full document. The index is one-based and `delayMs` adds to it. The
|
|
186
|
+
`animate_clip` tool input does not accept `beat`. Keep the stored animation's
|
|
187
|
+
`id`, `role`, `preset`, and `durationMs` when adding it. If the sound logo's measured
|
|
188
|
+
onset is the source of truth, keep the absolute delay or use
|
|
189
|
+
`bake_audio_animation` for audio-driven motion instead.
|
|
190
|
+
|
|
191
|
+
## Idle loop
|
|
192
|
+
|
|
193
|
+
For a loader or a splash that waits, a `loop` at low amplitude: `breathe` with
|
|
194
|
+
`intensity` 0.02–0.04, or `rotate` on a mark that is radially symmetric. Set
|
|
195
|
+
`durationMs` to the cycle, 2000–4000ms, and remember a cycle longer than the clip
|
|
196
|
+
shows a fragment of the motion.
|
|
197
|
+
|
|
198
|
+
Never loop a wordmark somebody is reading.
|
|
199
|
+
|
|
200
|
+
## Hold the end frame
|
|
201
|
+
|
|
202
|
+
The last thing on screen is the static mark. Leave the end hold
|
|
203
|
+
`motion-principles` sets (at least 1000ms) after the reveal completes with
|
|
204
|
+
nothing animating — an end card that cuts the instant it
|
|
205
|
+
settles reads as a mistake. On a 2500ms clip with a 1100ms draw and a 220ms
|
|
206
|
+
accent, that is satisfied; check it rather than assuming.
|
|
207
|
+
|
|
208
|
+
## Start from a rig when it fits
|
|
209
|
+
|
|
210
|
+
`list_compositions` and `get_composition` show the shipped `logo-sting` rig and
|
|
211
|
+
its parameters. `insert_composition` places it as a group with fresh clip IDs.
|
|
212
|
+
Inspect those IDs before changing its timing or staggering its parts. Use the
|
|
213
|
+
`title-slam` or `word-cards` examples for a typographic reveal; they are
|
|
214
|
+
starting documents rather than tool presets.
|
|
215
|
+
|
|
216
|
+
## Check it
|
|
217
|
+
|
|
218
|
+
- `validate_timeline`: `replace_curves_overlap` on the trims, `stagger_compressed`
|
|
219
|
+
on the wordmark, `animation_exceeds_clip` on a reveal longer than its clip,
|
|
220
|
+
`shape_path_invalid` or `unknown_shape_kind` on a mark that draws nothing.
|
|
221
|
+
- `preview_timeline_frame` at the midpoint of the draw, a held moment, and the
|
|
222
|
+
last frame. The midpoint is where a half-drawn path or a stroke that never
|
|
223
|
+
closes shows up; the last frame is where you confirm the mark is clean and
|
|
224
|
+
complete.
|
|
225
|
+
- Then `render_timeline`, because a sting is judged at speed.
|