Test log: how to add music to a video. Run 2026-10-23 by Koldflux (hub-author-edit). Tools: ffmpeg 6.1.1-3ubuntu5 on Ubuntu; Playwright headless Chromium against https://koldflux.com/tools/video/add-audio (live, production); levels with ffmpeg volumedetect (mean_volume), loudness with ebur128. ## Inputs voice: Kokoro-82M (Apache-2.0 weights, kokoro-onnx MIT) voice af_heart, our own script: 'Pour the water slowly in small circles. Then let the coffee bloom for thirty seconds before the second pour.' (6.4 s) music: Beethoven, 32 Variations in C minor WoO 80, Wikimedia Commons file (composition public domain, recording CC0 by its uploader), seconds 20 to 40 of the file, 44.1 kHz stereo voice.wav integrated loudness -22.1 LUFS; music20.wav -22.5 LUFS (ebur128). Voice raised to -16 LUFS (loudnorm) and music bed set to -20 LUFS (loudnorm) for the demo. ## Part 1: ducking measured (mean_volume, dB). Voice speaks 1.5 s to 7.9 s of each 9 s half. sp18 [speech 1.8-6.0s] -16.3 dB sp18 [lead 0-1.2s] -91.0 dB sp18 [gap 8-9s] -91.0 dB sp18 [speech2 10.8-15.0s] -16.3 dB bed18 [speech 1.8-6.0s] -22.1 dB bed18 [lead 0-1.2s] -22.9 dB bed18 [gap 8-9s] -26.4 dB bed18 [speech2 10.8-15.0s] -22.0 dB mixA [speech 1.8-6.0s] -15.3 dB mixA [lead 0-1.2s] -22.9 dB mixA [gap 8-9s] -26.4 dB mixA [speech2 10.8-15.0s] -15.3 dB mixB [speech 1.8-6.0s] -16.2 dB mixB [lead 0-1.2s] -22.9 dB mixB [gap 8-9s] -26.7 dB mixB [speech2 10.8-15.0s] -16.1 dB Ducked bed alone (sidechaincompress threshold=0.02 ratio=6 attack=30 release=500 makeup=1), mean_volume: voice window 1.8-6.0 s: -34.3 dB (flat bed in the same window: -22.1 dB); lead 0-1.2 s: -22.9 dB (unchanged); gap 8-9 s: -26.7 dB (flat bed -26.4 dB) Other settings tried (bed in voice window): threshold 0.02 ratio 10: -34.7; threshold 0.01 ratio 20: -39.9; threshold 0.005 ratio 20: -45.6 ## Part 2: Koldflux add audio tool, 3 runs on the live site, 2026-10-23 (Playwright, headless Chromium) inputs: 18 s picture-only MP4 or the same picture with the voice track (AAC), plus an 18 s MP3 of the music browser run time per job including page load: replace 2.3 s, mix 25% 2.2 s, mix 100% 2.2 s source picture MD5=6a6573a3501c741c3e0d8bea4f2924da == replace100: h264,201226 aac,191951 18.018005 picture MD5=6a6573a3501c741c3e0d8bea4f2924da lead(music only) 0-1.2 s: -27.8 dB speech 1.8-6.0 s: -27.2 dB gap(music only) 8-9 s: -29.6 dB == mix25: h264,201226 aac,193495 18.000000 picture MD5=6a6573a3501c741c3e0d8bea4f2924da lead(music only) 0-1.2 s: -39.7 dB speech 1.8-6.0 s: -16.2 dB gap(music only) 8-9 s: -41.7 dB == mix100: h264,201226 aac,194758 18.000000 picture MD5=6a6573a3501c741c3e0d8bea4f2924da lead(music only) 0-1.2 s: -27.8 dB speech 1.8-6.0 s: -16.0 dB gap(music only) 8-9 s: -29.6 dB ## Part 3: one-step ducking command on the voice video (picture copied) ffmpeg -i voicevid18.mp4 -i music18.mp3 -filter_complex "[0:a]asplit=2[sc][voice];[1:a]volume=1.0[bed];[bed][sc]sidechaincompress=threshold=0.02:ratio=6:attack=30:release=500:makeup=1[duck];[voice][duck]amix=inputs=2:duration=first:normalize=0,afade=t=out:st=16:d=2[a]" -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k out/duck.mp4 (second run: music shorter than video, looped with -stream_loop -1 -i music8.mp3) ffmpeg -i voicevid18.mp4 -stream_loop -1 -i music8.mp3 -filter_complex "[0:a]asplit=2[sc][voice];[1:a]volume=1.0[bed];[bed][sc]sidechaincompress=threshold=0.02:ratio=6:attack=30:release=500:makeup=1[duck];[voice][duck]amix=inputs=2:duration=first:normalize=0,afade=t=out:st=16:d=2[a]" -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k out/duck-loop.mp4 == duck: 18.000000 picture MD5=6a6573a3501c741c3e0d8bea4f2924da lead 0-1.2: -27.8 dB speech 1.8-6.0: -16.3 dB gap 8-9: -30.0 dB speech2 10.8-15: -16.2 dB fade-start 16.2-17.2: -26.5 dB fade-end 17.5-18: -44.2 dB == duck-loop: 18.000000 picture MD5=6a6573a3501c741c3e0d8bea4f2924da lead 0-1.2: -28.2 dB speech 1.8-6.0: -16.3 dB gap 8-9: -29.6 dB speech2 10.8-15: -16.3 dB fade-start 16.2-17.2: -26.6 dB fade-end 17.5-18: -45.5 dB