Sound effects for visual novels and Ren'Py
By flreey · Published · Code tested in Ren'Py 8.5.3 on macOS, on 2026-10-31
A visual novel lives on a few small sounds: the blip as a line types out, a knock before someone walks in, rain behind a quiet scene, a heartbeat when it turns tense, and the click of each choice. Below are thirteen of those, free and CC0, each one a page to play and download, and the Ren'Py code that plays them. We ran every block in a test project before publishing, and it caught three things the documentation does not warn about: they are in steps 5 and 6.
1. The sounds a visual novel uses
Each link opens a page with several versions to play and compare; the name in brackets is the event for the command in step 2.
Moving between scenes
- door opening
(door-open) - knock
(knock) - footsteps on wood
(footsteps-wood) - page turn
(page-turn) - phone buzz
(vibrate)
Backgrounds that loop
- rain
(rain) - quiet room
(room-tone) - wind
(wind) - dark ambience
(dark-ambience)
The menus need a click, a hover and a confirm. This set plays them in place:
2. Put the files in game/audio
From the project folder (the one with game/), one command writes these as MP3, each file named after its event:
npx sfxmint add dialogue-blip gasp laugh heartbeat door-open knock footsteps-wood page-turn vibrate rain room-tone wind dark-ambience click hover confirm --out game/audioNext to the files is a SOUNDS.md listing each sound's page and license; move it out of game/ before you build. All 16 take a free account's API key in SFXMINT_API_KEY; without a key you get the first 5. Or download single files from the pages above and drop them into game/audio.
3. Play sounds from your script
label start:
scene bg room
play music "audio/rain.mp3" fadein 2.0 # the music channel loops
"The rain hasn't stopped all day."
play sound "audio/knock.mp3"
"Someone is at the door."
play sound "audio/door-open.mp3"
play audio "audio/footsteps-wood.mp3" # audio: overlaps other sounds
"..."
stop music fadeout 3.0play soundplays once on the sound channel; a secondplay soundreplaces the first.play audioplays once and can overlap anything else, which suits footsteps under a door.play musicloops until stopped;fadeinandfadeouttake seconds.- Short names read better:
define audio.knock = "audio/knock.mp3", thenplay sound knock.
4. Background sound alongside music
Rain under a music track needs its own looping channel. Register one on the effects mixer, so the player's sound volume slider covers it:
init python:
renpy.music.register_channel("ambience", mixer="sfx", loop=True)
label rooftop:
play music "audio/theme.ogg"
play ambience "audio/wind.mp3" fadein 2.0
"It's colder up here."
stop ambience fadeout 2.05. Menu and button sounds
Give every button a hover and a click sound in one place, for example at the end of screens.rpy:
style button:
hover_sound "audio/hover.mp3"
activate_sound "audio/click.mp3"This does not reach the choice menu. In the default screens.rpy (Ren'Py 8.5), choice_button is declared is button and then again is default; the second one wins, so choices inherit nothing from button. Give them their own:
style choice_button:
hover_sound "audio/hover.mp3"
activate_sound "audio/click.mp3"The same goes for any style in that file declared is default; check the parent before you expect it to inherit.
6. Text blips while a line types
A character callback runs as a line is shown. Start a looping blip once the text begins to type, and stop it when the line has finished. Play it on a channel of its own:
init python:
renpy.music.register_channel("blip", mixer="voice", loop=True)
def blip(event, interact=True, blipfile="audio/dialogue-blip.mp3", **kwargs):
if not interact:
return
if event == "show_done":
renpy.music.play(blipfile, channel="blip")
elif event in ("slow_done", "end"):
renpy.music.stop(channel="blip")
define m = Character("Mina", callback=blip)
define k = Character("Kai", callback=blip, cb_blipfile="audio/blip-low.mp3")The cb_ prefix passes a different blip to each character, so voices are told apart: download a lower one as blip-low.mp3. The voice mixer puts blips under the player's voice volume slider.
- Why not
renpy.sound.play, as Ren'Py's own example does? The blip then shares the sound channel withplay sound. In our test, the knock from step 3 was still playing when Kai's line began, and the blip replaced it at once. On its own channel the knock plays out. - A new project shows text instantly, so the blip starts and stops at once and is never heard: the template's
options.rpyhasdefault preferences.text_cps = 0. Set a speed there, for example40characters a second; in our test a 32-character line then blipped for 0.8 s. Players who already ran the game keep their own Text Speed setting. - Sound setup in its own file needs
init -1. Ren'Py runs files in name order, so asounds.rpyholdingblip()runs afterscript.rpydefines the characters that use it, and the game stops withNameError: name 'blip' is not defined. Writeinit -1 python:instead ofinit python:there.
All of this as a project you can launch and test: renpy-visual-novel on GitHub.
Where the code comes from
Every statement above follows the Ren'Py documentation and ran in Ren'Py 8.5.3 on macOS, on 2026-10-31, in a test project that played through every line, logged what each channel was playing, and checked the button and choice styles:
Questions
Which audio format does Ren'Py play?
Ren'Py plays OGG Vorbis, Opus, MP3, WAV and FLAC. MP3 files from the library play as they are; convert to OGG or Opus if you want the smallest build.
Where do the files go in a Ren'Py project?
In game/audio. Paths in your script are relative to the game folder, so game/audio/door-open.mp3 is "audio/door-open.mp3".
Why does a second sound stop the first?
The sound channel plays one file at a time. Use play audio for sounds that may overlap, or register a channel of your own for a loop that should keep going.
Can I sell my visual novel with these sounds?
Yes, they are CC0. Steam and itch.io ask you to disclose AI-generated content; CC0 sound effects for commercial games covers both.