SnipAudioCut audio

Why are there gaps between joined MP3 files, and how do you remove them?

Published 2026-09-30 · by patrick-eu

Short answer: the gap is stored inside each MP3 file, not added by the joiner. An MP3 encoder pads the start and the end of every file to whole frames, and when the parts are played one after another, that padding becomes a short silence at each seam. Whether you hear it depends on one detail: whether the files carry a LAME tag that tells the decoder how much padding to strip.

To remove the gap from untagged parts, open each one in the audio cutter, type 0.025 as the start and the clip length minus 0.006 s as the end, export WAV, then join the WAVs in the audio joiner. Done that way, our parts landed within 2 samples of the original with 0 ms at each seam.

Where the gap comes from

An MP3 stores audio in frames of 1,152 samples (ISO/IEC 11172-3), which is 26.1 ms at 44.1 kHz. The encoder cannot write a part of a frame, so it does two things to every file it makes:

The LAME encoder records both amounts in a small header at the start of the file, the LAME tag: 12 bits for the delay and 12 bits for the padding. The fields are written by the function PutLameVBR in libmp3lame/VbrTag.c (LAME source, read 2026-09-30). FFmpeg's MP3 reader looks for that tag and, when it finds the encoder name LAME, Lavf or Lavc, splits the 24 bits into a start pad and an end pad and drops those samples; see mp3_parse_info_tag in libavformat/mp3dec.c (FFmpeg 4.4.2, read 2026-09-30). Chromium decodes audio for web pages with FFmpeg (media/filters/audio_file_reader.cc, read 2026-09-30), so the joiner on this site, which asks the browser to decode each file, gets the same trimming for free. Without the tag, nobody knows how much to trim, and the padding stays in the audio.

A joiner that decodes the parts and writes them one after the other, which is what the audio joiner does, therefore reproduces whatever the decoder gave it: trimmed parts join without a gap, untrimmed parts join with the padding of one part followed by the delay of the next.

How we tested

Results: three parts of one song, joined in the browser

