| name | compare-with-reference-video |
| description | Side-by-side comparison of a target master video against a reference video that exemplifies the style you're aiming for. Extracts aligned frame samples from both, builds an HTML comparison grid, computes mechanical metrics (cuts/min, motion magnitude, dominant palette), then drives a structured qualitative review across 7 dimensions (character framing, caption treatment, camera motion, cut rhythm, color/lighting, background complexity, energy). Emits review-notes compatible with auto-fix-from-review-notes. |
compare-with-reference-video
Purpose
After a target master video is rendered, the operator's first instinct is "this feels worse than the reference but I can't tell why." This atom answers that question structurally — by extracting matched frames, computing comparable metrics, and forcing a checklist review across 7 dimensions the LLM eyeball would otherwise skip.
The mechanical work (frame extraction, metric computation, grid HTML) is automated. The qualitative analysis is LLM/human driven against an explicit rubric. Output is a prioritized P0/P1/P2 fix list that auto-fix-from-review-notes can consume.
This skill is general-purpose — the dimensions and rubric work for ANY video format (music-video ad, narrated comic-strip, UGC, talking-head). The reference video acts as the "north star" for whatever format the operator is shipping.
When to use
- At State 8 of
video-orchestrator/orchestrator when a reference video for the target format exists (most common: when a molecule documents a reference run and you have a master to compare against it).
- After a re-roll pass to verify the fixes closed the gap.
- Standalone, any time the operator says "this feels off vs [reference]".
Skip when:
- No reference video exists (use
review-video-for-ad-quality instead — generic quality review).
- Reference and target are different lengths by > 2× (comparison breaks down at extreme length deltas).
- Reference is a 30s ad and target is a 4s loop — they're different formats, not comparable.
Inputs
--reference <path> — the reference video (required). Local mp4/mov.
--target <path> — the target master to evaluate (required). Local mp4/mov.
--output-dir <path> — default <target_folder>/review-notes/vs-reference-<ts>/. Holds the grid, frames, metrics, and report.
--fps <float> — sampling rate for both videos (default 2). Higher fps = more frames = more LLM tokens at review time.
--max-frames <int> — cap per video (default 60). At fps=2 this covers up to 30s; bump for longer videos.
--align <auto|relative|absolute> — how to match frames across the two videos:
auto (default): if durations within 10% match, use absolute timestamps; else relative (target_time = ref_time × duration_ratio).
relative: always normalize to 0–1 timeline position.
absolute: never normalize — only useful when both videos are the same nominal length.
Workflow
Step 1 — Frame extraction (mechanical)
Use the watch skill's pipeline (scripts/watch.py) under the hood, OR drive ffmpeg directly:
ffmpeg -i <video> -vf "fps=<fps>,scale=512:-1" <out>/frames/frame_%03d.jpg
Run on both reference and target. Save to <output_dir>/ref-frames/ and <output_dir>/target-frames/.
Step 2 — Metric computation (mechanical)
For each video compute and persist to <output_dir>/metrics.json:
| Metric | How |
|---|
duration_s | ffprobe -show_entries format=duration |
aspect_ratio | ffprobe -show_entries stream=width,height |
cuts_total | ffmpeg scene-detect filter (select='gt(scene,0.4)') |
cuts_per_minute | cuts_total / duration_min |
avg_cut_interval_s | mean of cut-to-cut intervals |
cut_interval_stdev_s | stdev — high values = irregular pacing |
motion_mag_mean | per-frame avg pixel difference (frame N+1 vs frame N), normalized 0–1. Proxy for "how much is happening per frame" |
motion_mag_p90 | 90th percentile — captures peak motion intensity |
dominant_palette | top 5 RGB hex colors via PIL quantize |
avg_luminance | mean Y channel value |
caption_box_estimate | OCR a few mid-frames to roughly detect caption presence + bounding box dims (optional, requires tesseract) |
Compute these for BOTH videos. The diff between them surfaces the structural deltas the operator can't see frame-by-frame.
Step 3 — Aligned grid HTML (mechanical)
Build <output_dir>/grid.html. Layout:
- Top: metrics side-by-side as a comparison table (target / reference / delta)
- Body: a vertical scroll of paired frames (target left, reference right) at matched timeline positions
- Each pair labeled with timestamp and a notes textarea
- Footer: a checklist of the 7 review dimensions (below)
The grid is the artifact the user opens. It also stays in the project folder so the LLM can re-open it on follow-ups.
Step 4 — Qualitative review (LLM-driven)
For each dimension, the LLM reviewer:
- Reads sampled frames from BOTH videos (use Read tool on the frame paths in
<output_dir>/{ref,target}-frames/)
- Compares against the rubric below
- Names 0–N findings with severity
Dimension 1 — Character framing
- What fraction of the frame does the protagonist occupy across shots?
- Is the character always facing camera or is the back-of-head common?
- Establishing wides vs character-dominant: what ratio?
- Rubric: if target has the character at <30% of frame in >40% of shots while reference has them at >40% — that's a P0 character-framing issue.
Dimension 2 — Caption treatment
- Size: % of frame width occupied by typical caption
- Background: pill / none / drop-shadow / outline
- Weight contrast within a single chunk: regular only, or regular+bold mix?
- Punctuation: leaked from transcript or stripped?
- Position: mid / lower-third / top / variable?
- Rubric: P0 if target caption width < 60% of reference caption width, or if punctuation noise leaks ("Super Bowl Sunday,").
Dimension 3 — Camera motion
- Is each shot's motion deliberately authored or ambient-only?
- Per-shot motion vector — push-in / parallax / static / tracking?
- Motion magnitude (use metric
motion_mag_mean)
- Rubric: P1 if target
motion_mag_mean is <50% of reference; P0 if <25%.
Dimension 4 — Cut rhythm
- Cuts per minute, mean and stdev
- Are cuts on a musical grid (bar boundaries) or arbitrary?
- Section breaks marked visually (transitions / freeze frames / palette shift)?
- Rubric: P1 if
avg_cut_interval_s is >2× the reference, or if cut_interval_stdev_s is >2× (irregular pacing).
Dimension 5 — Color & lighting
- Dominant palette overlap (Jaccard similarity of top-5 colors)
- Saturation distribution
- Cool/warm scene contrast variety
- Rubric: P2 mostly — flag if palette divergence is the operator's intent (different brand). P1 if avg luminance is >25% off (target much darker/lighter).
Dimension 6 — Background complexity
- Are background elements supporting the character or competing for attention?
- "Dead-zone" shots (no character, no clear focal point)?
- Rubric: P1 if target has 2+ character-less shots that don't earn their place (vs the reference which keeps almost every shot character-driven).
Dimension 7 — Energy & cohesion
- Does the video sustain attention end-to-end?
- Is there a clear arc (build → climax → resolution)?
- Climax bar/shot — does it land harder than its neighbors visually?
- Rubric: P0 if the climax frame is visually indistinguishable from non-climax frames (in the reference the climax is usually marked by color saturation OR a distinct motion event).
Step 5 — Write the review report
Output <output_dir>/report.md in the standard review-notes format that auto-fix-from-review-notes consumes:
---
status: NEEDS_REVISION
reviewer: compare-with-reference-video
reference: <path>
target: <path>
metrics_delta:
cuts_per_minute: target=18.4 ref=17.8 delta=+0.6
motion_mag_mean: target=0.06 ref=0.14 delta=-58%
avg_caption_width_pct: target=20 ref=32 delta=-12pp
---
## TL;DR
<one paragraph: top 3 reasons reference feels better>
## P0
- [Dimension] <issue> · evidence: <frame_path> @ <timestamp>
- auto-fix: <atom name> --<arg> "<value>"
## P1
- ...
## P2
- ...
## Proposed rebuild plan
| Pass | What | Time | Cost | Impact |
|---|---|---|---|---|
| A | ... | ... | ... | ... |
## Recommendation
<one sentence pointing at the cheapest pass with highest impact>
Step 6 — Embed in storyboard
If a storyboard.html exists in the target's project folder, append a "Review · target vs reference" section above the master video panel:
- TL;DR paragraph
- Two-column P0/P1 fix card
- Rebuild-plan card with Pass A/B/C/D summary
- Link to the full report and the grid HTML
This follows the single-review-surface rule from STORYBOARD.md — the operator should see the review at the top of the same page they reviewed the master from.
Output
<output_dir>/grid.html — paired-frame visual comparison + metrics table
<output_dir>/report.md — prioritized review notes (P0/P1/P2 + rebuild plan)
<output_dir>/metrics.json — raw mechanical metrics for both videos
<output_dir>/ref-frames/frame_*.jpg — sampled reference frames
<output_dir>/target-frames/frame_*.jpg — sampled target frames
- Inline edit to
<target_project>/storyboard.html — review summary section above the master panel
Quality Checks
- Both videos have ≥ 20 sampled frames each (otherwise the dimension review is sparse).
metrics.json has all 10 metric fields populated for both videos.
report.md frontmatter status is set (APPROVED or NEEDS_REVISION).
- Every P0/P1 finding cites at least one frame path + timestamp as evidence.
- Every P0/P1 finding names an auto-fixable atom OR explicitly says "manual re-roll required".
- The rebuild plan table covers ≥ 3 passes (otherwise the review wasn't deep enough).
- Storyboard.html was updated if it exists.
Known metric quirks
motion_mag_mean includes the big pixel diffs that happen at cut boundaries. A video with many hard cuts will register high motion even if every individual shot is static. Cross-check the quantitative number against the qualitative review of within-shot motion. (Future: filter pixel-diffs within ±1 frame of detected cuts.)
cuts_per_minute relies on ffmpeg scene-detect threshold 0.4. Hard-cut concatenated clips with similar palettes (e.g. felt-craft scenes that all share warm tones) may score below threshold — lower the threshold to 0.25 in those cases (currently a code change, future: CLI flag).
avg_luminance is sampled at one-in-twenty frames for speed — outliers in dark/light transitions can skew it. For precise lighting comparison, compute on all frames.
Failure Modes
- Reference is a placeholder, not a real reference run. If the reference video is a brand sizzle or unrelated format, the comparison is misleading. Skill should refuse to run or warn loudly if duration delta > 100% or aspect ratio differs.
- Both videos are bad and the diff is moot. If
target is broken (corrupt encoding, missing audio, wrong aspect), bail before comparison. The skill is for evaluating a finished master vs a known-good reference.
- Operator over-weights one dimension. A target that wins on captions but loses on cut rhythm shouldn't get APPROVED just because the operator likes the captions. The rubric is multi-axis on purpose.
- OCR for caption box estimation is brittle. When tesseract isn't installed, skip Dimension 2's automated measurement and rely on visual review only. Don't fail the whole skill.
- Reference video is private / unviewable. If the reference URL 404s or local path is missing, return a clear error with the missing path. Don't fall back to a generic review.
Composition
- Wraps the
watch skill (claude-video plugin) for frame extraction. Reuses its ffmpeg + Whisper pipeline.
- Feeds
auto-fix-from-review-notes via the report.md output format.
- Sits alongside
review-video-pacing-rhythm (cut/beat alignment), review-video-hook-strength (first-3s test), review-video-character-consistency (per-shot drift), review-music-video-beat-sync (caption-onset + bar-grid). This atom is the umbrella comparison — the others are deep-dives on single axes.
- Consumed by
video-orchestrator/review-video Phase 7b if a reference_path is set in implementation-brief.md.
Example invocation
python3 skills/atoms/review/compare-with-reference-video/scripts/compare.py \
--reference reference-videos/gum-of-gods-music-video.mp4 \
--target coinbase/video-01-music-video-debut/edits/master-final.mp4 \
--output-dir coinbase/video-01-music-video-debut/review-notes/vs-reference-01/ \
--fps 2 --max-frames 80
Then the LLM reads frames from both output dirs, fills in the qualitative review against the 7-dimension rubric, and writes the report.
Reference run
The canonical first execution of this skill was the Coinbase music-video v1 review: coinbase/video-01-music-video-debut/review-notes/01-vs-reference.md. That review found 3 P0s (character drift in bar 13, pencil artifact in scene 05, caption chunk bleed at 0:14), 3 P1s (character framing too small, caption presentation feels like subtitles, camera motion invisible), and proposed a 4-pass rebuild plan (A: captions only/free, B: re-roll 3 broken keyframes, C: re-roll establishing shots, D: re-animate weak motion). Pass A was recommended as the cheapest highest-impact starting move.
What NOT to do
- Don't grade subjectively without naming a frame. Every finding cites evidence.
- Don't fuse this with
review-video-for-ad-quality — those are different reviews (generic quality vs reference-anchored).
- Don't auto-trigger fixes from this skill. It writes review-notes and stops.
auto-fix-from-review-notes is the one that acts.
- Don't compare across formats. A talking-head ad vs a music video is not a reference-comparison.