APNext H3 Music Video Writer
A whole music video from one audio file
- audio
- llm
- image_1
- image_2
- image_3
- image_4
- image_5
- image_6
- image_7
- image_8
- image_9
- 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
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_seconds–max_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 awardrobeis 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, plusimage_1..9reference pictures.
The outputs are matched lists, which is the whole architecture: scenes → the video node's prompt, lengths (frame counts) → length, audio_segments → ref_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.
Inputs (55)
| Name | Type | Default | Description |
|---|---|---|---|
| audio | AUDIO | The song. It is cut into 5-15 s pieces and every piece becomes one scene / one clip. | |
| direction | STRING | The music-video concept: who performs, where, the look, the story arc, recurring motifs, what the chorus looks like vs the verses. Free text. | |
| lyrics | STRING | Lyrics, 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 spread evenly over the song (approximate). Empty = instrumental video. | |
| performance_mode | COMBO | Performance (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_mode | COMBO | Auto (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_seconds | FLOAT | 15.05.2–15.1 | Longest piece (H3 renders up to ~15 s). Lengths snap to H3's frame grid. |
| min_segment_seconds | FLOAT | 5.25.2–15 | Shortest piece. 124 frames (~5.2 s) is the shortest trained clip. |
| shots_per_scene | COMBO | Auto | Shots per scene. Auto lets the model cut to the music (more shots in loud parts). |
| visual_style | COMBO | Live-action, 35mm cinematic film aesthetic | Opens every [Shot 1]; kept identical across the whole video. |
| dialogue_language | COMBO | English | Language of the lyrics (the <d>[...] tag). |
| wildness | INT | 450–100 | 0 = grounded performance video, 100 = fully surreal. Above 40 seeds surreal events. |
| model | COMBO | sonnet | Who writes the prompt. sonnet / opus / haiku / fable / default are Claude Code aliases (`default` = whatever the CLI is configured for). 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. |
| research | BOOLEAN | false | Let Claude Code search the web for real references before writing - the actual location, wardrobe, lighting and physics. Slower, and it reaches the internet. |
| director | BOOLEAN | true | Load 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_subscription | BOOLEAN | true | Hide ANTHROPIC_API_KEY from the CLI so it uses your Claude Code login and subscription seat. Turn off to bill the API key instead. |
| timeout_seconds | INT | 120060–7200 | How 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. |
| seed | INT | -1-1–18446744073709550000 | Seeds the surreal picks and controls caching. -1 re-runs every queue. |
| cast_1opt | STRING | A cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video. | |
| cast_2opt | STRING | A cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video. | |
| cast_3opt | STRING | A cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video. | |
| cast_4opt | STRING | A cast line or block from an H3 Characters node (its `cast` output): the performer(s) and anyone else in the video. | |
| extra_castopt | STRING | Performer(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_languageopt | STRING | — | |
| custom_visual_styleopt | STRING | Any visual style not in the dropdown; overrides the dropdown when filled in. | |
| wardrobeopt | STRING | Wardrobe lock, one line per character, e.g. `Sheldon: brown corduroy jacket, green Flash T-shirt, khaki trousers, small silver ring in the left nostril`. Used word-for-word in every shot. Empty = Claude fixes one outfit per character itself (in the synopsis) and repeats it in every shot. | |
| locationsopt | STRING | Location 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_wardrobeopt | BOOLEAN | true | After writing, check that every shot a character is in restates 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_instructionsopt | STRING | — | |
| image_notesopt | STRING | Per-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_idopt | STRING | Continue an earlier Claude Code run by feeding it that node's session_id. The whole conversation, images included, is still in context. | |
| working_diropt | STRING | A 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. | |
| llmopt | APNEXT_LLM | Optional. 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_1opt | STRING | Steering 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_2opt | STRING | Steering 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_3opt | STRING | Steering 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_4opt | STRING | Steering 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_5opt | STRING | Steering 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_6opt | STRING | Steering 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_7opt | STRING | Steering 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_8opt | STRING | Steering 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_1opt | IMAGE | Reference 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_2opt | IMAGE | Reference 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_3opt | IMAGE | Reference 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_4opt | IMAGE | Reference 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_5opt | IMAGE | Reference 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_6opt | IMAGE | Reference 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_7opt | IMAGE | Reference 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_8opt | IMAGE | Reference 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_9opt | IMAGE | Reference 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_lyricsopt | BOOLEAN | false | Build 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_useopt | COMBO | Characters 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_briefsopt | STRING | Manually 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_modeopt | COMBO | Reference 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_scenesopt | BOOLEAN | true | Store 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_callopt | INT | 41–8 | How 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. |
Outputs (22)
| Name | Type | Description |
|---|---|---|
| scenes | STRING | — |
| durations | FLOAT | — |
| lengths | INT | — |
| audio_segments | AUDIO | — |
| segment_table | STRING | — |
| scenes_text | STRING | — |
| synopsis | STRING | — |
| cast | STRING | — |
| scene_count | INT | — |
| song_seconds | FLOAT | — |
| session_id | STRING | — |
| info | STRING | — |
| image_1 | IMAGE | — |
| image_2 | IMAGE | — |
| image_3 | IMAGE | — |
| image_4 | IMAGE | — |
| image_5 | IMAGE | — |
| image_6 | IMAGE | — |
| image_7 | IMAGE | — |
| image_8 | IMAGE | — |
| image_9 | IMAGE | — |
| clip_starts | FLOAT | — |