Quick answer: fadeout takes milliseconds. Use sound.fadeout(2000) for a 2-second fade, not fadeout(2). To target a specific channel, capture the Channel from sound.play and call channel.fadeout(ms).

Here is how to fix Pygame sound fadeouts that either snap to silence instantly or fail to stop the sound. The unit confusion (seconds vs milliseconds) catches everyone at least once.

The Symptom

Sound.fadeout(3) leaves the sound playing as if nothing happened. Or sound stops abruptly without the expected fade.

What Causes This

Milliseconds, not seconds. 3 ms fade is essentially instant. 3000 ms is a 3-second fade.

Wrong target. Sound.fadeout fades all channels playing that sound; channel.fadeout fades just one.

stop overrides fadeout. Calling stop after fadeout cancels the fade.

The Fix

Step 1: Use milliseconds.

import pygame
pygame.init()
pygame.mixer.init()

s = pygame.mixer.Sound("assets/loop.wav")
ch = s.play(loops=-1)

# Fade over 2 seconds
ch.fadeout(2000)

Step 2: For music, same units.

pygame.mixer.music.load("song.ogg")
pygame.mixer.music.play()
# Later
pygame.mixer.music.fadeout(3000)   # 3-second fade

Step 3: Target a specific channel.

ch = s.play()
if ch is None:
    print("No free channel")
else:
    ch.fadeout(1000)

Step 4: Avoid stop after fadeout.

# BAD: stop preempts fade
ch.fadeout(2000)
ch.stop()   # silence is immediate

# GOOD: let fade complete
ch.fadeout(2000)

Step 5: For complex transitions, schedule with timer.

FADE_DONE = pygame.USEREVENT + 5
ch.fadeout(2000)
pygame.time.set_timer(FADE_DONE, 2000)

# In event loop
if event.type == FADE_DONE:
    next_track.play()
    pygame.time.set_timer(FADE_DONE, 0)   # cancel

“Milliseconds always. Channel for specific fades. Do not stop on top of fade.”

Related Issues

For mixer end events, see mixer.music End Event. For event queue overflow, see Event Queue Overflow.

ms units. Channel target. No double-stop. Fades land smoothly.