PartsLength shown per partSilence before part 1Gap at seam 1Gap at seam 2Joined length
WAV10.000 s00030.000 s
MP3 with LAME tag (FFmpeg libmp3lame)10.000 s00030.000 s
MP3 without tag (this site's encoder)10.031 s25.1 ms (1,105 samples)31.0 ms (1,368 samples)31.0 ms (1,368 samples)30.093 s
MP3 without tag (FFmpeg, -write_xing 0)10.031 s25.1 ms (1,105 samples)31.0 ms (1,368 samples)31.0 ms (1,368 samples)30.093 s
Measured by snipaudio.com, 2026-09-30, Chromium 153, WAV export, crossfade 0. Mozart excerpt, 128 kbps, three 10-second parts.

The 1,368 samples per seam are exactly the frame arithmetic: 10 s is 441,000 samples, plus the 576-sample encoder delay is 441,576, which needs 384 frames, or 442,368 samples. The 792 samples of padding at the end of one part, plus the 576-sample delay at the start of the next, make 1,368. The joiner showed each untagged part as 10.031 s (442,368 samples) because that is what the browser decoded; the tagged parts showed 10.000 s.

With the tone parts, the silence a listener would get at each seam measured 24 to 38 ms at the -40 dBFS threshold, depending on how the encoder faded the frame edges. The joined tagged tones and the joined WAV tones had no seam silence at all.

The two untagged rows are identical because the tag is the only difference. The tag costs 417 bytes per file: our tagged 10-second parts were 160,958 bytes, the untagged FFmpeg parts 160,541 bytes.

Joining the bytes without re-encoding does not remove the gap

A common suggestion is to join MP3s by simply appending the files, or with FFmpeg's concat demuxer in copy mode, so nothing is re-encoded. That keeps the quality, but the padding stays. We decoded the byte-joined files with FFmpeg and with Chromium:

Byte-joined fileSilence at startGap per seamDecoded length
Three tagged parts, cat01,368 samples (31.0 ms)1,325,999 samples
Three tagged parts, FFmpeg concat copy01,368 samples (31.0 ms)1,325,736 samples
Three untagged parts (this site's encoder), cat1,105 samples1,368 samples (31.0 ms)1,327,104 samples
Three untagged FFmpeg parts, cat1,105 samples216 samples, but one whole frame missing per seam1,324,800 samples
Measured by snipaudio.com, 2026-09-30. FFmpeg 4.4.2 and Chromium 153 gave the same numbers for every file. Original is 1,323,000 samples.

With tagged parts, the tag of the first file strips the silence at the start, but the tags of the second and third file now sit in the middle of the stream, where the decoder ignores them, so the inner seams keep the full 1,368 samples. FFmpeg's concat copy rewrote the header and gave the same seams. The last row is a warning: the untagged FFmpeg parts each start with a small ID3 text tag, and when those tags land between frames, both decoders dropped one whole frame (1,152 samples) at each seam. The gap looks smaller only because 26 ms of the file went missing. We did not check which samples were lost.

Removing the gap with the cutter, then the joiner

The joiner has no per-clip trim, but the audio cutter cuts to the millisecond, so we used it to strip the padding from the untagged parts made with this site's own encoder. Each 10.031 s part was opened in the cutter with start 0.025 and end 10.025 typed in, then exported as WAV.

StepResult
Cutter, untagged 10.031 s part, start 0.025, end 10.025, WAV export441,000 samples, music 2 samples (0.05 ms) later than in the original
Joiner, the three cut WAVs, WAV export30.000 s, 0 samples at each seam, every part 2 samples late
Joiner, the three cut WAVs, MP3 128 kbps export0 samples at each seam; 1,107 samples of silence at the start of the new file
Joiner, the three untagged parts, crossfade 0.1 s, WAV exportno silence below -40 dBFS at the seams; joined tones 5.913 s instead of 6.113 s
Measured by snipaudio.com, 2026-09-30, Chromium 153.

Why 0.025 and 10.025: the decoded part starts 1,105 samples late (0.02506 s) and ends with 263 samples of padding (0.006 s), so 10.031 minus 0.006 is 10.025. The cutter's fields take whole milliseconds, which is where the 2-sample offset comes from; it is the same for every part, so the seams are still exact. For a 2-second part, the ends were 0.025 and 2.025.

A crossfade hides the silence rather than removing it: the 0.1 s overlap covers the padding, but it also overlaps 100 ms of music at every seam and shortens the result by 0.1 s per seam. Use it when the parts are different songs and a blend is what you want, not when the parts are one continuous recording.

What to do

  1. If you still have the WAV or the original file, join that, or cut the parts fresh and export them as WAV. WAV has no padding.
  2. If you only have MP3 parts, add them to the audio joiner and look at the length shown next to each part. A part that shows a few hundredths of a second more than it should, such as 10.031 s for a 10-second clip, is untagged and will leave a gap. Preview the seam to be sure.
  3. For untagged parts, open each one in the audio cutter, type 0.025 as the start and the shown length minus 0.006 as the end, export WAV, then join the WAVs. Export the joined file as MP3 only once, at the end.
  4. Do not use this site's MP3 export for parts you plan to rejoin. It writes no LAME tag, so every part carries 25.1 ms at the start and padding at the end. Export parts as WAV instead; this is what we measured above.

Questions

Which MP3 files carry the LAME tag?

Files made by LAME itself and by FFmpeg's libmp3lame do, and those joined with no gap in our test. Files made by this site's encoder do not. We did not test files from other encoders, so we cannot say what a given music store or ripping program writes.

Is the gap the same at other bitrates or lengths?

We measured 128 kbps and 2-second and 10-second parts only. The frame arithmetic says the padding depends on the part length, not the bitrate: it is whatever fills the last 1,152-sample frame, so between 0 and 26.1 ms, plus the fixed 576-sample delay. We did not verify other bitrates.

Does exporting the joined file as MP3 add a gap of its own?

Not at the seams. The MP3 export of our fixed join had 0 samples at each seam and 1,107 samples of silence at the very start, which is the encoder delay of the new file. The seams are inside the file, so they are untouched.

Continue with the next audio task