Nodes/SDXL Auto Prompter/APNext H3 Music Video Writer
ComfyUI Node

APNext H3 Music Video Writer

A whole music video from one audio file

By dagthomas·Created 3 years ago·Updated 11 days ago· 290
APNext H3 Music Video Writer
  • audio
  • llm
  • image_1
  • image_2
  • image_3
  • image_4
  • image_5
  • image_6
  • image_7
  • image_8
  • image_9
  • vocals
  • scenes
  • durations
  • lengths
  • audio_segments
  • segment_table
  • scenes_text
  • synopsis
  • cast
  • scene_count
  • song_seconds
  • session_id
  • info
  • image_1
  • image_2
  • image_3
  • image_4
  • image_5
  • image_6
  • image_7
  • image_8
  • image_9
  • clip_starts
  • project_name
direction
lyrics
performance_modePerformance (the singer lip-syncs the lyrics on camera)
segment_modeAuto (cut on the music)
max_segment_seconds15.0
min_segment_seconds5.2
shots_per_sceneAuto
visual_styleLive-action, cinematic
dialogue_languageEnglish
modelsonnet
researchfalse
directortrue
use_subscriptiontrue
timeout_seconds1200
seed-1
cast_1
cast_2
cast_3
cast_4
extra_cast
custom_dialogue_language
custom_visual_style
wardrobe
locations
enforce_wardrobetrue
extra_instructions
image_notes
resume_session_id
working_dir
context_1
context_2
context_3
context_4
context_5
context_6
context_7
context_8
scenes_from_lyricsfalse
reference_image_useCharacters only (ignore picture backgrounds)
scene_briefs
audio_modeReference audio (<Audio 1> = the song piece)
save_scenestrue
scenes_per_call4
prompt_modeRef2VA (bind reference images)
draft_modelhaiku
parallel_chunkstrue
project_name
transcribe_lyricstrue
cut_plan
sound_events
beat_grid
lyrics_in
lyric_interpretationAuto (the writer decides per song)
transition_styleReveal (a camera move discloses a new space around them)
run_modewrite (call the LLM)
continuitycut plan decides (continue over soft cuts, hard cut on drops / sections / taps)
session_historyWindowed (brief + previous chunk - fits long serial runs)

The moment you realise it: APNext H3 Music Video Writer turns a song into a music video. Not a mood video with a soundtrack slapped on - a video where every clip is cut on the music, every rendered piece is exactly as long as its audio slice, the performer lip-syncs the actual lyric lines, and the whole thing stitches back together against the original track. It's the most ambitious node in the H3 family, and it mostly works because it's ruthless about one thing: audio lengths and clip lengths must match exactly.

Here's the mechanism. H3 renders clips up to about 15 seconds, so the node cuts the song into consecutive pieces inside min_segment_secondsmax_segment_seconds. The cut points come from the music itself - spectral-flux onsets, energy steps, section changes, and (when you supply timed lyrics) right before a lyric line. Then every piece length is snapped to H3's frame grid (5 + 17k frames at 24fps) so each rendered clip is exactly as long as its audio slice. That's the detail that makes the final stitched video never drift out of sync - which is the failure mode that kills every hand-rolled attempt at this.

Then it writes one scene per piece, each a four-section H3 prompt where the piece is <Audio 1>, reused 1:1 as the clip's soundtrack. In Performance mode the singer lip-syncs the piece's lyric lines on camera (<Subject 1> sings <d>[English] exact line</d> in sync with <Audio 1>); Narrative answers the lyric with pictures; Mixed alternates. Quiet pieces get long intimate shots; loud or peak pieces get more cuts and the chorus look. Long songs are written in chunks of six scenes that continue one session, so the synopsis, wardrobe and location locks stay consistent.

