CoachBate Shot Audio Tail
Silence at the end of a shot, on purpose, from a JSON field
- audio
- AUDIO
A one-trick node, and a useful trick
CoachBate Shot Audio Tail takes audio and a shot_payload, and returns AUDIO. That's all it
does. If the shot's JSON has an output_audio_tail object, it keeps the first N seconds of the
speech, appends M seconds of silence, and hands you the result. If the shot's JSON doesn't have
that field, the audio passes straight through untouched - an identity node, no cost.
Why would you want that? Because the end of a generated shot is where audio tends to be weakest. H3 with a song mask will happily keep producing plausible dialogue right to the last frame, and if your editorial plan is "shot ends, beat of silence, cut to the next scene," generating that silence inside the model gives you whatever the model thinks should follow the line. Cutting it and replacing it with true digital silence is deterministic, and it's the same every time you re-render the shot.
The exact field contract
The output_audio_tail object is strict - it accepts exactly four keys and rejects anything else,
including a misspelling:
{
"shot_id": "014",
"duration_seconds": 8,
"video_prompt": "...",
"output_audio_tail": {
"keep_seconds": 6.5,
"silence_seconds": 1.5,
"sample_rate": 32000,
"channels": 2
}
}
keep_seconds and silence_seconds must be positive finite numbers; sample_rate and channels
must be positive integers. The node trims with TrimAudioDuration (starting at 0), builds the
tail with EmptyAudio, and joins them with AudioConcat in the after direction - all existing
installed audio nodes, no bespoke DSP hiding in the pack.
Note the doc's own warning: this affects generated output only, never the source file. And the README is blunt that you shouldn't add the field unless you mean it, because a silent tail is an editorial decision that survives every re-render, which cuts both ways. If you add it to a shot and then wonder months later why that one shot has a beat of nothing at the end - that's this node, and the answer is in the JSON.
Where it sits: after the audio comes back from the decode, before you mux. It works on any AUDIO stream, so it's just as happy on a ComfyUI-loaded song as on generated H3 audio.
Inputs and outputs
Required: audio (AUDIO), shot_payload (STRING, forced input - wire it from CoachBateShotLoader
so both stay in sync). Output: AUDIO.
That's the entire node. There are no widgets, which is worth saying plainly because it looks alarming in the node list: everything it needs is in the shot record.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/CoachBate/ComfyUI-CoachBate.git
Or ComfyUI Manager → search "CoachBate" → restart ComfyUI. Category CoachBate / H3. It calls
the installed TrimAudioDuration, EmptyAudio and AudioConcat nodes, which ship with modern
ComfyUI, so keep your install reasonably current. No extra pip dependencies.
Where people get burned
- "output_audio_tail requires keep_seconds, silence_seconds, sample_rate, channels." - the object has a missing or extra key. It's an exact set match.
- "output_audio_tail keep_seconds must be positive and finite." - a zero, a negative, or a string. JSON numbers only, and both time values have to be > 0.
- Wrong-sounding result after the join. Check that
sample_rateandchannelsmatch the audio you're feeding in - the silence is built at whatever you specify, and a mismatch is the one way to get an audible artefact out of a node that's supposed to be inaudible.
Inputs (2)
| Name | Type | Default | Description |
|---|---|---|---|
| audio | AUDIO | — | |
| shot_payload | STRING | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| AUDIO | AUDIO | — |