| name | create-pause-and-click-explainer |
| description | Produce a 9:16 short pause-and-annotate explainer over an existing live-action or AI-generated base clip. Overlays a Windows-style pixel cursor that flies in from off-screen and snaps to N (typically 2-6) product/scene elements, freezing the underlying clip mid-motion to add modern Title-Case labels with smooth curved arrows pointing to each element, then resumes motion to land a final reveal beat. Real mouse-click / pop / whoosh / unlock SFX from `skills/assets/sound-effects/`. No spoken VO required — the cursor + labels carry the message. Use for food / product / GRWM / place reveals where the items themselves need to be named without a creator on camera narrating. Validated on the DIBS Beauty `concept-5-glassy-matte-grwm` GRWM ad (5 product callouts, 10.43s output). |
create-pause-and-click-explainer
Purpose
Recreate the "scroll-stopping cursor explainer" format popular on TikTok / Reels for food, beauty, and product reveals. The viewer sees a live-action clip start to play, then a pixel mouse cursor flies in, the clip freezes mid-motion, and the cursor "clicks" on N items in sequence — each click pops a clean label with a curved arrow pointing at the item — before the clip resumes for a final reveal.
The format's appeal is mechanical: the cursor's intent ("I'm about to point at something") is the first-3-second visual hook. Pause buys the viewer time to read labels they couldn't have read at speed. The resume + final reveal is the payoff.
Use when
- An existing clip already contains all the items you want to call out — a GRWM holding products fanned in hand, a plate of food, a desk laid out, a tray of skincare, a shelf of inventory.
- The brief wants the items themselves to be the focus, not a creator monologue.
- You want a fast (~8-15s) social ad with no spoken VO requirement.
- You want strict $0 generation cost — every step is PIL + ffmpeg + a pre-existing base clip.
Do not use for
- Talking-head / UGC selfie-cam where the creator's voice carries the message → use
molecules/ugc-ad/.
- VO-narrated explainers / mechanism breakdowns → use
molecules/explainer-video/create-vo-anchored-beat-video.
- Music-video formats where a song drives the cut rhythm → use
molecules/music-video-ad/.
- Photoreal product spots that need fresh AI footage → use
molecules/product-video/create-product-sizzle-reel or atoms/video-generation/create-video-veo3.
- Cases where you have no usable base clip and would need to gen one from scratch — chain this molecule after a clip-creation molecule, don't try to do both in one.
Inputs
Required (in --brief arg or collected in Phase 0):
base_clip — absolute path to the source video that contains the items to annotate. 9:16 preferred (1080×1920). Prefer the brand's "no-captions" variant when one exists (burned captions collide with our labels).
pause_at — exact seconds where the freeze should happen. Pick a frame where every item is clearly readable and not occluded by motion blur. For GRWM ads this is typically the moment products are held at chest-height fanned in the hand.
items — list of N items to label (2-6 typical). Each item: text (string, Title Case preferred), anchor (x,y in 1080×1920 — the point on the product the arrow tip should land), label_center (x,y — where the text block sits). The molecule renders an interactive working/anchor-picker.html over the freeze still to make picking these coords a clickable operation rather than guesswork.
final_window — [start_sec, end_sec] of the base clip to use as the post-unpause reveal. Critical: scrub the base clip frame-by-frame around the proposed window first — many brand ads have a flat product end card or burned-in pill that creeps in shortly after the creator's reveal beat. The window must end before any such overlay.
Collected in Phase 0:
music_track — path to a music bed (brand library preferred). Default: dibs-on-me-1.mp3 style — instrumental, ~120 BPM, on-brand.
aspect — 9:16 (default). 1:1 and 16:9 supported but anchor coords assume 1080×1920; pass alternate frame_size if not.
pre_pause_window — [start, end] of base clip to use as Segment A. Default [0, pause_at].
held_smile_tail — true|false (default true). If true, freeze on the last frame of Segment C for 1.0s to give the reveal a "payoff hold" beat. Memory: short reveal windows (≤1s of clean creator frames) feel abrupt without this.
cursor_style — windows-pixel (default) | mac | chunky-finger. Only windows-pixel is implemented in v1.
output_folder — canonical 5-folder layout per PROJECT_STRUCTURE.md.
Optional:
existing_freeze_still — if Phase 1 has already been run.
existing_label_renders — directory of pre-rendered label PNGs to skip Phase 2.
Composed Atoms
Frame extraction (Phase 1):
atoms/source/extract-frame-ffmpeg (if it exists; otherwise ffmpeg -ss <t> -frames:v 1).
atoms/editing/trim-video-clips — cuts Segments A and C from the base.
Asset rendering (Phase 2):
atoms/overlays/render-video-overlay-track — the molecule's overlay-component path. v1 of this molecule bakes the cursor + label renderer into a local build_overlay_assets.py because the registry doesn't ship pixel-cursor or comic-label components yet; once those land, swap to the registry.
Freeze segment build (Phase 3):
atoms/editing/ken-burns-clip — optional 5% zoom-pulse on the freeze still for the "we just paused" beat (the molecule's local script does this in PIL).
- Local
build_freeze_segment.py — PIL frame-by-frame compositor for the cursor path + label fade-ins. Frame-by-frame in PIL is preferred over ffmpeg overlay=enable= filters here: cursor interpolation and per-label alpha curves stay obvious in code and avoid filter_complex graphs that grow with N items.
Assembly (Phase 5):
atoms/assembly/stitch-videos-ffmpeg — concat A + B + C (+ optional C-hold).
Audio (Phase 6):
atoms/audio-editing/add-sound-effects — places clicks / pops / whoosh / resume chime at the exact moments the cursor settles, the freeze starts, and the unpause fires.
atoms/audio-editing/mix-master — mix protocol with music ducking during the freeze window.
atoms/audio-editing/normalize-loudness — loudnorm I=-14 / TP=-1.5 final pass.
SFX library (sourced, not synthesized):
skills/assets/sound-effects/mouse-click.mp3 — the 5 cursor-settle clicks (trimmed to first 0.20s).
skills/assets/sound-effects/dragon-studio-pop-402324.mp3 — the 5 label-appear pops (trimmed to first 0.50s, 55% gain, layered ~60ms after each click).
skills/assets/sound-effects/dragon-studio-whoosh-cinematic-376875.mp3 — the pause whoosh (trimmed to 0.75s, fires ~150ms before the freeze).
skills/assets/sound-effects/unlock-sound.mp3 — the resume chime (trimmed to 0.90s, fires ~200ms before the unpause).
Review (Phase 7):
atoms/review/watch — frame-extract + per-timestamp QC pass on the master.
Decision Rules
- Always use the brand's no-captions / no-end-card variant of the base when one exists. The base ad's burned-in captions, pink-pill headlines, and brand end cards collide with our labels and steal hook time. Memory:
feedback_no_captions_base.md (proposed).
- Scrub the post-unpause window frame-by-frame BEFORE locking it. Brand ads commonly transition into a product end card 1-2s after the creator's final reveal. The clean creator-only window can be as short as 0.7s. Hold the last frame for 1.0s to compensate.
- Title Case > ALL CAPS for label text. ALL CAPS reads as "shouting" / older meme aesthetic. Title Case in a modern semibold sans (SF Pro Display Semibold @ 54px) is the current creator-native default.
- Arrows: smooth quadratic bezier, no wobble. A "hand-drawn" arrow with sine-wave jitter reads as scratchy/amateur. A clean curved bezier with a soft 3px drop shadow and a tapered 6px shaft + 26px arrowhead reads as a tasteful explainer overlay.
- 5% zoom-pulse on the freeze still, 250ms ease-in. Sells the "we just paused" beat. Without it the freeze reads as a stuck video, not a deliberate moment.
- 35% black scrim on the freeze (alpha ~95/255). Gives the white labels contrast on busy backgrounds without burying the underlying scene.
- Cursor flies in from off-screen top-right (1100, -120) over 300ms before the first click. Reads as "the user is taking control" and is the visual hook for the first 3 seconds. Exits to bottom-right (980, 1820) — the play-button area — over 300ms before the unpause.
- Click cadence: ~1.25s per item. First click fires at freeze_t = 0.30s (just after the cursor lands). Each subsequent click is ~1.25s after the previous one, with a 150ms cursor slide between them. Faster than 1.0s/item feels rushed; slower than 1.5s/item drags.
- Music ducks to 18% during the freeze window (
amix with volume='if(between(t,freeze_start,freeze_end),0.18,1.0)'). Returns to 100% at the unpause. Memory: feedback_music_duck_freeze.md (proposed).
- Hold last smile frame for 1.0s after the post-unpause window if
held_smile_tail=true. Gives the reveal a payoff beat. Skip when the post-unpause window is already ≥2s of clean reveal.
- Frame-by-frame PIL compositing for the freeze segment is preferred over ffmpeg
overlay=enable= filter chains. Cursor interpolation + per-label alpha curves are obvious in Python; an N-item filter_complex graph grows unwieldy past 3 items.
- Real SFX > synthesized. Use library files. Synthesized clicks (sine + decay) sound thin in a finished mix; the dragon-studio / mouse-click library files carry the right transient bite.
Workflow
State machine with 7 phases + 4 human gates. Each phase writes a deterministic artifact path; the molecule is resumable.
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ P0 INTAKE │ → │ P1 EXTRACT │ → │ P2 RENDER │ → │ P3 BUILD │
│ brief.md │ │ freeze.jpg │ │ ASSETS │ │ FREEZE SEG │
│ [GATE 1] │ │ seg-c probe │ │ cursor + │ │ frames → │
└─────────────┘ └─────────────┘ │ N labels │ │ seg-b.mp4 │
│ [GATE 2] │ │ │
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ P7 MUX + QC │ ← │ P6 AUDIO │ ← │ P5 CONCAT │ ← │ P4 CUT │
│ master.mp4 │ │ music duck +│ │ silent │ │ seg-a + │
│ [GATE 4] │ │ real SFX │ │ master │ │ seg-c (+ │
│ │ │ │ │ [GATE 3] │ │ seg-c-hold) │
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
Phase 0 — Intake (GATE 1: confirm brief)
Read --brief arg or collect intake (see Inputs). Two ops most likely to need attention:
- Pick
pause_at. Render working/pause-candidates/<sec>.jpg for 3-5 candidate timestamps near the proposed point. Hand to user. Most "obvious" pause points (e.g. "around 2 seconds") have a better neighbor within ±200ms.
- Pick
final_window. Sample the base clip every 100ms from pause_at + 20s to the end and render a contact-sheet working/final-window-candidates.jpg. Flag the earliest frame that contains an unwanted overlay (product card, brand end card, URL pill). The clean window is [last_clean_start, earliest_overlay_t - 0.1]. If clean window is < 1.0s, default held_smile_tail=true.
Write working/brief.md with all locked-in values. Recap and ask for explicit "approve".
Phase 1 — Extract freeze still + segment-C probe
ffmpeg -ss <pause_at> -i <base> -frames:v 1 working/frames/freeze-clean.jpg — the master still everything is composited onto.
- Run
final_window probe one more time (cheap insurance against burned overlays).
- Verify resolution matches
frame_size (default 1080×1920); fail loud if not.
Phase 2 — Render assets (GATE 2: anchor preview)
- Render
assets/cursor/cursor.png via the local cursor recipe (Windows-style pixel arrow, hot-spot at top-left, ~96×120px, white fill + black outline).
- Render
assets/labels/<NN>-<slug>.png for each item, transparent 1080×1920 each:
- SF Pro Display Semibold @ 54px (fallback: Helvetica Neue Bold) in Title Case.
- Soft 6px-blur shadow layer behind the text (offset +2 / +4 px, alpha 160).
- Thin 4px black stroke around white fill.
- Smooth quadratic bezier arrow from below the text block to the item anchor: 6px shaft, 26px arrowhead, 3px drop shadow.
- Composite all label PNGs + the cursor at item-1 anchor over
freeze-clean.jpg with a 35% black scrim → working/frames/preview-all-labels.jpg. Hand to user. Wait for explicit approval of label positions, font size, and arrow targets.
Phase 3 — Build freeze segment
Run working/build_freeze_segment.py (the molecule's local script):
- Loads freeze still + cursor + N label PNGs.
- Computes a timeline: cursor enters from off-screen top-right over 300ms, lands on item-1 anchor at
freeze_t = 0.30s, slides between items in 150ms hops, exits to bottom-right over the final 300ms.
- Each label fades in over 220ms starting at its click moment.
- Each frame composites: zoom (1.00 → 1.06 over first 250ms) → scrim → labels (with alpha curves) → cursor (at interpolated position).
- Saves all frames as JPGs in
working/segments/seg-b-frames/.
- Saves
working/segments/timeline.json with click times, pause/play SFX cues, cursor waypoints — consumed by Phase 6 audio.
- Encodes frames →
working/segments/seg-b.mp4 (h264 crf 18, 30fps, yuv420p).
Phase 4 — Cut segments A and C
ffmpeg -ss 0 -to <pause_at> -i <base> ... seg-a.mp4 — pre-pause live action.
ffmpeg -ss <final_window.start> -to <final_window.end> -i <base> ... seg-c.mp4 — post-unpause reveal.
- If
held_smile_tail=true: ffmpeg -ss <final_window.end - 0.03> -frames:v 1 ... smile-hold.jpg, then loop=1 -t 1.0 → seg-c-hold.mp4.
Re-encode all segments to identical codec params (libx264, crf 18, 30fps, yuv420p, no audio). Mismatched fps causes ffmpeg concat demuxer to silently drop frames — same failure mode as create-vo-anchored-beat-video.
Phase 5 — Concat silent master (GATE 3: silent master review)
ffmpeg -f concat -safe 0 -i concat.txt ... master-silent.mp4 over [A, B, C, C-hold?].
- Verify duration ≈ sum of segment durations within ±0.04s.
- Hand to user. Wait for approval that the cursor + label rhythm reads correctly. This is the cheapest place to catch rhythm issues — re-running Phase 3 with adjusted click cadence is ~5s of wall time.
Phase 6 — Audio build
Run working/build_audio.sh:
- Resample each
skills/assets/sound-effects/ source SFX to 48kHz stereo wav, trim each to the usable head (click 0.20s, pop 0.50s, whoosh 0.75s, resume 0.90s).
- Build the music bed: trim
music_track to total duration, fade in 300ms + fade out 800ms, set base volume to 0.85.
- Build the SFX track: layer whoosh at
pause_at - 0.15, then for each item layer click at pause_at + click_offset and pop +60ms after at 55% gain, then resume chime at unpause_at - 0.20.
- Final mix: duck music to 0.18 between
pause_at + 0.05 and unpause_at - 0.15, sum with SFX track at +1.2 weight, loudnorm I=-14 TP=-1.5.
Phase 7 — Mux + self-QC (GATE 4: confirm master)
ffmpeg -i master-silent.mp4 -i master-audio.wav -c:v copy -c:a aac ... finals/<slug>.mp4.
- Run
atoms/review/watch finals/<slug>.mp4 --fps 2 --resolution 540 and read every frame.
- Verify: cursor enters before freeze starts, lands on each item at the expected click time, labels appear in the right order, no labels clipped at frame edges, music ducks at freeze start and returns at unpause, real SFX (not synthesized hum) is audible at every click.
- Hand to user. Wait for ship-it.
Output
Under the project's canonical 5-folder layout:
<project>/
├── HOW_TO.md ← per-project recipe (auto-generated)
├── source/ ← (only if base clip lives outside the project)
├── assets/
│ ├── cursor/cursor.png ← Phase 2
│ └── labels/<NN>-<slug>.png ← Phase 2 (one per item)
├── working/
│ ├── brief.md ← Phase 0
│ ├── pause-candidates/<sec>.jpg ← Phase 0 GATE 1
│ ├── final-window-candidates.jpg ← Phase 0 GATE 1
│ ├── frames/
│ │ ├── freeze-clean.jpg ← Phase 1
│ │ ├── preview-all-labels.jpg ← Phase 2 GATE 2
│ │ └── smile-hold.jpg ← Phase 4 (if held_smile_tail)
│ ├── segments/
│ │ ├── seg-a.mp4 ← Phase 4
│ │ ├── seg-b-frames/frame_NNNN.jpg ← Phase 3
│ │ ├── seg-b.mp4 ← Phase 3
│ │ ├── seg-c.mp4 ← Phase 4
│ │ ├── seg-c-hold.mp4 ← Phase 4 (if held_smile_tail)
│ │ ├── timeline.json ← Phase 3 (consumed by Phase 6)
│ │ └── concat.txt
│ ├── sfx/ ← Phase 6 (resampled + trimmed)
│ ├── master-silent.mp4 ← Phase 5 GATE 3
│ ├── master-audio.wav ← Phase 6
│ ├── build_overlay_assets.py ← molecule's local renderer
│ ├── build_freeze_segment.py ← molecule's local PIL compositor
│ ├── build_video.sh ← seg cut + concat
│ └── build_audio.sh ← SFX + music + duck mix
└── finals/
└── <slug>.mp4 ← Phase 7 deliverable
Plus manifest.json at project root listing every artifact path, phase status, gate approvals (with timestamps), and any warnings.
Quality Checks
ffprobe duration of master is within ±0.1s of expected (seg_a + seg_b + seg_c + seg_c_hold?).
- Master
r_frame_rate=30/1 (all segments must match). Mismatched fps → silent frame drops in concat.
- Every label PNG is 1080×1920 with transparent alpha and no pixel rendered outside the visible frame (i.e. no text clipping at the edges).
- Cursor PNG hot-spot is at top-left (0, 0) so anchor coords map 1:1 to the arrow tip.
- The 5 mouse clicks land within ±33ms (1 frame) of the cursor's
cursor_settle time per timeline.json.
- Music level: 100% pre-pause and post-unpause; 18% during the freeze window. Verify with
astats per-second RMS levels.
- Loudnorm output: integrated I in [-14.5, -13.5] LUFS, true-peak ≤ -1.5 dBFS.
- The post-unpause reveal contains zero frames of any brand end card / overlay pill from the source ad (the Phase 0 probe should have caught this; verify in Phase 7 watch pass).
atoms/review/watch Phase 7 pass has zero critical issues.
Failure Modes
- End card / product pill bleeds into the post-unpause window. Most common failure on this format. Brand ads transition to a product end card 1-2s after the creator's reveal, and the "no-captions" variant often still has these. Fix: re-do the Phase 0
final_window probe and tighten the window. If the clean window is < 1.0s, force held_smile_tail=true.
- Labels clip at frame edges. Edge labels with a
label_center too close to x=0 or x=1080 will clip text. Catch in Phase 2 GATE 2; fix by pulling label_center inward by 50-80px.
- Cursor "snaps" feel robotic. Cursor slides between items in linear interpolation feel mechanical. Fix: use ease-in-out (smoothstep) on the inter-item slide. The molecule's local
build_freeze_segment.py does this by default; if a custom path is authored, preserve the ease.
- Click SFX feels like it's playing AFTER the cursor lands. The mouse-click waveform has a 30-50ms attack envelope; humans perceive the peak as the click moment, not the start. Compensate by firing the SFX 30ms BEFORE the cursor settle time. Memory:
feedback_click_sfx_attack_offset.md (proposed).
- Synthesized SFX sound thin in the mix. The first build of this molecule used ffmpeg-synthesized clicks (sine + decay) — they sounded "tinny" against the music bed. Fix: use the
skills/assets/sound-effects/ library files exclusively (see Composed Atoms). Memory: feedback_real_sfx_over_synth.md (proposed).
- ALL CAPS labels feel dated. v1 of this molecule used Helvetica Bold ALL CAPS at 64px; reviewers flagged it as "shouty / older meme aesthetic". Fix: Title Case in SF Pro Display Semibold @ 54px. Don't revert to ALL CAPS without a brand-specific request.
- Scratchy / wobbly arrows look amateur. v1's arrows had sine-wave wobble for a "hand-drawn" feel — reviewers flagged it as ugly. Fix: clean bezier with no wobble; add a soft 3px drop shadow for depth instead.
- Concat demuxer silently drops frames. Mismatched fps between Segment A (from base) and Segment B (from image sequence). Fix: re-encode all segments to libx264 / crf 18 / 30fps / yuv420p before concat, even if it means a re-encode of an already-correct seg-a.
- Audio peaks clip after the loudnorm pass. Music bed at -3 dBFS + 6 SFX hits stacked at the same moment can push the sum > 0 dBFS. Fix: lower per-SFX
volume to 0.9-1.0 (not 1.2) when stacking multiple at the same adelay.
- The post-unpause segment cuts in cold. The transition from the still freeze frame to live motion is jarring without a click/whoosh on the unpause. Fix: fire the resume chime 200ms before the unpause; sets up the "play" expectation.
Case study: DIBS Beauty concept-5-glassy-matte-grwm recreation (validated)
- Base clip:
dibsbeauty/concept-5-glassy-matte-grwm/finals/concept-5-glassy-matte-grwm-v4-no-captions.mp4 (1080×1920, 32s, 30fps).
- Pause point:
pause_at = 2.00s — creator holding 5 cream sticks fanned at chest height.
- Items (5):
Love Beam Perfector — anchor (335, 1100), label_center (260, 440)
Desert Island Duo — anchor (455, 1080), label_center (260, 720)
Glow Tour Duo — anchor (590, 1075), label_center (820, 440)
Love Beam Powder — anchor (770, 1170), label_center (820, 720)
Setting Mist — anchor (905, 1310), label_center (540, 1640)
- Final window:
[27.00, 27.70] — only 0.7s of clean creator-smile reveal before the brand end card kicked in at 27.8s. held_smile_tail=true filled to 1.7s of post-unpause.
- Music:
dibsbeauty/music/dibs-on-me-1.mp3 (on-brand DIBS track).
- SFX: mouse-click ×5, dragon-studio-pop ×5, dragon-studio-whoosh-cinematic ×1, unlock-sound ×1.
- Output:
creator-content/test-videos/06-pause-and-click-explain/finals/06-pause-and-click-explain.mp4 (10.43s, 1080×1920, 3.8MB).
- Build artifacts under
creator-content/test-videos/06-pause-and-click-explain/working/ — render_overlays.py, build_freeze_segment.py, build_video.sh, build_audio.sh.
- Wall time: ~30s on M-series Mac. Cost: $0 (no API calls; all SFX library + PIL + ffmpeg).
Cite this case study in the run's HOW_TO.md so future agents have a working reference for the anchor-coord math and click-cadence values.