Audio Loudness
Normalize to YouTube, Spotify, or just 'not annoying' — LUFS loudness inside ComfyUI
- audio
- video
- audio
You know the feeling: you export a video, it sounds fine in the editor, and on the phone it's either deafening or a whisper. That's loudness normalization, and it's the most ignored step in AI video. Audio Loudness fixes it properly - it measures your audio and adjusts it to a target loudness (LUFS) and true peak, in the standard modes broadcasters and platforms actually use. It's the stage your final mix goes through before you export.
The mode combo is where you pick the strategy:
- ebu_r128 (default) - true loudness normalization to an integrated LUFS target (
target_i, default -16), a true-peak ceiling (target_tp, default -1.5 dBTP), and a loudness range cap (target_lra). This is the broadcast/streaming standard - YouTube, Spotify, and friends all normalize to roughly this neighborhood. Under the hood it's FFmpeg'sloudnorm(single or two-pass). - dynamic -
dynaudnorm, which normalizes within short windows so quiet passages come up even if the track is dynamic.dyn_frame_ms(500) sets the window;dyn_gauss(31) is the gaussian smoothing window. - normalize - a straight gain to hit a peak or RMS target:
peak_target_db(default -1) withpeak_mode(true_peakor sample peak), or RMS viause_rms+rms_target_db(default -9).
How it works
Every mode measures first, then applies gain - the source shows loudness is measured (with loudnorm's analysis) before the filter runs, which is what makes the targets meaningful rather than guesses. use_lufs toggles whether the measurement path uses LUFS in the normalize mode. You feed audio (COMFYTV_AUDIO) or video (its soundtrack), and the output is the normalized audio. Internal inputs (force_run_token, project_id, parent_output_id) are frontend plumbing - leave them.
Installing ComfyTV
cd ComfyUI/custom_nodes
git clone https://github.com/jtydhr88/ComfyTV
Full backend restart, then ComfyTV → AudioFX; ComfyUI Manager finds "ComfyTV". Zero extra Python deps - the processing runs through FFmpeg filters (loudnorm, dynaudnorm, volume) via the PyAV bindings ComfyUI already ships. No models.
The pack-wide install trap: on macOS / ComfyUI Desktop / multi-install machines, cd ComfyUI/custom_nodes can clone into the wrong instance - clone succeeds, stages never appear. Find the running instance's path in the startup log, clone into that instance's custom_nodes by absolute path (quote paths with spaces/parentheses), confirm ComfyTV/__init__.py is top-level, restart the backend fully.
Common issues
- Track sounds quiet even after normalization. -16 LUFS is a conservative master - it's what streaming targets, but it can feel quiet solo. If you're exporting for your own ears, push
target_ito -14 or -12. If you're shipping to a platform, leave it. - Peaks clip after loudnorm.
target_tpis your ceiling - at -1.5 dBTP there's headroom for the encoder. If you still clip, drop it to -2. If you raised it toward 0, that's on you. - Dynamic mode pumps or breathes.
dyn_frame_mstoo small makes the gain follow every transient. Use 500–1000ms; 3000ms for music. - Two modes give wildly different levels. They should -
normalizeis about peaks/RMS (loudness by level),ebu_r128is about perceived loudness (loudness by ear). Choose by destination, not by which is louder.
Loudness is the invisible final polish. Run this, run Audio Analyze to confirm the platform verdict, ship. That's the whole game.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| force_run_token | INT | 00–2147483647 | Internal — bumped on Run to invalidate ComfyUI's input cache. |
| project_id | STRING | Internal — populated by the projectStore on the frontend. | |
| parent_output_id | INT | 00–2147483647 | Internal — lineage parent set by spawn handlers on the frontend. |
| mode | COMBO | ebu_r128 | 3 options: ebu_r128, dynamic, normalize |
| target_i | FLOAT | -16.0-30–-10 | integrated loudness target (LUFS) |
| target_tp | FLOAT | -1.5-3–0 | max true peak (dBTP) |
| target_lra | FLOAT | 11.01–20 | — |
| dyn_frame_ms | INT | 50010–8000 | — |
| dyn_gauss | INT | 313–301 | gaussian window (odd) |
| peak_target_db | FLOAT | -1.0-30–0 | — |
| peak_mode | COMBO | true_peak | 2 options: true_peak, sample |
| use_rms | BOOLEAN | false | — |
| rms_target_db | FLOAT | -9.0-30–0 | — |
| use_lufs | BOOLEAN | false | — |
| audioopt | COMFYTV_AUDIO | — | |
| videoopt | COMFYTV_VIDEO | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| audio | COMFYTV_AUDIO | — |