PTENES
MODULE 2.2

🔧 Gates, Scripts, and Determinism

Now for the actual commands behind each phase. You’ll see the engine scripts — islands.py, cut.py, verify-cut.py, lint-timeline.py, captions.py, make-sfx.sh e mix-sfx.py — and the rule of determinism that makes all of this reliable. Every block is ready to paste and includes instructions on how to verify it.

6
Topics
~35min
Duration
Intermediate
Level
Practice
Type
Sections read in this module0% · 0 of 0
1

🎚️ silencedetect, not Whisper timestamps

The cut doesn’t trust the timestamps Whisper returns—they have a jitter of ±0.2–0.3s, which leaves bits of silence and half-words. Instead, the engine measures the actual silence in the audio with the silencedetect from ffmpeg. The script islands.py use that energy to find the voice snippets and suggest which ones to keep, using the last take of each sentence.

Objective

Detect voice segments in the raw footage using audio energy and generate a islands.json with the KEEP/DROP proposal — the foundation for the edit, without relying on the transcript’s imprecise timestamps.

terminal
python3 islands.py \
  --media  \
  --transcript word.json \
  --noise -30dB --d 0.35 \
  --out islands.json

How to verify

The script prints a readable table: each island with its timing, text, and proposed KEEP/DROP with the reason. Read the table; if a repeated shot or tangent slipped past the heuristic, correct the field "keep" in the islands.json before moving on to the cut.py.

Key concepts: silencedetect, jitter, voice island, KEEP/DROP.

2

✂️ islands.py → cut.py → verify-cut.py

The cut is a chain of three scripts with a hard gate at the end. islands.py suggests what to keep; cut.py bridges the islands with keep=true in a single file; and verify-cut.py checks that the result is clean — no long silences in the body or repetitions. It comes out with exit 0 when it passes and exit 1 when it fails. You don’t animate anything before this PASS.

islands.py → islands.json cut.py glues keep=true → cut verify-cut.py exit 0 = PASS revise keep hard gate
The editing pipeline: each arrow passes an artifact along. The last box (cyan) is blocking — if the verify-cut.py if it doesn’t exit 0, the cut doesn’t move on to the animations.
terminal
# 1) monta o corte a partir das ilhotas com keep=true
python3 cut.py --islands islands.json --out edicion/corte-final.mp4

# 2) re-transcreva o CORTE (word-level) e verifique — bloqueante
python3 verify-cut.py \
  --media edicion/corte-final.mp4 \
  --transcript corte-word.json \
  --max-sil 0.6
echo "exit: $?"   # 0 = PASSA · 1 = FALHA

How to verify

Check the exit code (echo "exit: $?"). 0 means a clean cut — you can animate it. 1 means there’s still a long silence in the body or an audible repetition: the script prints exactly what failed. Fix the keep and run the pipeline again until it reaches 0.

Key concepts: script chain, hard gate, exit 0/1.

3

📏 lint-timeline.py: gap > 4s = error

The pacing rule “never more than 4s without a visual hit” stops being advice and becomes automatic check. O lint-timeline.py reads the motion/index.html from Hyperframes, measures the distance between visual beats, and reports an error when it finds a gap larger than the --max-gap (4s by default). It’s a rhythm safety net — it doesn’t replace reviewing the frames, but it catches the obvious gap before rendering.

Objective

Run a static lint check on the timeline and fail (exit ≠ 0) if any stretch goes more than 4s without a visual beat—blocking the render of a reel with a pacing gap.

terminal
# lint do ritmo: falha se houver >4s sem beat visual
python3 lint-timeline.py motion/index.html --max-gap 4.0
echo "exit: $?"   # 0 = ritmo ok · ≠0 = buraco de ritmo

How to verify

If it passes, exit 0 and silence. If it fails, the lint points to the line and the gap duration ("gap from X s starting at Ys"). Add a beat (cut, zoom, chip, B-roll, or reveal) at that point in the motion/index.html and run it again until there are no errors.

Key concepts: static lint, visual beat, --max-gap, rhythm gate.

4