The inputs that matter

  • audio - the song (from Load Audio). Everything flows from this.
  • direction - the concept: who performs, where, the look, the story arc, what the chorus looks like vs the verses.
  • lyrics - one line per line. Timestamps make the sync exact: [0:15] line, 0:15 line, or LRC [00:15.20] line; [Chorus]-style tags are kept. Untimed lines get spread evenly (approximate). Empty = instrumental.
  • performance_mode - Performance / Narrative / Mixed.
  • segment_mode - Auto cuts on the music inside the allowed range; Fixed takes the longest allowed piece each time; Lyric lines tries hardest to cut right before a line.
  • cast_1..4 / extra_cast - the performer(s). An H3 Characters node in ✏️ custom mode with a wardrobe is made for this - a described performer with a locked outfit.
  • wardrobe / locations / enforce_wardrobe - the same locks as the Crossover Writer (cast-carried wardrobe is merged in).
  • The usual visual_style, dialogue_language (the lyric language), wildness, and the Claude Code block, plus image_1..9 reference pictures.

The outputs are matched lists, which is the whole architecture: scenes → the video node's prompt, lengths (frame counts) → length, audio_segmentsref_audio_1, durations → your frame math. Plus segment_table - the cut list, 01 0:00.00 – 0:15.08 (15.08s, 362 frames) energy: peak lyrics: … - and scenes_text, synopsis, cast, scene_count, song_seconds, session_id, info.

The finishing move

Each piece renders as its own clip, so the last step is H3 Scenes Join with replace_audio set to the original song - the joined per-scene audio gets swapped for the real track, and one Create Video → Save Video writes a single music video that plays in sync. The pack ships examples/h3/h3_music_video.json end to end.

Installing

Part of dagthomas/comfyui_dagthomas: ComfyUI Manager → comfyui_dagthomas, or clone into custom_nodes + pip install -r requirements.txt, restart. Under APNext → H3.

Claude Code must be installed and logged in - the default engine, subscription seat and all - unless you connect an H3 LLM Backend to the llm socket and run it on Ollama instead. Budget for the slow end: a 3-minute song is a dozen-plus pieces, each a full Claude turn, plus the audio analysis. Where people get burned: forgetting timed lyrics and wondering why the "sync" is approximate, and skipping replace_audio and wondering why the final video's sound doesn't match the song. Both are one input away from right.

Categorycomfyui_dagthomas/H3

Inputs (69)

