SuperAPI AI Directory

create-concept-loop-video

Design & creation

View repository

Updated 10-15 21:54

About this skill

create-concept-loop-video

Purpose

Produce a shippable 10-25s looping concept video using the concept-loop archetype from CREATOR_GRAMMAR.md §8. The archetype's defining pattern:

  • Seamless loop — the last frame must visually match the first. The video is built to play on infinite repeat without a visible seam.
  • Color-as-emotion — each state in the cycle has its own background color palette. The color is the mood. No explanatory caption.
  • In-world text — any text on screen is part of the visual design (e.g. a day label in the character's hand, a status badge floating over the scene), not a platform-layer subtitle burn.
  • Opens on emotional payoff — the first state shown is the most resonant, not the logical start of the sequence (e.g. a "Friday" state opens the Monday-through-Friday loop).
  • Music-only, beat-synced cuts — no VO, no karaoke captions. Cut density is lower than the silent-product-demo archetype (2 cuts/10s in the body); the rhythm is in the beat-snapped transitions between states.
  • Rapid cut burst near the loop point — a small burst (3-5 cuts) signals the loop is resetting, giving infinite-scroll replay a satisfying rhythm.

The molecule accepts a concept brief describing the loop idea and N states, generates one visual frame per state, renders each state clip, and assembles them through the EDL renderer with grammar.loop: true.

Inputs

Input Required Notes
loop_concept yes One-sentence description of the loop idea (e.g. "a character cycling through the seven moods of a work week").
states yes Ordered list of N state definitions. Each entry: { "label": "<day or state name>", "color": "<hex>", "description": "<visual description of the state>", "in_world_text": "<optional text rendered in-world>" }. Min 3 states, max 8.
open_state yes The label of the state that opens the video (the emotional payoff). Must NOT be the logical first state (e.g. for a Mon-Fri loop, open on Friday, not Monday).
music_path no Path to an existing music track. If omitted, find-music is run.
character_reference no Path to a reference image of the character or object. Drives Nano Banana consistency across states.
target_duration no, default 15 Target runtime in seconds (10-25).
output_dir yes Project directory (canonical 5-folder layout).

Composed Atoms

  • atoms/audio-editing/extract-beat-grid — analyse the music track and produce beat-grid.json. Required because concept-loop is beat_sync: true.
  • atoms/planning/generate-edit-decision-list — build the edl.json with archetype concept-loop, grammar.loop: true, beat-snapped cut points, and the loop-closure clip pairing (first state clip referenced again as the final clip).
  • atoms/assembly/render-edl — execute the validated edl.json into master.mp4. Handles hard-cut concat, music bed, and in-world text captions (rendered as static-title-card cues inside the EDL).
  • atoms/music/find-music — source a royalty-free music track when none is provided. Prefer ambient/loopable tracks that do not have an obvious intro or outro (they'll be trimmed to loop).
  • atoms/image-generation/create-image-nano-banana-fal — render one keyframe per state from the concept description + color + optional character reference. This is the primary visual generation step.
  • atoms/motion-graphics/create-motion-graphics-remotion — optional: if a state requires animated text or kinetic elements (not just a static still-to-motion clip), author the state frame as a Remotion component instead of a Nano Banana still.
  • atoms/motion-graphics/create-kinetic-typography — optional: if in-world text needs kinetic treatment (day label bouncing in, status bar animating), use this atom to author the text animation for that state.
  • atoms/editing/add-zoom-cuts — apply a subtle zoom-punch on the rapid cut burst at the loop point to signal the reset.
  • atoms/review/watch — frame-grounded QC of the final master, including loop-seam check (first frame vs last frame).

Workflow

Phase 0 — Intake and concept lock

  1. Read loop_concept, states, and open_state. Validate:
    • Minimum 3, maximum 8 states.
    • open_state is a label present in states.
    • open_state is NOT the logical first state (if it is, warn the user and ask them to pick a different opening state).
    • Each state has a distinct color hex value — no two adjacent states in the play order may share a color family (e.g. two blues in a row).
  2. Derive the play order: move open_state to position 0, then continue in the original state sequence, wrapping around. Example: if states are [Mon, Tue, Wed, Thu, Fri] and open_state is Fri, the play order is [Fri, Mon, Tue, Wed, Thu, (Fri)].
  3. Write the play order to working/play-order.json.
  4. Create output_dir with canonical 5-folder structure.

Phase 1 — Music + beat grid

  1. Music selection:
    • If music_path is provided, skip to step 2.
    • Run find-music with descriptors matching the loop concept (e.g. "loopable ambient, 100-120 BPM, chill, no long intro"). Prefer tracks that sustain energy without a dramatic drop (drops disrupt the loop seam).
    • Write to assets/music/track.mp3.
  2. Run extract-beat-grid:
    python3 skills/atoms/audio-editing/extract-beat-grid/scripts/extract_beats.py \
      --source assets/music/track.mp3 \
      --output-dir working/beat-grid/
    
  3. Verify bpm is positive and beats is non-empty.
  4. Determine the per-state clip duration: target_duration / N_states. Adjust each state's duration to the nearest bar boundary in the beat grid (±150ms tolerance) so cuts land on bars.

Phase 2 — Generate per-state frames

For each state in the play order:

  1. Build a generation prompt:
    "<state.description>, background color <state.color>,
    in-world text '<state.in_world_text>' visible,
    matching the character/object from <character_reference>"
    
  2. Run create-image-nano-banana-fal with the prompt and character_reference (if provided). Save to assets/keyframes/<state_label>.png.
  3. If the state needs animated in-world text (kinetic label, floating status), run create-kinetic-typography or create-motion-graphics-remotion to render a short motion clip (assets/clips/<state_label>-anim.mp4) for that state instead of a static still.
  4. Loop closure: after generating all N states, verify that the open_state keyframe (assets/keyframes/<open_state_label>.png) can serve as the first AND last frame (visually, the last rendered frame of the sequence should be close to the first). Do not re-generate — the EDL will reference the first state's clip at both ends.

Phase 3 — Generate edit decision list

  1. Build the scene table at working/scene-table.json:
    [
      { "scene_id": "loop-open", "description": "<open_state.description>",
        "role": "hook", "color": "<open_state.color>",
        "in_world_text": "<open_state.in_world_text>" },
      { "scene_id": "state-02", "description": "<state[1].description>",
        "role": "feature", "color": "<state[1].color>",
        "in_world_text": "<state[1].in_world_text>" },
      ...
      { "scene_id": "loop-close", "description": "<open_state.description>",
        "role": "feature", "color": "<open_state.color>",
        "notes": "loop-closure — references the same clip as loop-open" }
    ]
    
    The loop-close entry references the open_state clip again (not a new generation). This is what creates the seamless loop.
  2. Run generate-edit-decision-list:
    • archetype: "concept-loop"
    • source_mode: "generative"
    • beat_grid: "working/beat-grid/beat-grid.json"
    • grammar.loop: true
    • target_duration: <target_duration>
    • output_path: "working/edl.json"
  3. Verify the EDL:
    • grammar.loop is true.
    • The first and last clips reference the same source spec (the open_state).
    • meta.audio_mode is "music-only".
    • grammar.caption_family is "static-title-card" (in-world text cues).
    • No clip has role: "payoff-hold" — the concept-loop archetype does not use a payoff hold (payoff_hold.required: false in archetypes.json).
    • All clip target_in values snap within ±150ms of a bar boundary.

Phase 4 — Assemble scene clips

For each state clip in the play order:

  1. If a motion clip was generated in Phase 2 (assets/clips/<state_label>-anim.mp4), use it directly as the EDL clip source.
  2. If only a still keyframe exists, the EDL records source.mode: "generate", spec.keyframe: "assets/keyframes/<label>.png". render-edl will hold the still for the clip's duration (still-image clip handling).
  3. Apply add-zoom-cuts to the 3-5 clips nearest the loop point (the rapid cut burst): set motion: { "type": "zoom-punch", "from": 1.0, "to": 1.08 }.

Phase 5 — Render master

Run render-edl:

python3 skills/atoms/assembly/render-edl/scripts/render_edl.py \
  --edl working/edl.json \
  --output finals/master.mp4 \
  --project-dir <output_dir> \
  --crf 18

Phase 6 — Loop seam verification

  1. Extract the first and last frames:

    ffmpeg -ss 0.1 -i finals/master.mp4 -frames:v 1 -q:v 2 working/frame-first.png
    ffmpeg -sseof -0.1 -i finals/master.mp4 -frames:v 1 -q:v 2 working/frame-last.png
    
  2. Run atoms/review/watch on finals/master.mp4 with the QC prompt:

    "Check: (1) does the first frame closely match the last frame — same color, same character state, no jarring jump? (2) do cuts land on beats? (3) is the opening state the most emotionally resonant one? (4) are there no VO tracks, no karaoke captions? (5) is in-world text legible and integrated into the visual design? (6) is there a small burst of rapid cuts near the loop point?"

  3. Report findings as a timestamp punch list. If the loop seam is visible (significant color or composition mismatch between first/last frames), fix the loop-close clip and re-render Phase 5.

Decision Rules

DR-1 — Emotional-payoff-first opening

The first state in the play order must be the emotional peak, not the logical start. For a Mon-Fri loop, "Friday" (relief/energy) opens — not "Monday" (dread). For a product-benefit loop, the "after" state opens — not the "before."

If the user provides states in chronological order and does not specify open_state, ask rather than guessing. An incorrectly ordered loop opens on the most forgettable state and loses the scroll-stop hook.

DR-2 — Loop closure (first frame = last frame)

The loop-close entry in the scene table must reference the open_state clip — not a newly generated frame. Even a visually identical regenerated frame will have slight differences that break the loop seam. Use the exact same source: source.spec.keyframe: "assets/keyframes/<open_state>.png".

If the open_state is a motion clip (animated in-world text), the loop-close clip must use the first frame of that animation held static, not a looped replay. This prevents audio/visual drift when the platform loops the video.

DR-3 — Color adjacency rule

Adjacent states in the play order must have visibly distinct colors. Do not place two similar-hue states next to each other (e.g. two blues, two greens). The color change IS the editorial cut signal. If the user's state list has adjacent same-hue states, reorder or ask for a color substitution.

DR-4 — No payoff-hold clip

The concept-loop archetype does not use a payoff hold. Do not add a payoff-hold role to any clip — the EDL validator will pass (this archetype marks payoff as not required). The loop's "reward" is the return to the opening state, not a held single shot.

DR-5 — In-world text vs platform caption

All text visible in the video must be rendered as part of the visual frame (via Nano Banana in-world text, Remotion component, or kinetic typography). Never use the burn-in-captions atom or ASS captions to display state labels. The in-world text is part of the art direction; a burned-in subtitle is a platform UI element, which breaks the aesthetic register.

DR-6 — Loop burst placement

The rapid cut burst (3-5 quick cuts) should occur in the last 15% of the video (the tail segment), signaling the loop reset. This matches the concept-loop archetype's cuts_per_10s.tail: 5 target. Set these clips' transition_in to beat-synced-cut and add motion.zoom-punch via add-zoom-cuts.

Output

<output_dir>/
├── source/
│   └── character-reference.png    ← optional input reference
├── assets/
│   ├── music/
│   │   └── track.mp3
│   └── keyframes/
│       └── <state_label>.png      ← one Nano Banana frame per state
├── working/
│   ├── play-order.json            ← state sequence starting from open_state
│   ├── beat-grid/
│   │   ├── beat-grid.json
│   │   └── manifest.json
│   ├── scene-table.json
│   ├── edl.json
│   ├── frame-first.png            ← Phase 6 loop-seam check frame
│   ├── frame-last.png             ← Phase 6 loop-seam check frame
│   └── planning-notes.md
└── finals/
    ├── master.mp4                 ← the delivered looping Reel
    └── manifest.json

The finals/master.mp4 is the shippable loop: 9:16, 1080×1920, h264 + AAC, 30fps, 10-25s, with in-world text baked into the visuals and no VO track.

Quality Checks

  • finals/master.mp4 exists and is non-zero; ffprobe reports both video and audio streams.
  • Duration within ±0.5s of target_duration.
  • Resolution is 1080×1920 (9:16), 30fps.
  • edl.json has grammar.loop: true and the first clip's source spec matches the last clip's source spec (same keyframe path).
  • working/frame-first.png and working/frame-last.png are visually consistent — same dominant color, same subject in the same position.
  • meta.audio_mode is "music-only"; no VO track in edl.json.
  • grammar.caption_family is "static-title-card"; no karaoke-pop cues.
  • Exactly N+1 clips in the timeline (N states + the loop-close clip that mirrors the first state).
  • All beat-synced-cut clip boundaries land within ±50ms of a bar boundary in beat-grid.json.
  • Self-QC punch list from Phase 6 has zero critical loop-seam or beat-sync issues.
  • manifest.json exists with status: pass.

Failure Modes

  • Loop seam visible — the last frame has a different color or character pose than the first frame. The loop-close clip was generated anew instead of referencing the open_state keyframe. Fix: point the EDL loop-close clip to assets/keyframes/<open_state>.png explicitly.
  • Opening state is the logical first, not the emotional payoff — the loop lacks a scroll-stop hook. Re-select open_state per Decision Rule 1.
  • Adjacent same-hue states — two states with similar colors are adjacent in the play order; the cut is invisible and the loop feels monotonous. Reorder or replace colors per Decision Rule 3.
  • In-world text illegible — small text rendered inside the Nano Banana frame at 9:16 is often too small. If in_world_text is ≥5 words, consider rendering the text as a create-motion-graphics-remotion component with larger, styled typography, and composite it over the Nano Banana frame.
  • Music has an obvious intro/outro — the loop seam is hidden audibly, but a long intro before the first beat makes the loop feel wrong on repeat. Trim the music track to start on the first downbeat using audio.music with a start-offset in the EDL, or use find-music with "loopable, no long intro" as the criterion.
  • generate-edit-decision-list reports beat-grid missing for a beat_sync: true archetype — extract-beat-grid was skipped or the file path is wrong. Run Phase 1 before Phase 3.
  • More than 8 states — the per-state clip duration at 15s becomes too short for any motion treatment (< 1.5s). Reduce to ≤8 states or increase target_duration.
  • render-edl cannot find generate-mode clips — assets/clips/ or assets/keyframes/ paths in the EDL do not exist. Verify Phase 2 completed successfully and all paths in edl.json are relative to project_dir.