Session 8: Sound and music
Phase 6 — Pygame · Session 8 of 14
What we’re learning today
Your games have been silent. Today changes that.
Pygame’s mixer plays sound effects (short clips like
a “bounce” or a “ding”) and music (longer tracks that
loop in the background). By the end of class, your Pong
will sound like a game — paddle bounces, scoring, and
maybe even background music.
Sound is the smallest amount of code with the biggest “feel” change in any game.
You’ll need to remember from last time
- Your Pong project from Session 6 (or any other game).
- Loading resources outside the loop — Session 3. Same idea applies to sounds.
pygame.event.KEYDOWN— Session 4. Sounds usually trigger from events.
What you’ll need today
- Some sound files. In class, there’s a folder of free game sounds for you. At home, you can grab free sounds from freesound.org or opengameart.org.
- A working game (Pong, fruit catcher, collector — any one works).
Part A: Sound effects
Two kinds of audio
Pygame splits audio into two APIs:
pygame.mixer.Sound— short clips (a beep, a bounce, a click). Can play many at once. Loaded fully into memory.pygame.mixer.music— long tracks (background music, a song). Streamed from disk, only one at a time.
Use Sound for game effects. Use music for background tracks.
Loading and playing a sound
The basic pattern. Open a copy of your Pong (or any game). Add at the top:
pygame.init()
# ... existing setup ...
# Load sounds — once, outside the loop
bounce_sound = pygame.mixer.Sound("bounce.wav")
score_sound = pygame.mixer.Sound("score.wav")
Inside the loop, where the relevant thing happens:
if ball_rect.colliderect(left_paddle):
ball_dx = -ball_dx
bounce_sound.play() # ← here
if ball_rect.right < 0:
right_score += 1
reset_ball()
score_sound.play() # ← here
Save. Run. You should hear a sound when the ball bounces. And a different one when someone scores.
That’s it. Two lines per sound.
Sound formats
Pygame supports .wav, .ogg, and .mp3 (with the
right system libraries — sometimes spotty on Linux).
- WAV — uncompressed. Big files, instant load, perfect quality. Best for sound effects.
- OGG — compressed, open format. Smaller files, good quality. Best for music.
- MP3 — compressed, common. Sometimes works, sometimes doesn’t. Convert to OGG if you have issues.
For sound effects (short clips), stick to WAV.
Volume
Sounds can be quieter. Set volume from 0.0 (silent)
to 1.0 (full):
bounce_sound.set_volume(0.4)
Per-sound, set once after loading. Or change at runtime to make a sound fade.
You can also set the channel’s volume — but for most games, per-sound is enough.
Sound + boundaries
Add a “wall bounce” sound for when the ball hits the top or bottom:
wall_sound = pygame.mixer.Sound("wall.wav")
# in update:
if ball_rect.top < 0 or ball_rect.bottom > HEIGHT:
ball_dy = -ball_dy
wall_sound.play()
The two-tone “bonk-bing” of a Pong bounce comes from having different sounds for paddle vs wall hits.
Checkpoint: Your game plays sounds when at least two different things happen. This is the natural stop point if class is cut short.
Part B: Background music
Now the long-form audio. Music plays continuously while the game runs.
Load and play
pygame.mixer.music.load("background.ogg")
pygame.mixer.music.set_volume(0.3) # quieter than effects
pygame.mixer.music.play(-1) # -1 means loop forever
The play(-1) is the key — it loops the track
indefinitely. play(1) plays it once. play(3) plays
it three times, then stops.
Add this once, near the start of your program (after
pygame.init()). The music starts playing immediately.
Run. Background music plays. Sound effects (paddle bounces, scores) play over the music. Both work.
Pausing and stopping music
Common controls:
pygame.mixer.music.pause() # pause
pygame.mixer.music.unpause() # resume
pygame.mixer.music.stop() # stop completely
pygame.mixer.music.set_volume(0.0) # silent (without stopping)
Often games have a key to mute. In your event handling:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
# toggle music
if pygame.mixer.music.get_busy():
pygame.mixer.music.pause()
else:
pygame.mixer.music.unpause()
Press M to toggle music.
Stretch — switch tracks for game over
When the game ends, switch from upbeat music to something more dramatic. You’ll need a second track:
# at game over:
pygame.mixer.music.load("game_over.ogg")
pygame.mixer.music.play() # play once
You can load a new track at any time.
Stretch — sound channels
For more advanced control, Pygame uses channels. By default sounds play on whichever channel is free; you can also pin a sound to a specific channel:
channel = pygame.mixer.Channel(0)
channel.play(bounce_sound)
channel.set_volume(0.5)
Useful for “I want this kind of sound to never overlap itself” or “I want music in one channel and effects in another.” Don’t drill — mention only if asked.
Extension — record your own sound
Free apps like Audacity (or any phone voice recorder) let you record short clips. Save as WAV. Use as game sounds. Your game now has your voice or your tap in it.
If you have a buddy nearby, record them saying “score!” or “ouch!” and use it. Suddenly it’s your game.
Extension — sound for every event
Add a sound for everything:
- Game start: an intro sound.
- Each score: a different ding.
- Bouncing off a wall vs paddle: two sounds.
- Each paddle: different sounds (left vs right).
- A “warning” sound when one player is one point from losing.
Each one is two lines of code (load + play). Add up to a very polished feel.
Wrap-up
Before we leave, share with the room:
- For everyone — show your game with sound. Did the feel change?
- For the kids who added music — what music?
- Anyone find or record a particularly satisfying sound?
- (Honest question:) Is your game more fun with sound? Or just louder?
Today you learned:
- Two audio APIs:
mixer.Sound(effects) andmixer.music(music). - Load once, play many times — same pattern as images.
.set_volume(0.0 - 1.0)for individual sounds..play(-1)for looping background music.- Sound triggers from events or game state — bounce, score, lose.
Sound is the smallest amount of code with the biggest feel change in any game. Two lines per sound. Huge difference.
Next up: sprite classes and groups — using Phase 4’s
classes properly. Your games have many objects — bullets,
enemies, particles. Today you’d track each in a list.
Next session, sprites become real Sprite instances,
and Pygame’s Group handles them all at once.
If you missed this session
Open Thonny. Get a sound effect (any short WAV) and optionally a music file (any OGG).
-
Open one of your games (Pong, fruit catcher, etc.).
-
Load the sound:
bounce_sound = pygame.mixer.Sound("bounce.wav"). -
Play it where appropriate (collision, score, etc.):
bounce_sound.play(). -
(Stretch) Add background music:
pygame.mixer.music.load("background.ogg") pygame.mixer.music.play(-1) -
(Stretch) Add a mute key (M).
About 30-45 minutes. By the end your game should make noise.
Stretch and extension ideas
- Different sound per event — wall bounce vs paddle bounce vs score vs win.
- Volume control — keys to turn music up/down.
- Mute toggle.
- Music switches for different game states (intro, playing, game over).
- Random sound variety — instead of one bounce
sound, pick from a list of three each time.
bounce_sounds = [ pygame.mixer.Sound("bounce1.wav"), pygame.mixer.Sound("bounce2.wav"), pygame.mixer.Sound("bounce3.wav"), ] random.choice(bounce_sounds).play() - Recorded sounds — your voice, your tap, your whatever. WAV files work fine.
- Sound chiptune music — search “chiptune music free” for retro game-style tracks.
- Push the new sound version to GitHub (Session 7
callback).
git add,commit -m "Add sound effects",git push.
What’s next
Next week: sprite classes and groups. Your games
have lots of objects. Tracking them in raw lists
(fruits = [], enemies = []) works for a while but
gets messy fast. Pygame’s Sprite class and Group
container make organizing many objects much cleaner.
This is where Phase 4’s classes really pay off.