💬 captions.py: captions at mouth level

O captions.py generates 2–3-word caption “beats,” synchronized with the voice, in a retention-focused style. The anti-cliché trick is in the highlight: the words you put in --keywords (numbers, brand names, strong concepts) are painted onto the your accent color. Since the keywords change with every video, your captions never come out the same as anyone else's.

Objective

Produce one captions.json with short beats and one highlighted keyword per beat, for captions that support the speech (at chest/microphone height) instead of repeating it at the bottom of the screen.

terminal
python3 captions.py \
  --transcript corte-final.json \
  --max-words 3 \
  --keywords "" \
  --out captions.json

How to verify

Open the captions.json: each beat has start, end and a list words with hi:true on the highlighted word. Check that the timings match the voice and that the highlighted word is the right one (if there’s no keyword match, the script highlights the longest word in the beat).

Key concepts: caption beat, --keywords, accent color, safe area.

5

🔊 make-sfx.sh + mix-sfx.py: SFX Under the Voice

Audio is made in two steps. make-sfx.sh synthesizes the effects palette (whoosh, pop, type, buzz, boom, ding, riser) with ffmpeg—no downloads, royalty-free, and always the same. Then, mix-sfx.py overlays each effect at the right time under the voice, with a limiter to prevent clipping. You pass the events as a list of [nome, segundo].

Objective

Generate the SFX palette locally and mix it into the render at the cut points, delivering a final.mp4 with effects that “produce” the transitions without competing with the speech.

terminal
# 1) sintetiza a paleta de SFX em ./sfx
bash make-sfx.sh sfx

# 2) mistura os efeitos sobre o render, por baixo da voz
python3 mix-sfx.py \
  --base render.mp4 --sfx-dir sfx --out final.mp4 \
  --events '[["boom",0.0],["whoosh",6.5],["ding",8.6]]'

How to verify

After the make-sfx.sh, make sure the folder sfx/ has the .wav (whoosh, boom, ding…). After the mix-sfx.py, listen to the final.mp4: the effects should land exactly on the cuts and stay under the voice. If you recut the video, the event timings change — retime the list --events.

Key concepts: SFX palette, events [name, t], ducking, limiter.

6

🎲 Determinism in Hyperframes

None of this works if every render comes out differently. That’s why Hyperframes animations are deterministic: without Math.random() and without Date.now(), no repeat:-1 (endless repeats), and the timelines remain in a paused, recorded for the engine to control frame by frame. Same project → same frame, every time — a requirement for reliable lint and QC.

✗ Breaks determinism

  • ✗Math.random() for positions/timings
  • ✗Date.now() / system clock
  • ✗repeat:-1 (infinite loop)

✓ Preserves determinism

  • ✓Fixed values or values derived from the duration
  • ✓Calculated finite repetitions
  • ✓gsap.timeline({paused:true}) registered

Objective

Confirm that the motion engine (Hyperframes) is installed and available—the prerequisite for rendering deterministic timelines.

terminal
# confere que o motor de motion está disponível
npx hyperframes --version

# checagem rápida: nenhuma fonte de aleatoriedade na timeline
grep -nE "Math\.random|Date\.now|repeat:\s*-1" motion/index.html || echo "OK: determinístico"

How to verify

O --version should print a number (engine installed). The grep should print OK: determinístico: if it lists lines, there’s a source of randomness that needs to be removed before rendering. Run the lint-timeline.py right afterward — together, the two guarantee a reproducible render.

Key concepts: determinism, finite repeat, paused, reproducibility.

✅ Module summary

✓
Cut based on actual silence — islands.py finds the islands by energy, not by Whisper timestamps.
✓
Chain with a hard gate — cut.py assembles and verify-cut.py blocks (exit 0/1) before animation.
✓
Pacing and captions verified — lint-timeline.py blocks gaps >4s; captions.py highlights your keywords.
✓
Local SFX and deterministic render — make-sfx.sh/mix-sfx.py and Hyperframes without random/now: same input, same output.

Next track:

Track 3 — How to use it: install the stack, generate your skill during the interview, and run your first reel.