URN Audio Mixer
A multitrack timeline inside ComfyUI? URN Audio Mixer actually pulls it off
- audio
ComfyUI's audio story is mostly model-side: ACE-Step for instrumental music, Chatterbox for a voice, MMAudio for foley. What it has never had is the boring part after generation - putting a vocal, a backing track and a sound effect on a timeline at the right times and balancing them. In a normal editor you'd do that in a DAW, which means exporting out of ComfyUI and losing the graph.
URN Audio Mixer tries to keep it inside the node. It's a multi-track timeline with draggable clips, per-clip trim and fades, a track volume slider, a browser-side preview, and one mixed AUDIO output.
How it works
Be clear about the split: this node is mostly frontend. The JavaScript draws the timeline, waveforms, clip rectangles and trim/fade handles, and it drags files from your ComfyUI input folder onto tracks. Everything you set - track volumes, clip positions in real seconds, trim points, fade lengths, padding - gets serialised into the mix_state_json widget and saved inside the workflow, so the arrangement travels with the graph.
At execution, the backend reads that state, decodes every clip with PyAV, applies per-clip trim and cosine fades, matches sample rates and channel counts, and sums everything into one buffer. The export window is calculated, not guessed: it starts at the earliest used (trimmed) clip position across all tracks and ends at the furthest used clip end, preserving the relative gaps and overlaps inside that span. pad_start_sec and pad_end_sec are then added around it. Finally, if the summed mix exceeds 1.0, the whole thing is scaled back - peak protection, not a limiter.
Preview playback is entirely browser-side, so you can audition a mix without queuing a workflow. That's the feature that makes the node usable - an audio editor with no preview is a form.
Inputs and outputs that matter
There are only three widgets, and you'll touch one of them:
mix_state_json- internal storage for the arrangement. Leave it alone; the readme says so explicitly, and hand-editing it is how you turn a working mix into a mystery.pad_start_sec/pad_end_sec- whole seconds of silence before and after the exported span. Bumppad_start_secwhen you need a beat of air before the first hit.
Output is a single audio socket. There are no per-track outputs - everything is summed. Feed it to URN Save Audio with Lyrics, or to a video model that needs a driving track.
On the node itself, the controls you'll actually use: Add Track or drop files in the top area (each dropped file becomes its own track), a track volume slider per track, Add Audio File to stack more clips onto a track, and trim/fade handles on each clip. New clips default to fade in/out equal to 5% of their duration, and the fade handles can't cross each other. The red outline tells you which clip is selected, which matters because Remove File removes only that one.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/Clivey1234/Comfyui_URN_AudioTools
Restart ComfyUI. This node needs nothing beyond core ComfyUI - it decodes with the PyAV that ships with normal installs - but the whole pack shares a requirements.txt (faster-whisper, audio-separator, librosa, scipy, soundfile, mutagen) used by the splitter, extender and lyrics nodes. If you cloned manually and skipped install.bat, those other nodes will fail; install the requirements with ComfyUI's Python if you want the full pack.
Manager route: search Comfyui_URN_AudioTools. After updating an existing install, hard refresh the browser - this node's entire UI is a frontend file, and a cached old copy renders old widgets.
Common issues
The mix is quiet, or quieter than the individual clips. That's peak protection doing its job on a sum. Three tracks at full volume will clip, and the node scales the whole result down rather than flattening the loudest moment. Pull track volumes down and re-preview.
Saved workflow opens with one track empty. The mixer references files by name from ComfyUI's input folder. Delete or rename a source file and the clip has nothing to decode. Keep sources where they are.
Timing looks right in the preview but wrong after export, or vice versa. Preview is browser-side and export is backend-side; position values are stored in real seconds, so they should agree. If they don't, check padding first - pad_start_sec shifts everything in the export while the on-screen timeline doesn't move.
A clip's fades reset after I reload the workflow. They shouldn't - fades and trims are part of mix_state_json and are saved with the graph. If they truly vanish, save the workflow after editing and make sure you're not importing an old version of it.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| mix_state_json | STRING | [] | — |
| pad_start_sec | INT | 00–86400 | — |
| pad_end_sec | INT | 00–86400 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| audio | AUDIO | — |