wizz-method 2.11.2 → 2.12.0
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/package.json +1 -1
- package/skills-registry.yaml +19 -5
- package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/04-criativos.md +1 -1
- package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/11-checklist-roi.md +1 -1
- package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/12-mineracao-ofertas.md +1 -1
- package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/pontos-em-aberto.md +2 -2
- package/src/modules/wizz/_shared/video-pipeline.md +11 -3
- package/src/modules/wizz/agents/wizz-designer/SKILL.md +1 -1
- package/src/modules/wizz/agents/wizz-social/SKILL.md +1 -0
- package/src/skills-lib/wizz-remotion-best-practices/rules/sfx.md +3 -1
- package/src/skills-lib/wizz-router/references/routing-table-flat.md +1 -0
- package/src/skills-lib/wizz-video-effects/SKILL.md +85 -0
- package/src/skills-lib/wizz-video-effects/assets/effects.config.template.json +178 -0
- package/src/skills-lib/wizz-video-effects/assets/remotion/EffectsLayer.tsx +87 -0
- package/src/skills-lib/wizz-video-effects/assets/remotion/README.md +37 -0
- package/src/skills-lib/wizz-video-effects/assets/remotion/useEffects.ts +120 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/CREDITS.md +56 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/cash.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/chime.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/click-soft.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/click.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/coin.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/error.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/impact-bass-1.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/impact-bass-2.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/key-press.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/notif.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/notification.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/paste.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/ping.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/pop.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/riser-metal.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/riser.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/send.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/sparkle.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/stamp.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/success.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/type.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/typing.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/whoosh-cinematic.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/whoosh-short.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/assets/sfx/whoosh.mp3 +0 -0
- package/src/skills-lib/wizz-video-effects/references/audio.md +87 -0
- package/src/skills-lib/wizz-video-effects/references/data-contract.md +145 -0
- package/src/skills-lib/wizz-video-effects/references/do-not-use.md +54 -0
- package/src/skills-lib/wizz-video-effects/references/editing-patterns.md +58 -0
- package/src/skills-lib/wizz-video-effects/references/gates.md +37 -0
- package/src/skills-lib/wizz-video-effects/references/sfx-licensing.md +40 -0
- package/src/skills-lib/wizz-video-effects/references/when-to-use.md +129 -0
- package/src/skills-lib/wizz-video-effects/scripts/budget.py +211 -0
- package/src/skills-lib/wizz-video-effects/scripts/check_excess.py +218 -0
- package/src/skills-lib/wizz-video-effects/scripts/check_loudness.py +175 -0
- package/src/skills-lib/wizz-video-effects/scripts/check_rhythm.py +137 -0
- package/src/skills-lib/wizz-video-effects/scripts/check_safe_zone.py +221 -0
- package/src/skills-lib/wizz-video-effects/scripts/config.py +583 -0
- package/src/skills-lib/wizz-video-effects/scripts/fill_breaths.py +355 -0
- package/src/skills-lib/wizz-video-effects/scripts/measure_density.py +122 -0
- package/src/skills-lib/wizz-video-effects/scripts/normalize_audio.py +189 -0
- package/src/skills-lib/wizz-video-effects/scripts/sound.py +342 -0
- package/src/skills-lib/wizz-video-effects/scripts/test_budget.py +131 -0
- package/src/skills-lib/wizz-video-effects/scripts/test_check_loudness.py +156 -0
- package/src/skills-lib/wizz-video-effects/scripts/test_checks.py +190 -0
- package/src/skills-lib/wizz-video-effects/scripts/test_config.py +135 -0
- package/src/skills-lib/wizz-video-effects/scripts/test_fill_breaths.py +179 -0
- package/src/skills-lib/wizz-video-effects/scripts/test_sound.py +173 -0
- package/src/skills-lib/wizz-video-effects/scripts/timeline.py +85 -0
package/package.json
CHANGED
package/skills-registry.yaml
CHANGED
|
@@ -159,7 +159,7 @@ areas:
|
|
|
159
159
|
- id: wizz-animate
|
|
160
160
|
entry: true
|
|
161
161
|
door: motion
|
|
162
|
-
when: "PORTA de motion/assets: executar animação e gerar mídia. Craft de micro-interação (regras do Emil Kowalski: saída > entrada, só transform/opacity, reduced-motion). Puxa: wizz-design-motion-principles (auditar animação existente), wizz-remotion-best-practices (vídeo em React), wizz-canvas-design (PNG/PDF por código) e wizz-algorithmic-art (visual generativo)."
|
|
162
|
+
when: "PORTA de motion/assets: executar animação e gerar mídia. Craft de micro-interação (regras do Emil Kowalski: saída > entrada, só transform/opacity, reduced-motion). Puxa: wizz-design-motion-principles (auditar animação existente), wizz-remotion-best-practices (vídeo em React), wizz-video-effects (efeitos e SFX de ad/Reels com portões), wizz-canvas-design (PNG/PDF por código) e wizz-algorithmic-art (visual generativo)."
|
|
163
163
|
rel:
|
|
164
164
|
pairs_with: [wizz-design-motion-principles]
|
|
165
165
|
- id: wizz-design-motion-principles
|
|
@@ -171,7 +171,7 @@ areas:
|
|
|
171
171
|
door: motion
|
|
172
172
|
when: "MONTAR o filme: vídeo em React (Remotion) com áudio, SFX, legenda e timing com precisão de frame. No pipeline de vídeo é o Remotion que faz a montagem final; o hyperframes só desenha as telas mudas (regra: HyperFrames desenha, Remotion monta — ver _shared/video-pipeline.md do módulo wizz)."
|
|
173
173
|
rel:
|
|
174
|
-
after: [buttercut, ctc-align, get-brolls, hyperframes, wizz-motion-3d-director]
|
|
174
|
+
after: [buttercut, ctc-align, get-brolls, hyperframes, wizz-motion-3d-director, wizz-video-effects]
|
|
175
175
|
- id: wizz-canvas-design
|
|
176
176
|
door: motion
|
|
177
177
|
when: "Gerar PNG/PDF editável por código (carrossel, quote card, infográfico) com fonte/cor/logo sempre certos. Traz canvas-fonts."
|
|
@@ -187,13 +187,27 @@ areas:
|
|
|
187
187
|
when: "Timing por forced alignment: timestamps palavra a palavra de narração TTS para sincronizar legenda, corte de cena e animação com a fala. Fonte única de timing no pipeline de vídeo; substitui Whisper e estimativa manual."
|
|
188
188
|
rel:
|
|
189
189
|
after: [voicebox]
|
|
190
|
+
before: [wizz-remotion-best-practices, wizz-video-effects]
|
|
191
|
+
# Motor de decisão de efeitos (Python) portado do sistema de efeitos do
|
|
192
|
+
# trafego em 2026-09-24: orçamento anti-excesso, som por evento semântico,
|
|
193
|
+
# efeitos nos respiros, portões de zona segura (ad/orgânico), densidade,
|
|
194
|
+
# excesso, ritmo e loudness. Não monta o filme: isso é do Remotion.
|
|
195
|
+
- id: wizz-video-effects
|
|
196
|
+
door: motion
|
|
197
|
+
when: "Decidir e conferir os EFEITOS e o SOM de um vídeo curto vertical (ad ou Reels): que efeito entra (punch, flash, shake, transição), onde e quando não entra, que SFX toca em cada acontecimento e a que volume, ducking, loudness do master e portões de zona segura, densidade, excesso e ritmo do gancho. Gatilhos: 'efeitos sonoros do criativo', 'SFX do Reels', 'o vídeo tá com zoom demais', 'conferir zona segura do anúncio', 'LUFS do vídeo'."
|
|
198
|
+
not_when:
|
|
199
|
+
- "Montar o filme em React (sequência de cenas, legenda, <Audio>): é wizz-remotion-best-practices; esta skill decide os efeitos que ele monta."
|
|
200
|
+
- "Filme de lançamento de produto com trilha e direção visual: é wizz-huashu-design."
|
|
201
|
+
rel:
|
|
202
|
+
after: [ctc-align, hyperframes]
|
|
190
203
|
before: [wizz-remotion-best-practices]
|
|
204
|
+
pairs_with: [wizz-ad-creative]
|
|
191
205
|
clis:
|
|
192
206
|
- id: hyperframes
|
|
193
207
|
when: "DESENHAR telas de vídeo em HTML/CSS (HTML→MP4, agent-native, 20+ skills): cena isolada e MUDA — maqueta de WhatsApp/ChatGPT, gráfico animado, título, loop. NÃO usar para montagem com áudio/SFX/legenda sincronizada (posicionador de áudio falhou em produção, ~2,76s de drift; CSS vaza entre telas): a montagem final é do Remotion (regra: HyperFrames desenha, Remotion monta — ver _shared/video-pipeline.md do módulo wizz). Precisa ffmpeg + Node 22 + Chrome headless."
|
|
194
208
|
not_when: "Montagem com áudio/SFX/legenda sincronizada (posicionador de áudio falhou em produção, ~2,76s de drift; CSS vaza entre telas)."
|
|
195
209
|
rel:
|
|
196
|
-
before: [wizz-remotion-best-practices]
|
|
210
|
+
before: [wizz-remotion-best-practices, wizz-video-effects]
|
|
197
211
|
platform: [darwin, linux]
|
|
198
212
|
# O check confere o SHA fixado, não só a existência da pasta: o bundle é
|
|
199
213
|
# copiado (não clonado), então o pin é gravado num marcador no install.
|
|
@@ -283,7 +297,7 @@ areas:
|
|
|
283
297
|
when: "Timestamps palavra a palavra CONFIÁVEIS de narração TTS (texto conhecido): sincronizar legenda, corte de cena, animação e elemento gráfico com a fala. Fonte única de timing em vídeo — substitui timestamps do Whisper e estimativa manual. Skill da lib: ctc-align (instalação + helper align.py + regras de âncora monotônica e duração mínima). Roda em CPU, sem CUDA."
|
|
284
298
|
rel:
|
|
285
299
|
after: [voicebox]
|
|
286
|
-
before: [wizz-remotion-best-practices]
|
|
300
|
+
before: [wizz-remotion-best-practices, wizz-video-effects]
|
|
287
301
|
platform: [darwin, linux]
|
|
288
302
|
check: "test -x ./tools/ctc-align/.venv/bin/ctc-forced-aligner"
|
|
289
303
|
install: 'mkdir -p tools/ctc-align && uv venv --python 3.12 tools/ctc-align/.venv && uv pip install --python tools/ctc-align/.venv/bin/python "git+https://github.com/MahmoudAshraf97/ctc-forced-aligner.git"'
|
|
@@ -608,7 +622,7 @@ areas:
|
|
|
608
622
|
- id: wizz-ad-creative
|
|
609
623
|
when: "Gerar/iterar criativos e headlines de anúncio em escala (RSA, variações)."
|
|
610
624
|
rel:
|
|
611
|
-
pairs_with: [fal-video, wizz-copywriting, wizz-paid-ads]
|
|
625
|
+
pairs_with: [fal-video, wizz-copywriting, wizz-paid-ads, wizz-video-effects]
|
|
612
626
|
- id: wizz-analytics-tracking
|
|
613
627
|
when: "Tracking de conversão pros anúncios (compartilhada com growth)."
|
|
614
628
|
rel:
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
### 4.1 Creative structure
|
|
10
10
|
|
|
11
|
-
1. **Hook** (first 3 seconds): captures and filters. *(
|
|
11
|
+
1. **Hook** (first 3 seconds): captures and filters. *(adopted rule, see [open point 19](pontos-em-aberto.md).)*
|
|
12
12
|
2. **Body**: useful content, social validation, an engaging story.
|
|
13
13
|
3. **CTA**: urgent call to action.
|
|
14
14
|
|
package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/11-checklist-roi.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
### 11.6 Creative and campaign
|
|
32
32
|
|
|
33
|
-
- Hook in the first
|
|
33
|
+
- Hook in the first 3 seconds (see [open point 19](pontos-em-aberto.md)), captions always on, 9:16, no studio look. **Link CTR > 1.5% and CPC < R$2** — outside that range the problem is the creative.
|
|
34
34
|
- **Rule for reading the two metrics**: CTR below 1.5% = creative (weak hook, nobody clicks). CPC above expected with good CTR = audience or VSL, not creative. While either metric is off, **don't touch the page, order bump, or upsell** — the leak is upstream. Broad audience: **the creative does the segmenting, not the interest targeting**. Found the winner: new variations and angles **within the same campaign**.
|
|
35
35
|
- Campaign: CBO with Advantage on everything (per the source, ABO is going away). **Initial bid cap = break-even × 1.5** (§3.2 and §3.7 of [`03`](03-campanha-bidcap.md), [point 58](pontos-em-aberto.md); the source said "full average ticket, never half"). Inflated budget: a R$30 ticket calls for a R$300-500/day campaign. Zero targeting (age and placement on automatic).
|
|
36
36
|
- Verdict rules: spent almost nothing in the first **17h** = the offer didn't pass (tested for free). A creative is only cut with **all 4 conditions of §3.7** of [`03`](03-campanha-bidcap.md): spend ≥ 3x the Bid Cap, ROAS below break-even, zero sales and no recent structural change (don't touch it before that; [point 59](pontos-em-aberto.md)). **3 days of positive ROI** = offer validated: **don't lower the bid cap** while performance is high (§3.2, [point 57](pontos-em-aberto.md)); scale in the lever order of §3.7 (raise the Bid Cap first) with the step from the ROAS ruler of §3.2 (≥ 2.5 → +50% · 2.0 to 2.5 → +30% · < 2.0 → +20%; [point 54](pontos-em-aberto.md)).
|
package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/12-mineracao-ofertas.md
CHANGED
|
@@ -75,7 +75,7 @@ Self-contained interactive mind map (HTML with the data embedded in a `BLOCKS` a
|
|
|
75
75
|
| **Encontrar** (Find) | potential market ("if there's no ad running, there's no market"), an offer to model, a specialist | 12.1, 12.2, 9.2 |
|
|
76
76
|
| **Entender** (Understand) | market, audience (pain/desire/awareness, "steal their words" from comments and reviews), a single unified list of the offer's features, a single persona with a name and a routine | 8, 12.5 |
|
|
77
77
|
| **Estruturar** (Structure) | product, irresistible offer, promise, unique mechanisms, deliverables ("the word PDF never appears"), a bonus that knocks down an objection, order bump (3 to 5 active), upsell/downsell | 5, 11.1, 11.4 |
|
|
78
|
-
| **Executar** (Execute) | copy in the order **page, then ad, then VSL**; 9x16 creative with a hook in the first
|
|
78
|
+
| **Executar** (Execute) | copy in the order **page, then ad, then VSL**; 9x16 creative with a hook in the first 3 seconds; page with two plans and a cross-sell pop-up; checkout with email only and Pix >75%; Instagram BBF; VSL 90s-2min; ads manager set up before spending | 6.1, 6.2, 11.2, 11.3, 11.4 |
|
|
79
79
|
| **Escalar** (Scale) | separate BM and account; CBO with an initial bid cap = break-even × 1.5 ("ABO is going away"); 17h with no spend = the offer didn't pass; a creative is only cut with all 4 conditions of 3.7 together (spend ≥ 3x the Bid Cap); 3 green days = offer validated, and from there the budget rises by the ROAS ruler of 3.2 | 3.2, 3.7, 11.6 |
|
|
80
80
|
|
|
81
81
|
**Note:** the source said "bid cap at full ticket", "3x the ticket with ROI<1 kills it" and "3 green days raises up to 50%". Since 2026-09-24 the rulers in [`03`](03-campanha-bidcap.md) apply ([points 56, 58 and 59](pontos-em-aberto.md)).
|
package/src/modules/lowticket/skills/wizz-lowticket-metodologia/knowledge/pontos-em-aberto.md
CHANGED
|
@@ -22,7 +22,7 @@ Contradictions and gaps found while distilling this methodology, pulled into thi
|
|
|
22
22
|
16. **VSL on the page: does it help or hurt?** Section 11.3 mandates a 90s-2min VSL in low ticket and 6.2 mandates a VSL right after the headline. But the funnel data in 6A.9 shows the page **without** a VSL performing better (87→35 vs. 75→32). Likely reading: the VSL helps when the offer needs explaining and hurts when the product explains itself. Confirm with the source.
|
|
23
23
|
17. **"More profitable": training an expert, or closing a co-production deal?** The Instagram source says training an expert is the most profitable path (no prior audience, no bargaining); it also says co-production is the most profitable and sustainable model. The two pieces of material compare against different baselines: co-production wins **against running solo**, and training wins **against co-production**, because it doesn't split the profit. Reading adopted in section 9.2: train > partner > solo, with the partnership winning on execution speed. Neither source compares all three side by side, so confirm with the source.
|
|
24
24
|
18. ~~**VSL length: 90s-2min or 7-9 minutes?**~~ **RULE ADOPTED (2026-08-28, by reading the material; not confirmed with the source.)** What decides it is **what the VSL needs to prove**, not the ticket alone. A product that proves itself on screen (showrun, a recording opening the product from the inside, 11.3): **90s to 2min**. An offer that needs to build a mechanism and authority before reaching the price: **7 to 9 min**, in the 6-block structure (4.7). The 6-block structure holds in both cases: it changes the time per block, not the order or function. **When in doubt, start short:** stretching a short VSL that retained is cheaper than salvaging a long one nobody watched.
|
|
25
|
-
19.
|
|
25
|
+
19. ~~**Hook: 3 or 5 seconds?**~~ **RULE ADOPTED (2026-09-24, owner's decision): 3 seconds.** Section 4.1 and the newer source said 3 seconds and section 11.6 said 5; 11.6 was corrected. Reason: Meta's hook rate (3-second plays divided by impressions) measures exactly this window, and TikTok recommends stating the proposition in the first 3 seconds. The 5 seconds that remain in §18.13 are a different rule: the **mechanism** comes in by second 5, after the hook, not in the first line.
|
|
26
26
|
20. ~~**Buying engagement (an engagement-buying tool)**~~ **RULE ADOPTED (2026-09-24, the operation owner's decision): allowed with a condition.** Buying engagement is allowed, with the risk noted in the plan (Meta may cut reach or take down the account). Condition in [`guardrails.md` 20](guardrails.md#20--buying-engagement-likes-and-comments).
|
|
27
27
|
21. ~~**Realistic AI profile and platform policy**~~ **RULE ADOPTED (2026-09-24, the operation owner's decision): allowed with a condition.** An AI avatar is allowed with a notice that it is AI; what you don't do is hide that it's AI. Condition in [`guardrails.md` 21](guardrails.md#21--ai-avatar).
|
|
28
28
|
|
|
@@ -136,4 +136,4 @@ Contradictions and gaps found while distilling this methodology, pulled into thi
|
|
|
136
136
|
|
|
137
137
|
---
|
|
138
138
|
|
|
139
|
-
**Count:** 67 points in total:
|
|
139
|
+
**Count:** 67 points in total: 24 open, 40 resolved, adopted or closed (8 of them with a condition in guardrails: 20, 21, 23, 32, 44, 45, 48, 49), 0 blocked, and 3 with no publishable content because they're local bookkeeping (5, 6, 13), with the number preserved only so it doesn't break a link from another shard.
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
| Assemble the film: sequence of scenes + narration + SFX + captions + frame timing | **Remotion** (skill `wizz-remotion-best-practices`) |
|
|
23
23
|
| Captions passing behind the person on a recorded talking head (matte) | embedded-captions (HyperFrames bundle), then the clip goes to Remotion |
|
|
24
24
|
| Word-by-word timestamps of the TTS narration (sync captions/cuts/animation) | `ctc-align` |
|
|
25
|
+
| Decide which effects and SFX go where (and where not), then gate the render: safe zone (ad or organic), density, excess, rhythm, loudness | `wizz-video-effects` |
|
|
25
26
|
| Narration / voice / TTS / voice cloning | `voicebox` |
|
|
26
27
|
| **Source B-roll / inserts: find real footage of what the script names** | `get-brolls` (darwin) |
|
|
27
28
|
| Edit/cut recorded footage (cuts, selects, roughcut) | `buttercut` (darwin-arm64 only) |
|
|
@@ -42,11 +43,18 @@
|
|
|
42
43
|
with its provenance recorded. Runs in parallel with steps 2–4: it depends on the
|
|
43
44
|
script, not on the voice. **This step has a human gate — the agent never
|
|
44
45
|
self-approves.**
|
|
45
|
-
6. **
|
|
46
|
+
6. **Effects and sound** — `wizz-video-effects` turns what happens in the
|
|
47
|
+
narration into semantic events (a price appears, a verdict lands, a message
|
|
48
|
+
arrives), maps each to a licensed SFX and a level, anchors punches and flashes
|
|
49
|
+
on real speech breaths from the ctc-align timestamps, and runs every effect
|
|
50
|
+
through the anti-excess budget. Output: the timeline JSON the assembly reads.
|
|
51
|
+
7. **Assembly** — **Remotion** joins the exported scenes and the approved B-roll,
|
|
46
52
|
adds audio, SFX, and captions at the ctc-align timestamps, with frame precision.
|
|
47
53
|
Scenes follow the handoff contract below.
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
8. **Gates on the final file** — `wizz-video-effects` measures the rendered MP4:
|
|
55
|
+
safe zone for the piece's profile, density, loudness. Then look at frames.
|
|
56
|
+
9. **Fine cut** (if there's recorded footage) — `buttercut`.
|
|
57
|
+
10. **Publishing/distribution** — social/ads area.
|
|
50
58
|
|
|
51
59
|
**Screens vs. B-roll — they are not interchangeable.** `hyperframes` invents a
|
|
52
60
|
visual (a mockup, a chart, a title); `get-brolls` finds the real thing that the
|
|
@@ -30,7 +30,7 @@ You are Wizz's Designer. It creates high-level interfaces and landing pages, sho
|
|
|
30
30
|
For every task, **go through the right door via the `Skill` tool** and bring the result in plain language:
|
|
31
31
|
- Decide BEFORE building (brief, visual direction, motion/3D path) → `wizz-decision-maker` (pulls in wizz-design-needs, wizz-taste-skill, wizz-motion-3d-director)
|
|
32
32
|
- Create/improve UI (landing, design system, component, polish) → `wizz-ui-ux-pro-max` (pulls in wizz-premium-landing-ui-researcher, wizz-ui-component-curator, wizz-impeccable, wizz-taste-skill, wizz-huashu-design, wizz-react-components)
|
|
33
|
-
- Run animation and generate media → `wizz-animate` (pulls in wizz-design-motion-principles, wizz-remotion-best-practices, wizz-canvas-design, wizz-algorithmic-art)
|
|
33
|
+
- Run animation and generate media → `wizz-animate` (pulls in wizz-design-motion-principles, wizz-remotion-best-practices, wizz-video-effects, wizz-canvas-design, wizz-algorithmic-art)
|
|
34
34
|
|
|
35
35
|
Always **show the visual/plan before the code**. Code building is `wizz-agent-dev`'s job; for a one-off tweak, point to `wizz-quick-dev`.
|
|
36
36
|
|
|
@@ -65,6 +65,7 @@ When the script becomes a video (you route, you don't do the editing yourself; f
|
|
|
65
65
|
- **Timing** of the narration (word-by-word timestamps for captions and cuts) → `ctc-align`
|
|
66
66
|
- **Draw the screens** (visual scene changes in HTML/CSS, HTML→MP4) → CLI `hyperframes`
|
|
67
67
|
- **Source the B-roll / inserts** (real footage of the fact, person, product, news item or screen the script names) → CLI `get-brolls` (macOS). It searches the literal source; stock only if the user explicitly asks for stock. Ends in a Storyboard the **human** approves — never approve on their behalf
|
|
68
|
+
- **Effects and sound** (which effect and which SFX go where, and where not; ducking, loudness, safe-zone/density/excess/rhythm gates) → skill `wizz-video-effects`; it writes the timeline the assembly reads
|
|
68
69
|
- **Assemble the film** (audio, SFX, captions, frame timing) → **Remotion** (skill `wizz-remotion-best-practices`); never place audio/captions in hyperframes. Scenes arrive following the handoff contract in `_shared/video-pipeline.md` (same fps and size, silent MP4 or transparent WebM)
|
|
69
70
|
- **Edit/cut** the recorded video (cuts, selects, roughcut) → CLI `buttercut` (Apple Silicon only)
|
|
70
71
|
- **Analyze** a reference video (frames + transcription) → CLI `claude-video`
|
|
@@ -23,4 +23,6 @@ The following sound effects are available:
|
|
|
23
23
|
- `https://remotion.media/shutter-modern.wav`
|
|
24
24
|
- `https://remotion.media/shutter-old.wav`
|
|
25
25
|
|
|
26
|
-
For
|
|
26
|
+
For ads and Reels, do not pick sounds by hand and do not search the internet: invoke the `wizz-video-effects` skill. It maps narrative events to a licensed SFX catalogue (cleared for paid ads), sets the level against the voice, computes ducking windows and rejects excess. Trending audio from the Instagram or TikTok picker is never cleared for ads.
|
|
27
|
+
|
|
28
|
+
Outside ads (personal or internal video), https://github.com/kapishdima/soundcn/tree/main/assets is a starting point; check each file's licence before use.
|
|
@@ -55,6 +55,7 @@ Use this table to map skills/CLIs/MCPs directly **outside a Wizz project** (no `
|
|
|
55
55
|
| **Video with audio, narration, SFX, or synced captions** (film assembly) | `wizz-remotion-best-practices` (Remotion assembles; frame timing). Pipeline rule: HyperFrames draws the mute screens, Remotion assembles the film — don't use hyperframes to position audio/captions (~2.76s drift and CSS leaking between screens in production) | 1 |
|
|
56
56
|
| Draw a video SCREEN/scene in HTML/CSS, mute visual (app mockup, animated chart, title, loop; HTML→MP4) | CLI `hyperframes` (designer registry; `npx skills add heygen-com/hyperframes`) — mute screens only; assembly with audio goes to Remotion (line above) | 2 |
|
|
57
57
|
| Word-by-word timestamps for TTS narration (sync captions/cuts/animation with speech) | `ctc-align` (designer registry; single source of timing, replaces Whisper) | 2 |
|
|
58
|
+
| Effects and sound of a short vertical video (ad or Reels): which effect goes where and when not, which SFX for each event and at what level, ducking, master loudness, safe-zone/density/excess/rhythm gates | `wizz-video-effects` (designer registry; decides the effects, Remotion assembles them) | 2 |
|
|
58
59
|
| **Find B-roll / inserts / cutaways / supporting footage** for a video or Reel (real footage of the fact, person, product, news item or screen the script names; YouTube/Instagram/TikTok/Pexels/Pixabay/Commons/NASA) | CLI `get-brolls` (designer registry; **macOS**, global `skills add` + its own `install.sh`; needs ffmpeg with drawtext, Node 22+, Python 3.11+). Ends in a Storyboard the HUMAN approves — the agent never self-approves. Stock only when the user explicitly asks for stock | 1 |
|
|
59
60
|
| **Edit/cut video** (cuts, selects, roughcut) | CLI `buttercut` (designer registry; **Apple Silicon only**, clone-and-run in the project, heavy deps) | 1 |
|
|
60
61
|
| Generate an AI video/image (Veo/Kling/Wan), UGC / ad-ready style | CLI `fal-video` (designer registry; official `@fal-ai/client`, needs `FAL_KEY` in `.env`). Replaced the old arcads entry: same use case, 50-95% cheaper per video, no heavy clone-and-run | 1 |
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wizz-video-effects
|
|
3
|
+
description: Decision engine and gates for the effects and sound of short vertical video (ads and Reels). Decides which effect enters (punch, flash, shake, transition, interrupt), where, with what strength and when it must NOT enter; maps narrative events to a licensed SFX catalogue at a level set against the voice; anchors effects on real speech breaths; computes ducking; and gates the render on safe zone (ad or organic profile), density, excess, rhythm and loudness (-14 LUFS). Use when a narrated ad or Reel needs its effects and SFX decided or checked, when a video "zooms all the time" or "sounds amateur", when checking an ad's safe zone or loudness, or before handing the timeline to Remotion. Triggers - efeitos sonoros do criativo, SFX do Reels, zoom demais, conferir zona segura, LUFS do vídeo. Does not assemble the film (that is Remotion) and does not draw screens (that is HyperFrames).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# wizz-video-effects: which effect, where, and when not
|
|
7
|
+
|
|
8
|
+
A catalogue of effects is easy. What makes a video look professional is the **refusal**: every effect competes for a budget, every sound answers to an event in the narration, and every gate measures the final file. This skill is the engine that decides; Remotion assembles what it decides.
|
|
9
|
+
|
|
10
|
+
## Where it sits in the video pipeline
|
|
11
|
+
|
|
12
|
+
1. Script and narration exist; `ctc-align` gave word-by-word timestamps.
|
|
13
|
+
2. Screens were drawn silent (HyperFrames) or footage was sourced.
|
|
14
|
+
3. **This skill:** events → SFX and levels → effects anchored on breaths → budget → timeline JSON → (render in Remotion) → gates on the final MP4.
|
|
15
|
+
4. Remotion assembles from the timeline JSON. For the `<Audio>` and `<Sequence>` API, invoke the `wizz-remotion-best-practices` skill.
|
|
16
|
+
|
|
17
|
+
## Hard rules
|
|
18
|
+
|
|
19
|
+
- **Every effect goes through the budget** (`scripts/budget.py`). Nothing writes an effect straight into the timeline.
|
|
20
|
+
- **When an effect does not fit, switch effect; do not drop the instant.** Punch where the top has headroom, flash where content sits against the safe zone.
|
|
21
|
+
- **The instant comes from a speech breath, never from the clock** (`scripts/fill_breaths.py`).
|
|
22
|
+
- **The key is semantic.** Ask for `verdict`, `price`, `message`, never for `stamp.mp3`. An unknown event is refused with the list of valid ones; a wrong sound is worse than no sound.
|
|
23
|
+
- **Volume is not strength.** Volume is the mix; strength is who wins an instant when two effects compete. Both live in the config.
|
|
24
|
+
- **SFX never carries critical information.** Most feed video plays muted; every claim that matters is also on screen as text.
|
|
25
|
+
- **No canonical number in documentation.** Numbers live in `effects.config.json` and docs cite the key. A number that exists in two places ends in two versions.
|
|
26
|
+
- **Only licensed sound.** The shipped catalogue is Pixabay only, cleared for paid ads when mixed into a work. Trending picker audio from Instagram or TikTok never goes into an ad.
|
|
27
|
+
- **No gate replaces looking.** Extract frames from the final MP4 and look, every time.
|
|
28
|
+
|
|
29
|
+
## Setup (once per project)
|
|
30
|
+
|
|
31
|
+
1. Copy `assets/effects.config.template.json` to the project as `effects.config.json`. Set `offer`, frame size, the default `safe_zone_profile` (`ad` or `organic`) and the SFX folder.
|
|
32
|
+
2. Copy `assets/sfx/` to the folder the config points at (for Remotion, under `public/`). Keep `CREDITS.md` with it.
|
|
33
|
+
3. Run `python3 scripts/config.py --config effects.config.json`. An incoherent config fails with the reason; nothing is assumed.
|
|
34
|
+
4. Run `python3 scripts/sound.py --config effects.config.json coverage` for coverage: sounds no event uses, events pointing at missing files.
|
|
35
|
+
5. Run `python3 scripts/normalize_audio.py --config effects.config.json --check` so every SFX sits on the level contract.
|
|
36
|
+
|
|
37
|
+
Requires Python 3 (stdlib only) and ffmpeg/ffprobe.
|
|
38
|
+
|
|
39
|
+
## Per piece
|
|
40
|
+
|
|
41
|
+
1. **Timeline.** Write the piece's timeline JSON ([references/data-contract.md](references/data-contract.md)): `words[]` from ctc-align, `scenes[]`, `elements[]`, and the safe-zone profile (`ad` or `organic`).
|
|
42
|
+
2. **Events to sounds.** List what happens in the narration as semantic events with their times, from the words: a price appears, a verdict lands, a message arrives. Map them with `res = sound.map_requests([(5.4, "verdict"), (7.1, "element_in")], cfg)` from `scripts/sound.py`; it returns the shots (file and `vol` from the config) plus the refusals, with the reason. Write the accepted shots into `sfx[]` with their `event`. Events that trigger an effect (a `verdict` triggers `shake`): pass `sound.effect_candidates(res)` through `budget.select(...)` from `scripts/budget.py` and write the accepted ones into `shakes[]`; no other step writes them.
|
|
43
|
+
3. **Effects on breaths.** `python3 scripts/fill_breaths.py TIMELINE --narration NARRATION.mp3 --visual LAYOUT_RENDER.mp4 --write` anchors punches and flashes on real speech breaths, runs every candidate through the budget, and sets scene transitions and cut sounds by speech pace. Without `--write` it only reports.
|
|
44
|
+
- `--narration` is required in practice: ctc-align words touch each other (each ends where the next starts), so `words[]` alone yields almost no breaths.
|
|
45
|
+
- `--visual` must be a silent render **in the piece's final layout**. It decides punch vs flash by how close content sits to the safe-zone edge; measured on full-screen footage, every breath turns into a flash. Content inside a clipped card can take a punch.
|
|
46
|
+
- Cut sounds are added here, after step 2: if the report lists sounds that would no longer be accepted together, map everything again. `check_excess.py` checks all sounds together and blocks on collisions.
|
|
47
|
+
4. **Ducking.** `python3 scripts/sound.py duck TIMELINE --write` writes `ducking[]`; the Remotion volume callback is in the data contract.
|
|
48
|
+
5. **Before rendering.** `python3 scripts/check_excess.py TIMELINE` (blocks) and `python3 scripts/check_rhythm.py TIMELINE` (warns).
|
|
49
|
+
6. **Render** in Remotion from the timeline: SFX at their `vol`, narration with the ducking callback. `assets/remotion/` holds a reference `EffectsLayer.tsx` that consumes the timeline (copy it into the project). For the Remotion API itself, invoke `wizz-remotion-best-practices`.
|
|
50
|
+
7. **Gate the final MP4.** Footage (an actor take, b-roll) edge to edge fails the safe zone by construction: the gate measures contrast, not text. Put footage in a frame inside the zone over a plain or darkened background. `check_safe_zone.py FINAL.mp4 --timeline TIMELINE` (the piece's profile; run with `--profile` for the other one if the piece also runs there), `measure_density.py FINAL.mp4 --timeline TIMELINE`, `check_loudness.py FINAL.mp4` (prints the ffmpeg command that fixes it when it fails, and warns when that fix will compress; a quiet narration is better raised before the render).
|
|
51
|
+
8. **Look.** Extract frames of the final file, including one of the CTA block, and read them.
|
|
52
|
+
|
|
53
|
+
Every script takes `--config PATH`; without it, `VIDEO_EFFECTS_CONFIG`, then `./effects.config.json`.
|
|
54
|
+
|
|
55
|
+
## Gates
|
|
56
|
+
|
|
57
|
+
| Gate | Blocks? | Why |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `check_excess.py` | yes (unless `--warn`) | an effect that always happens stops marking anything |
|
|
60
|
+
| `check_safe_zone.py` | yes | text under the platform UI is text nobody reads |
|
|
61
|
+
| `measure_density.py` | yes | an empty frame loses the scroll |
|
|
62
|
+
| `check_loudness.py` | yes | platforms normalise to about -14 LUFS; louder only distorts |
|
|
63
|
+
| `check_rhythm.py` | **warns only** | its numbers come from weak sources |
|
|
64
|
+
|
|
65
|
+
Full table, what each gate cannot see, and escape hatches: [references/gates.md](references/gates.md).
|
|
66
|
+
|
|
67
|
+
## References
|
|
68
|
+
|
|
69
|
+
| File | Answers |
|
|
70
|
+
|---|---|
|
|
71
|
+
| [references/when-to-use.md](references/when-to-use.md) | **start here.** Each effect with its trigger, refusal, ceiling and conflict |
|
|
72
|
+
| [references/do-not-use.md](references/do-not-use.md) | what was already decided against, and why |
|
|
73
|
+
| [references/audio.md](references/audio.md) | which sound for which event, levels, ducking, loudness |
|
|
74
|
+
| [references/sfx-licensing.md](references/sfx-licensing.md) | what can run in a paid ad vs an organic post, per library |
|
|
75
|
+
| [references/editing-patterns.md](references/editing-patterns.md) | hook, rhythm, CTA, captions, safe zones, organic reach: what has a source and what does not |
|
|
76
|
+
| [references/gates.md](references/gates.md) | what each gate measures and what it misses |
|
|
77
|
+
| [references/data-contract.md](references/data-contract.md) | the timeline JSON the scripts read and write |
|
|
78
|
+
|
|
79
|
+
## Out of scope
|
|
80
|
+
|
|
81
|
+
- Assembling the film, captions layout, `<Audio>` API: `wizz-remotion-best-practices`.
|
|
82
|
+
- Drawing screens: HyperFrames.
|
|
83
|
+
- Word timestamps: `ctc-align`.
|
|
84
|
+
- Script, hook wording and CTA wording: the copy or social area.
|
|
85
|
+
- Launch films with music and art direction: `wizz-huashu-design`.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_readme": "Every canonical number of the effects engine lives HERE. A doc cites the key, never rewrites the value: a number copied to a second place ends up with two versions. Copy this file to your project as effects.config.json and fill `offer`. Keys starting with `_` are notes and are ignored by the loader. A key whose number has a weak source carries a sibling `_source`.",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"offer": "",
|
|
5
|
+
|
|
6
|
+
"paths": {
|
|
7
|
+
"media_root": "public",
|
|
8
|
+
"sfx_subdir": "sfx",
|
|
9
|
+
"_note": "Both relative to the folder that holds this config. media_root is what the renderer serves (Remotion `public/`); sound `src` values written into the timeline are relative to it (e.g. `sfx/pop.mp3`). Every script also accepts --sfx-dir to override."
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
"frame": {
|
|
13
|
+
"width": 1080,
|
|
14
|
+
"height": 1920,
|
|
15
|
+
"fps": 30,
|
|
16
|
+
"_note": "9:16 vertical. No script assumes this resolution: they all read it from here."
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
"safe_zone_profile": "organic",
|
|
20
|
+
"_safe_zone_profile": "Default profile. A timeline JSON can override it per piece with a top-level `safe_zone_profile`. The safe-zone and density gates measure against the declared profile.",
|
|
21
|
+
"safe_zones": {
|
|
22
|
+
"organic": {
|
|
23
|
+
"top": 250,
|
|
24
|
+
"bottom": 1570,
|
|
25
|
+
"left": 60,
|
|
26
|
+
"right": 920,
|
|
27
|
+
"recommended_margin": 30,
|
|
28
|
+
"_source": "Organic Reels/TikTok UI, measured on real pieces of the project this engine came from. The right edge is tighter than the left because the interaction icon column lives there. Third-party guides put the organic bottom UI at 320-400px on 1080x1920 (low to medium credibility, no own measurement)."
|
|
29
|
+
},
|
|
30
|
+
"ad": {
|
|
31
|
+
"top": 269,
|
|
32
|
+
"bottom": 1248,
|
|
33
|
+
"left": 65,
|
|
34
|
+
"right": 1015,
|
|
35
|
+
"recommended_margin": 30,
|
|
36
|
+
"_source": "Meta Business Help Center, 'About text overlays and the Safe Zone for ads in Stories and Reels' (https://www.facebook.com/business/help/980593475366490/): keep ~14% top, ~35% bottom and ~6% each side free of text, logo and key elements. Converted on 1080x1920: top 0.14*1920=268.8 -> 269, bottom 1920-0.35*1920=1248, sides 0.06*1080=64.8 -> 65 and 1015. Read from the indexed summary of the page on 2026-09-24, not the full HTML."
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
"safe_zone_gate": {
|
|
41
|
+
"dp_min": 15.0,
|
|
42
|
+
"edge_artifact_rows": 3,
|
|
43
|
+
"min_rows_to_fail": 10,
|
|
44
|
+
"sample_width": 216,
|
|
45
|
+
"_dp_min": "Row contrast (horizontal std-dev of luma) that counts as legible content. Own threshold, not density.dp_min: density asks 'is there anything here?', the safe zone asks 'is there LEGIBLE TEXT past the line?'. Measured on a real piece: real text 69, background glow and grid 7.0 to 8.6.",
|
|
46
|
+
"_edge_artifact_rows": "Rows ignored at each frame edge: the encoder leaves 1 to 2 dark pixels at the border, which pass dp_min on a light background without being content.",
|
|
47
|
+
"_min_rows_to_fail": "Below this many invading rows the gate warns instead of failing: a whole label past the line measured 27 rows, a rounded card corner grazing it measured 6."
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
"density": {
|
|
51
|
+
"n_bands": 7,
|
|
52
|
+
"min_filled": 3,
|
|
53
|
+
"dp_min": 7.0,
|
|
54
|
+
"step_s": 0.5,
|
|
55
|
+
"sample_width": 216,
|
|
56
|
+
"_note": "Uniform bands between the active safe zone top and bottom, only to measure how full the frame is. Measures local contrast (std-dev of luma), never mean brightness: a light background makes mean brightness pass everything.",
|
|
57
|
+
"_source": "min_filled 3 of 7 is a house rule of the source project, not an external benchmark."
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
"budget": {
|
|
61
|
+
"_readme": "The anti-excess engine. An effect only enters if its curve section still has budget, it respects the minimum distance and it does not repeat too much. When two candidates collide, the higher narrative strength wins.",
|
|
62
|
+
"gap_min_global": 2.0,
|
|
63
|
+
"burst_gap_s": 5.0,
|
|
64
|
+
"_burst_gap_s": "Two same-type effects further apart than this are not read as one burst: the viewer had visual rest between them. Also the widest hole fill_breaths.py tries to close.",
|
|
65
|
+
"per_effect": {
|
|
66
|
+
"punch": { "max_per_min": 10, "gap_min": 3.5, "max_in_row": 2, "dominant": true },
|
|
67
|
+
"flash": { "max_per_min": 10, "gap_min": 2.5, "max_in_row": 3, "dominant": false },
|
|
68
|
+
"shake": { "max_per_min": 3, "gap_min": 8.0, "max_in_row": 1, "dominant": true, "max_dur_s": 0.25 },
|
|
69
|
+
"interrupt": { "max_per_min": 7, "gap_min": 8.0, "max_in_row": 1, "dominant": true },
|
|
70
|
+
"transition": { "max_per_min": 30, "gap_min": 1.2, "max_in_row": 2, "dominant": false }
|
|
71
|
+
},
|
|
72
|
+
"_per_effect": "House numbers of the source project, tuned on 14 pieces after an audit measured one zoom punch every 2.5s. No external source.",
|
|
73
|
+
"curve": [
|
|
74
|
+
{ "name": "hook", "until_frac": 0.06, "weight": 1.2 },
|
|
75
|
+
{ "name": "body", "until_frac": 0.86, "weight": 1.0 },
|
|
76
|
+
{ "name": "cta", "until_frac": 1.0, "weight": 1.2 }
|
|
77
|
+
],
|
|
78
|
+
"_curve": "Weights are almost flat on purpose: dynamism belongs to the WHOLE video, never concentrated in the hook. Hook and CTA get 20% more, nothing else."
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
"sound": {
|
|
82
|
+
"_readme": "Which sound marks which happening. The key is SEMANTIC (what is happening in the story), never the file name. Changing SFX library means changing the `sound` column, without touching any rule.",
|
|
83
|
+
"narration_volume": 1.0,
|
|
84
|
+
"relative_ceiling": 0.65,
|
|
85
|
+
"_relative_ceiling": "No event `vol` may exceed relative_ceiling * narration_volume. A SFX that competes with the voice steals attention instead of marking it.",
|
|
86
|
+
"max_same_sound_in_row": 3,
|
|
87
|
+
"collision_window_s": 0.08,
|
|
88
|
+
"_collision_window_s": "Two sounds closer than this are heard as one dirty sound. House number, no external source.",
|
|
89
|
+
"reference_level_db": -20.0,
|
|
90
|
+
"max_peak_db": -3.0,
|
|
91
|
+
"_levels": "Level contract for every SFX file (normalize_audio.py): mean at reference_level_db, peak locked at max_peak_db, the peak always wins. The `vol` table below only means something if every file starts from the same level. Measured on a real piece: narration at -20.6 dB mean, an un-normalized whoosh ended at -36.1 dB after its vol, i.e. inaudible. An accent sits 6 to 10 dB under the voice.",
|
|
92
|
+
"_strength": "When an event has `triggers`, `strength` (0..1) is the narrative weight of the visual effect it fires, independent from `vol`: volume is mix, strength is how much the moment matters.",
|
|
93
|
+
"bed": {
|
|
94
|
+
"ambience": { "file": "bed/ambience.mp3", "vol": 0.13 },
|
|
95
|
+
"music": { "file": "bed/music.mp3", "vol": 0.2 },
|
|
96
|
+
"_readme": "OPTIONAL continuous layers under everything, relative to paths.media_root. Each enters only if the file exists; a missing file never breaks a render. Music must come from a source cleared for the piece's platform and use (paid ad vs organic)."
|
|
97
|
+
},
|
|
98
|
+
"events": {
|
|
99
|
+
"block_cut": { "sound": "whoosh", "vol": 0.55, "when": "switch of narrative block" },
|
|
100
|
+
"scene_cut": { "sound": "whoosh", "vol": 0.45, "when": "screen change inside the same block" },
|
|
101
|
+
"element_in": { "sound": "pop", "vol": 0.5, "when": "a card, line or label enters" },
|
|
102
|
+
"text_in": { "sound": "riser-metal", "vol": 0.42, "when": "a TEXT card enters: headline, title, anchor sentence" },
|
|
103
|
+
"list_item": { "sound": "coin", "vol": 0.42, "when": "item of a repeating list; the checklist plink" },
|
|
104
|
+
"verdict": {
|
|
105
|
+
"sound": "stamp",
|
|
106
|
+
"vol": 0.65,
|
|
107
|
+
"strength": 0.95,
|
|
108
|
+
"when": "verdict, seal or decision slammed down",
|
|
109
|
+
"triggers": ["shake"]
|
|
110
|
+
},
|
|
111
|
+
"message": { "sound": "notif", "vol": 0.55, "when": "a message, notification or alert arrives" },
|
|
112
|
+
"typing": { "sound": "type", "vol": 0.35, "when": "text being typed on screen" },
|
|
113
|
+
"paste": { "sound": "paste", "vol": 0.45, "when": "copy or paste" },
|
|
114
|
+
"click": { "sound": "click", "vol": 0.4, "when": "interface interaction, cursor, tap" },
|
|
115
|
+
"send": { "sound": "send", "vol": 0.5, "when": "send, publish, submit" },
|
|
116
|
+
"price": { "sound": "cash", "vol": 0.55, "when": "the price appears or changes" },
|
|
117
|
+
"gain": { "sound": "coin", "vol": 0.45, "when": "value gained, savings, counter going up" },
|
|
118
|
+
"done": { "sound": "success", "vol": 0.55, "when": "check, ready, task done" }
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
"ducking": {
|
|
123
|
+
"depth_db": 4.0,
|
|
124
|
+
"attack_ms": 30,
|
|
125
|
+
"release_ms": 200,
|
|
126
|
+
"assumed_sfx_duration_s": 0.4,
|
|
127
|
+
"_source": "extrapolation, no primary source. Research proposal: 3-6 dB depth, attack < 50 ms, release 150-300 ms, applied only when a transient SFX collides with the start of a spoken word. Derived from music-under-voice ducking guides (18-25 dB, attack < 300 ms, release 300-700 ms), which target a continuous bed, not short SFX.",
|
|
128
|
+
"_assumed_sfx_duration_s": "Used only when neither the timeline nor the file on disk gives a SFX duration."
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
"loudness": {
|
|
132
|
+
"target_lufs": -14.0,
|
|
133
|
+
"tolerance_lu": 1.0,
|
|
134
|
+
"max_true_peak_db": -1.0,
|
|
135
|
+
"lra": 11.0,
|
|
136
|
+
"encode_tp_margin_db": 1.0,
|
|
137
|
+
"_encode_tp_margin_db": "Measured on the test piece (2026-09-24): re-encoding the loudnorm output to AAC pushed the true peak from -1.0 to -0.8 dBTP, so the fix command failed its own gate. For lossy outputs the printed fix aims this much under max_true_peak_db.",
|
|
138
|
+
"_source": "Industry mastering practice for TikTok and Reels (-14 LUFS integrated, -1 dBTP), replicated by mastering guides; neither TikTok nor Meta publishes an official loudness spec. Mastering louder does not help: the platform turns it down and may distort on re-encode. lra is the ffmpeg loudnorm default, used only by the printed fix command."
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
"rhythm": {
|
|
142
|
+
"max_static_s": 5.0,
|
|
143
|
+
"hook_s": 3.0,
|
|
144
|
+
"hook_cut_every_s": 3.0,
|
|
145
|
+
"body_cut_every_s": [2.5, 4.0],
|
|
146
|
+
"cta_tail_s": 3.0,
|
|
147
|
+
"counts_as_change": ["scenes", "punches", "shakes", "flashes", "interrupts", "elements"],
|
|
148
|
+
"_source": "Weak source: pattern-interrupt and pacing blogs (change something every 3-5s; Reels cuts every 2.5-4s) plus the 3-second hook rate metric of Meta Ads Manager. That is why check_rhythm.py only warns, never blocks.",
|
|
149
|
+
"_hook_s": "Owner decision: the hook closes at 3s.",
|
|
150
|
+
"_cta_tail_s": "No source. Seconds at the end excluded from the body cadence, because the CTA lowers effect density on purpose to keep price and button legible."
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
"breaths": {
|
|
154
|
+
"min_silence_s": 0.22,
|
|
155
|
+
"silence_db": -30,
|
|
156
|
+
"edge_guard_s": 0.4,
|
|
157
|
+
"fast_speech_wps": 2.8,
|
|
158
|
+
"punch_peak_scale": 1.066,
|
|
159
|
+
"pace_profiles": {
|
|
160
|
+
"first": { "transition": "cut", "sound_event": "block_cut" },
|
|
161
|
+
"fast": { "transition": "whip", "sound_event": "block_cut" },
|
|
162
|
+
"slow": { "transition": "zoom", "sound_event": "element_in" }
|
|
163
|
+
},
|
|
164
|
+
"punch_sound_event": "element_in",
|
|
165
|
+
"_note": "An effect anchors in the real silence of speech, in the MIDDLE of the silence, never glued to a word. Below min_silence_s a gap is a pause between syllables, not a breath. punch_peak_scale is the real peak of a punch multiplied by the continuous micro-zoom (1.035 * 1.03); a point closer to the frame edge than the zone edge divided by it leaves the safe zone at the peak.",
|
|
166
|
+
"_fast_speech_wps": "House number: words per second above which a block reads as a burst and gets an aggressive cut. No external source."
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
"avatar": {
|
|
170
|
+
"_optional": "Only for pieces with a generated avatar. Delete the block otherwise.",
|
|
171
|
+
"max_full_shot_s": 4.5,
|
|
172
|
+
"max_occlusion_s": 5.0,
|
|
173
|
+
"max_take_s": 6.0,
|
|
174
|
+
"variation_step_s": 4.0,
|
|
175
|
+
"min_variation_s": 1.2,
|
|
176
|
+
"_source": "The gesture cycle of a generated avatar is around 7.5s and then visibly repeats. Every ceiling here derives from that observation on one provider; re-measure for another."
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Reference layer that plays a wizz-video-effects timeline in Remotion.
|
|
2
|
+
// Copy into the project and adapt; see README.md.
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { AbsoluteFill, Sequence, staticFile, useCurrentFrame, useVideoConfig } from "remotion";
|
|
5
|
+
import { Audio } from "@remotion/media";
|
|
6
|
+
import {
|
|
7
|
+
EffectsTimeline,
|
|
8
|
+
allSounds,
|
|
9
|
+
flashOpacity,
|
|
10
|
+
instants,
|
|
11
|
+
narrationVolume,
|
|
12
|
+
punchScale,
|
|
13
|
+
shakeOffset,
|
|
14
|
+
transitionAt,
|
|
15
|
+
} from "./useEffects";
|
|
16
|
+
|
|
17
|
+
/** Narration with the ducking callback, plus every SFX at its `at` and `vol`. */
|
|
18
|
+
export const EffectsAudio: React.FC<{
|
|
19
|
+
timeline: EffectsTimeline;
|
|
20
|
+
narrationSrc: string;
|
|
21
|
+
narrationStart?: number; // seconds
|
|
22
|
+
}> = ({ timeline, narrationSrc, narrationStart = 0 }) => {
|
|
23
|
+
const { fps } = useVideoConfig();
|
|
24
|
+
const startFrame = Math.round(narrationStart * fps);
|
|
25
|
+
return (
|
|
26
|
+
<>
|
|
27
|
+
<Sequence from={startFrame} name="narration">
|
|
28
|
+
<Audio
|
|
29
|
+
src={staticFile(narrationSrc)}
|
|
30
|
+
volume={narrationVolume(timeline.ducking ?? [], fps, startFrame)}
|
|
31
|
+
/>
|
|
32
|
+
</Sequence>
|
|
33
|
+
{allSounds(timeline).map((s, i) => (
|
|
34
|
+
<Sequence key={`sfx-${i}`} from={Math.round(s.at * fps)} name={`sfx ${s.event ?? s.src}`}>
|
|
35
|
+
<Audio src={staticFile(s.src)} volume={s.vol} />
|
|
36
|
+
</Sequence>
|
|
37
|
+
))}
|
|
38
|
+
</>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Shake moves everything inside it. Put the whole stage here, never the captions alone. */
|
|
43
|
+
export const ShakeStage: React.FC<{ timeline: EffectsTimeline; children: React.ReactNode }> = ({
|
|
44
|
+
timeline,
|
|
45
|
+
children,
|
|
46
|
+
}) => {
|
|
47
|
+
const frame = useCurrentFrame();
|
|
48
|
+
const { fps } = useVideoConfig();
|
|
49
|
+
const { x, y } = shakeOffset(frame / fps, instants(timeline.shakes));
|
|
50
|
+
return <AbsoluteFill style={{ transform: `translate(${x}px, ${y}px)` }}>{children}</AbsoluteFill>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Punch and scene transitions scale the shot. Wrap only the shot (footage or
|
|
54
|
+
* screen), inside a container with overflow hidden: scaling the whole frame
|
|
55
|
+
* pushes text through the safe zone at the peak. */
|
|
56
|
+
export const PunchShot: React.FC<{ timeline: EffectsTimeline; children: React.ReactNode }> = ({
|
|
57
|
+
timeline,
|
|
58
|
+
children,
|
|
59
|
+
}) => {
|
|
60
|
+
const frame = useCurrentFrame();
|
|
61
|
+
const { fps } = useVideoConfig();
|
|
62
|
+
const t = frame / fps;
|
|
63
|
+
const tr = transitionAt(frame, fps, timeline.scenes ?? []);
|
|
64
|
+
const scale = punchScale(t, instants(timeline.punches)) * tr.scale;
|
|
65
|
+
return (
|
|
66
|
+
<AbsoluteFill
|
|
67
|
+
style={{
|
|
68
|
+
transform: `translateX(${tr.x}px) scale(${scale})`,
|
|
69
|
+
filter: tr.blurPx > 0 ? `blur(${tr.blurPx}px)` : undefined,
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</AbsoluteFill>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Opacity-only flash: moves no pixel, so it fits where a punch does not. */
|
|
78
|
+
export const FlashOverlay: React.FC<{ timeline: EffectsTimeline; color?: string }> = ({
|
|
79
|
+
timeline,
|
|
80
|
+
color = "#FFFFFF",
|
|
81
|
+
}) => {
|
|
82
|
+
const frame = useCurrentFrame();
|
|
83
|
+
const { fps } = useVideoConfig();
|
|
84
|
+
const opacity = flashOpacity(frame / fps, instants(timeline.flashes));
|
|
85
|
+
if (opacity <= 0) return null;
|
|
86
|
+
return <AbsoluteFill style={{ backgroundColor: color, opacity, pointerEvents: "none" }} />;
|
|
87
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Remotion reference layer
|
|
2
|
+
|
|
3
|
+
Reference code to **copy into your Remotion project** (for example `src/effects/`) and adapt. It is not a package and nothing imports it from here.
|
|
4
|
+
|
|
5
|
+
It plays a timeline written by this skill's scripts ([../../references/data-contract.md](../../references/data-contract.md)):
|
|
6
|
+
|
|
7
|
+
| Export | File | Does |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `EffectsAudio` | `EffectsLayer.tsx` | narration with the `ducking[]` volume callback, every `sfx[]` entry and every scene cut sound at its `at` and `vol` |
|
|
10
|
+
| `ShakeStage` | `EffectsLayer.tsx` | short decaying tremor on `shakes[]`, never longer than `SHAKE.durS` |
|
|
11
|
+
| `PunchShot` | `EffectsLayer.tsx` | zoom pulse on `punches[]` and the `zoom` / `whip` scene transitions |
|
|
12
|
+
| `FlashOverlay` | `EffectsLayer.tsx` | opacity-only flash on `flashes[]` |
|
|
13
|
+
| pure math | `useEffects.ts` | the same functions, frame in, value out, deterministic |
|
|
14
|
+
|
|
15
|
+
Usage sketch:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<AbsoluteFill>
|
|
19
|
+
<ShakeStage timeline={tl}>
|
|
20
|
+
<div style={{ position: "absolute", overflow: "hidden", ...shotBox }}>
|
|
21
|
+
<PunchShot timeline={tl}>{/* silent screen or footage */}</PunchShot>
|
|
22
|
+
</div>
|
|
23
|
+
{/* cards and captions of the piece */}
|
|
24
|
+
</ShakeStage>
|
|
25
|
+
<FlashOverlay timeline={tl} />
|
|
26
|
+
<EffectsAudio timeline={tl} narrationSrc="narration.mp3" />
|
|
27
|
+
</AbsoluteFill>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Rules the layer assumes:
|
|
31
|
+
|
|
32
|
+
- It draws what the timeline says and decides nothing. How many effects and where was decided by the scripts and the budget.
|
|
33
|
+
- Wrap only the shot in `PunchShot`, inside a box with `overflow: hidden`. Scaling the whole frame pushes captions and cards through the safe zone at the peak.
|
|
34
|
+
- Shake constants (`SHAKE`, `PUNCH`, `FLASH`, `TRANSITION` in `useEffects.ts`) shape one accepted effect; they are component constants, not budget numbers.
|
|
35
|
+
- `src` values are relative to Remotion `public/` (`staticFile`), as the data contract says.
|
|
36
|
+
|
|
37
|
+
For the Remotion API itself (`<Audio>`, `<Sequence>`, `<Video>` props such as `objectFit`), invoke the `wizz-remotion-best-practices` skill.
|