NameTypeDefaultDescription
audioAUDIOThe song. It is cut into 5-15 s pieces and every piece becomes one scene / one clip.
directionSTRINGThe music-video concept: who performs, where, the look, the story arc, recurring motifs, what the chorus looks like vs the verses. Free text.
lyricsSTRINGLyrics, one line per line. Timestamps make the sync exact: `[0:15] line`, `0:15 line` or LRC `[00:15.20] line`; section tags like [Chorus] are kept. Untimed lines are timed automatically from a Whisper pass when transcribe_lyrics is on (1:1 when the line counts match, else spread over the sung span); with transcription off they are spread evenly over the WHOLE song and can land in instrumental passages - a singer mouthing words nobody sings. Empty = take the `lyrics_in` socket, else transcribe the song (transcribe_lyrics), else an instrumental video. Typed text always wins over the socket.
performance_modeCOMBOPerformance (the singer lip-syncs the lyrics on camera)3 options: Performance (the singer lip-syncs the lyrics on camera), Narrative (story visuals, nobody sings on camera), Mixed (performance and story, alternate or blend)
segment_modeCOMBOAuto (cut on the music)How the song is cut. Auto cuts on onsets / energy changes inside the allowed length range (lyric-line starts are preferred when lyrics are timed); Fixed takes the longest allowed piece every time; Lyric lines tries hardest to cut right before a line.
max_segment_secondsFLOAT15.05.2–15.1Longest piece (H3 renders up to ~15 s). Lengths snap to H3's frame grid.
min_segment_secondsFLOAT5.25.2–15Shortest piece. 124 frames (~5.2 s) is the shortest trained clip.
shots_per_sceneCOMBOAutoShots per scene. Auto lets the model cut to the music (more shots in loud parts).
visual_styleCOMBOLive-action, cinematicOpens every [Shot 1]; kept identical across the whole video.
dialogue_languageCOMBOEnglishLanguage of the lyrics (the <d>[...] tag).
modelCOMBOsonnetWho writes the prompt. sonnet / opus / haiku / fable / default are Claude Code aliases (`default` = whatever the CLI is configured for). `codex` is the OpenAI Codex CLI with its configured model (shown when installed; `codex:<model-id>` in an H3 LLM Backend picks a specific one). ollama: / lmstudio: / local: entries are whatever your local servers were serving when the page loaded; pick one to run fully offline. Anything not listed goes in model_override.
researchBOOLEANfalseLet the agent CLI (Claude Code or Codex) search the web for real references before writing - the actual location, wardrobe, lighting and physics. Slower, and it reaches the internet.
directorBOOLEANtrueLoad the H3 director skills (data/h3/skills): the core writing rules, the format this node emits, and style/motion craft, each with a reference library of gold examples and style anchors that Claude Code reads on demand. Costs a few extra seconds and tokens per run.
use_subscriptionBOOLEANtrueHide the API key from the CLI so it uses your login and subscription seat (ANTHROPIC_API_KEY for Claude Code, OPENAI_API_KEY for Codex). Turn off to bill the API key instead.
timeout_secondsINT120060–7200How long to wait PER CALL before the node gives up on the CLI (this is the node's own watchdog, not a Claude limit). Single H3 prompts take 25-60s; a multi-scene chunk with director/research on can take 10-20 minutes. The multi-scene writers retry a timed-out chunk at half size automatically.
seedINT-1-1–18446744073709550000Controls caching. -1 re-runs every queue.
cast_1optSTRINGA cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video.
cast_2optSTRINGA cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video.
cast_3optSTRINGA cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video.
cast_4optSTRINGA cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video.
extra_castoptSTRINGPerformer(s) / characters typed by hand, one per line, e.g. `Lead singer: a woman in her 30s with a platinum pixie cut` or `Character (played by Actor) from Show`. Merged with the cast sockets.
custom_dialogue_languageoptSTRING
custom_visual_styleoptSTRINGAny visual style not in the dropdown; overrides the dropdown when filled in.
wardrobeoptSTRINGWardrobe lock, one line per CAST member, e.g. `Sheldon: brown corduroy jacket, green Flash T-shirt, khaki trousers, small silver ring in the left nostril`. Used word-for-word in subject_definitions and at the character's first appearance in each scene; later shots carry it on the `<Subject N>` label, as H3's guide specifies. Empty = Claude fixes one outfit per cast member itself (in the synopsis) and reuses it. Only the cast is locked - extras and background people are described where they appear and never carry an anchor set.
locationsoptSTRINGLocation lock, one line per recurring place, e.g. `Sheldon's living room: beige three-seat sofa facing a wall-mounted TV on the LEFT, tall bookshelf of comics behind it, bay window with white blinds on the RIGHT, warm tungsten floor lamp in the far corner`. Used word-for-word in every scene set there, so the room looks the same in every scene. Empty = the model fixes each recurring place itself (in the synopsis) and repeats it.
enforce_wardrobeoptBOOLEANtrueAfter writing, check that each character's FIRST appearance in a scene states all of that character's wardrobe anchors verbatim, and that every scene set in a locked location restates that location's anchors. If anything is dropped or changed, the model gets one repair turn in the same session. Off = trust the first answer.
extra_instructionsoptSTRING
image_notesoptSTRINGPer-picture notes, one per line: `Image 1: the singer`, `Image 2: the rooftop, use as the location`. With reference_image_use = Characters only (the default), a note like that is the ONLY way a picture may be read as a location or prop - otherwise every picture is a performer and its backdrop is ignored.
resume_session_idoptSTRINGContinue an earlier run by feeding it that node's session_id. The whole conversation, images included, is still in context. A session sticks to its backend: Claude Code ids resume with Claude Code, `codex-` ids with Codex, `local-` ids with the same local model.
working_diroptSTRINGA folder Claude Code may read while writing - a script, a shot list, lookbook notes. Empty uses a throwaway scratch folder, which is the safe default.
llmoptAPNEXT_LLMOptional. Connect an APNext H3 LLM Backend node to write with Ollama, LM Studio, another OpenAI-compatible server or an API model instead of Claude Code. Overrides the model dropdown while connected.
context_1optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
context_2optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
context_3optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
context_4optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
context_5optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
context_6optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
context_7optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
context_8optSTRINGSteering input from another APNext node (Time, Scene, Poses, Plots, Feelings, Cinematic, Science, Geography, Architecture, Fashion, ...). The node detects which kind it is and tells Claude how to use it. Sockets grow as you connect them.
image_1optIMAGEReference image 1: <Picture 1> in the prompt. Connect the same image to image_1 on the MiniMax H3 Reference to Video node, or use this node's image_1 output.
image_2optIMAGEReference image 2: <Picture 2> in the prompt. Connect the same image to image_2 on the MiniMax H3 Reference to Video node, or use this node's image_2 output.
image_3optIMAGEReference image 3: <Picture 3> in the prompt. Connect the same image to image_3 on the MiniMax H3 Reference to Video node, or use this node's image_3 output.
image_4optIMAGEReference image 4: <Picture 4> in the prompt. Connect the same image to image_4 on the MiniMax H3 Reference to Video node, or use this node's image_4 output.
image_5optIMAGEReference image 5: <Picture 5> in the prompt. Connect the same image to image_5 on the MiniMax H3 Reference to Video node, or use this node's image_5 output.
image_6optIMAGEReference image 6: <Picture 6> in the prompt. Connect the same image to image_6 on the MiniMax H3 Reference to Video node, or use this node's image_6 output.
image_7optIMAGEReference image 7: <Picture 7> in the prompt. Connect the same image to image_7 on the MiniMax H3 Reference to Video node, or use this node's image_7 output.
image_8optIMAGEReference image 8: <Picture 8> in the prompt. Connect the same image to image_8 on the MiniMax H3 Reference to Video node, or use this node's image_8 output.
image_9optIMAGEReference image 9: <Picture 9> in the prompt. Connect the same image to image_9 on the MiniMax H3 Reference to Video node, or use this node's image_9 output.
scenes_from_lyricsoptBOOLEANfalseBuild the whole video from the lyrics: the song is cut where lyric phrases start (needs timestamped lyrics like `[0:15] line`; without timestamps it falls back to the segment mode above) and every scene's imagery is written from its lyric lines - the pictures stage what the words say, while the concept supplies style, palette and motifs. Instrumental stretches still cut on the music.
reference_image_useoptCOMBOCharacters only (ignore picture backgrounds)How the reference pictures may be read. Characters only: every picture is a character/performer reference - identity (and wardrobe) carries over, the photo's background, setting and lighting are ignored, and no scene or location is ever derived from them; a picture note can still declare a specific image a location or prop. Auto: the model decides what each picture shows (a backdrop can become the scene).
scene_briefsoptSTRINGManually planned scenes from chained H3 Scene Brief nodes: each brief (what happens, where, which cast members and pictures) becomes the binding plan for its scene/piece. Pinned numbers take that piece; unpinned briefs fill in order; pieces without a brief stay the model's to invent.
audio_modeoptCOMBOReference audio (<Audio 1> = the song piece)How the song reaches the video model. Reference audio: each piece is attached as ref_audio_1 and the prompts define <Audio 1> (classic Ref2VA; lip-sync is a strong suggestion). Masked latent: for workflows that write the song slice straight into the H3 audio latent and protect it from denoising (e.g. `H3 Song Audio + Masked Video Context` fed by this node's `clip_starts`) - the prompts then reference the protected master-song audio and define no <Audio N>, and lip-sync is enforced by the model itself. Do not wire audio_segments to ref_audio in that setup.
save_scenesoptBOOLEANtrueStore every successful run as a JSON bundle in output/apnext_scenes/ (scenes, synopsis, segment times, durations, clip starts, cast). Reload it any time with APNext H3 Scenes Load - re-render without paying for the LLM again.
scenes_per_calloptINT41–8How many scenes to ask the model for per call. Smaller chunks finish well inside timeout_seconds and fail smaller (a timed-out chunk is retried at half size automatically); larger chunks are slightly cheaper per scene. 1 = every scene written in its own call.
prompt_modeoptCOMBORef2VA (bind reference images)Which official prompt guide the scenes follow, and whether the WRITING model is shown the reference pictures. Ref2VA (guide_ref_en.md): pictures are bound as <Picture N> and also sent to the writer, so it describes the real face. Needs a vision-capable model. Ref2VA blind: the video model still receives every picture under the same label, but the writer never sees one - it takes who is in each picture from the cast lines and image_notes. Use it with any model WITHOUT vision (a text-only local model, an uncensored fine-tune), and write an `Image 1: ...` note per picture. FL / T2VA (guide_base_en.md): everything from scratch in words. Pictures are ignored entirely and no <Picture N> label is written - they still pass through the image outputs. Auto picks Ref2VA when pictures are connected, FL otherwise.
draft_modeloptCOMBOhaikuWho DRAFTS the scene chunks. The main `model` stays the director: it plans the video (synopsis, wardrobe/location locks, scene plan) and runs the continuity repair, while this model writes the scenes from that plan. haiku drafts several times faster than sonnet. `same as model` turns the split off. Ignored when the run is backed by Codex, a local server or an H3 LLM Backend override - those runs use one model throughout.
parallel_chunksoptBOOLEANtrueWrite the scene chunks concurrently instead of one after another: one planning call (by `model`) fixes the synopsis, the locks and a per-scene plan, then up to 4 chunks at a time are drafted from that plan (by `draft_model`), and one continuity pass repairs any drift. Much faster for long songs. Off = the classic serial run where every chunk continues one session. Ignored when resume_session_id is set or the whole song fits in one call.
project_nameoptSTRINGA tag for this run - auto-filled with a random name like 'NeonDollyFoley-7k3q' when the node is created; type your own to rename the project. Wire the node's `project_name` output into Save Video's `filename_prefix` and every clip of the run lands in its own subfolder (output/video/<name>/), so the output folder shows at a glance which videos belong together. Saved scene bundles carry it too. Empty = a fresh random name each run (stable when `seed` is fixed).
transcribe_lyricsoptBOOLEANtrueWhen the lyrics box is empty, transcribe the song with Whisper (openai/whisper-large-v3-turbo via transformers; ~1.6 GB download on first use) into timed `[m:ss] line` lyrics, so the scenes still sing the real words and lyric-timed cuts work. Wire a vocal stem into `vocals` for a cleaner transcription than the full mix.
cut_planoptSTRINGThe `cut_plan` output of an APNext H3 Cut Plan node (or hand-typed lines shaped `01 0:00.00 - 0:12.96`). The video is then cut into EXACTLY these scenes and segment_mode / max_segment_seconds / min_segment_seconds are ignored. Decide the scenes once, see them, edit them, then write.
sound_eventsoptSTRINGThe `events` (or `events_json`) output of an APNext H3 Sound Events node. Every piece's brief then carries the bass hits, impacts, drops, builds and stops that land INSIDE that clip, timed from the clip's own start, and the model is told to stage the picture on them - a cut, a camera hit, a light change, a move that lands on the beat. Leave it unconnected and the scenes are written from the song's energy profile alone, as before.
beat_gridoptSTRINGThe `grid_json` output of an APNext H3 Beat Grid node: its tempo, every beat and the downbeats (phase-fitted to the Sound Events hits, with your BPM override). Every piece's brief then lists ITS beats from this grid - `[beat] every 0.469 s at +0.00 +0.47 ...` - instead of the writer's own rougher measurement. Leave it unconnected and the beats come from the song structure the writer measures itself.
lyrics_inoptSTRINGLyrics from another node - the `lyrics` output of an APNext H3 Lyrics Transcribe node, or any STRING. Used only while the `lyrics` box above is empty: anything typed there wins, so Whisper's words can be corrected by hand without unplugging the socket.
vocalsoptAUDIOOptional vocal stem (e.g. AudioSeparation's vocals output) used only for lyric transcription - much cleaner than transcribing the full mix. Ignored when lyrics are typed in or transcribe_lyrics is off.
lyric_interpretationoptCOMBOAuto (the writer decides per song)How far the pictures may stray from the words. The lyric is always audible and sung as performance_mode says; this decides what the PICTURE does with it. Literal shows what the words say. Loose stages the feeling, never the nouns. Metaphor builds the whole video on one image system that is not the lyric. Counterpoint tells a different story that rhymes with the song. Reframe keeps the subject but moves it into another world, era or genre. Surreal is dream logic that escalates. Surprise me picks one of the non-literal readings by seed, so every re-run is a different video. Auto leaves it to the writer.
transition_styleoptCOMBOReveal (a camera move discloses a new space around them)What a CONTINUING scene may do with its place. The chain carry keeps the actor exactly - pose, direction, speed - but not the previous place, so a change of place inside a take has to be written. Auto: the writer picks walk-through or reveal per scene where the plan moves. Stay: same place, always. Walk-through: the actor carries the take through a door / corridor / corner into the new place, camera following. Reveal: the actor holds, the camera moves, and a new space is disclosed around them. New place: every scene gets its own place - continuing scenes move there inside the take (walk-through or reveal, alternating), so no two consecutive scenes share a location. Hard cuts are unaffected.
run_modeoptCOMBOwrite (call the LLM)Continue from the scenes you already have. Reuse last run: skip the LLM and return the newest bundle this node saved to output/apnext_scenes/ (`save_scenes`) - the same scenes, lengths, clip starts and project name - so everything downstream renders again with other sampler settings, seeds or references. While it is on, edits to the direction, lyrics or cast are ignored; set it back to write for a new script. For an older run pick the file in APNext H3 Scenes Load.
continuityoptCOMBOcut plan decides (continue over soft cuts, hard cut on drops / sections / taps)Which pieces are written as the SAME take carrying on and which open on a hard cut. Cut plan decides: soft cuts (onset / downbeat / lyric line) continue, drops, section starts, stops and taps cut. Flow everywhere: one continuous take. Cut everywhere: every scene is its own clip - a fresh setup at every cut, nothing carried over. Set H3 Chain Render's `continuity` to the same value so the render matches what was written.
session_historyoptCOMBOWindowed (brief + previous chunk - fits long serial runs)Serial runs on a LOCAL model (ollama:/lmstudio:/local:) replay the whole session on every chunk, so context grows with every scene until num_ctx bursts. Windowed always replays the first exchange (the brief and its synopsis reply) plus AS MANY recent chunks verbatim as num_ctx affords - a short song still sees everything; on a long one the chunks that no longer fit are folded into a running recap (one small extra LLM call) that rides along, on top of the one-line story-so-far gists every chunk gets. Full is the old replay-everything behaviour; fine for short songs or big num_ctx. Claude Code / API models ignore this.

Outputs (23)

NameTypeDescription
scenesSTRING
durationsFLOAT
lengthsINT
audio_segmentsAUDIO
segment_tableSTRING
scenes_textSTRING
synopsisSTRING
castSTRING
scene_countINT
song_secondsFLOAT
session_idSTRING
infoSTRING
image_1IMAGE
image_2IMAGE
image_3IMAGE
image_4IMAGE
image_5IMAGE
image_6IMAGE
image_7IMAGE
image_8IMAGE
image_9IMAGE
clip_startsFLOAT
project_nameSTRING