⚠️ Beta State

PyBevy is in an early and experimental stage. The API is incomplete, subject to breaking changes without notice, and you should expect bugs. Many features are still under development.

Audio

Play sound effects, loop background music, and control volume.

Introduction

Audio is essential for game feel. pybevy supports loading audio files (.ogg, .wav, .flac, .mp3), playing them once or looping, and controlling playback at runtime.

Key types:

  • AudioPlayer: Component that triggers audio playback from a loaded source.
  • PlaybackSettings: Initial settings (volume, loop, speed).
  • AudioSink: Component added automatically for controlling playback.
from pybevy.prelude import *
from pybevy.audio import AudioPlayer, AudioSource, PlaybackSettings, AudioSink, Volume

Playing a Sound Effect

Spawn an entity with AudioPlayer and a loaded audio source. By default, audio plays once and stops.

def setup(commands: Commands, asset_server: AssetServer):
    commands.spawn(Camera2d())
 
    # Play a one-shot sound effect
    commands.spawn(
        AudioPlayer(asset_server.load("sounds/sfx.ogg", AudioSource)),
    )

Looping Background Music

Use PlaybackSettings.LOOP for music that repeats forever. You can also set the initial volume.

@component
class BackgroundMusic(Component):
    pass
 
def setup_music(commands: Commands, asset_server: AssetServer):
    commands.spawn(
        AudioPlayer(asset_server.load("sounds/music.ogg", AudioSource)),
        PlaybackSettings(
            mode=PlaybackSettings.LOOP.mode,
            volume=Volume.Linear(0.5),
        ),
        BackgroundMusic(),
    )

Controlling Playback at Runtime

Once audio starts playing, PyBevy adds an AudioSink component to the entity. You can query for it to pause, resume, change volume, or stop playback.

def music_controls(
    keyboard: Res[ButtonInput],
    query: Query[AudioSink, With[BackgroundMusic]],
):
    for sink in query:
        # M: Toggle mute
        if keyboard.just_pressed(KeyCode.KeyM):
            sink.toggle_mute()
 
        # Space: Toggle pause
        if keyboard.just_pressed(KeyCode.Space):
            sink.toggle_playback()
 
        # Up/Down: Volume control
        if keyboard.just_pressed(KeyCode.ArrowUp):
            sink.set_volume(Volume.Linear(min(sink.volume().to_linear() + 0.1, 1.0)))
        if keyboard.just_pressed(KeyCode.ArrowDown):
            sink.set_volume(Volume.Linear(max(sink.volume().to_linear() - 0.1, 0.0)))

Playing Sounds from Game Systems

A common pattern is to play a sound in response to a game event. Simply spawn a new AudioPlayer entity when the event occurs.

def play_on_keypress(
    commands: Commands,
    keyboard: Res[ButtonInput],
    asset_server: AssetServer,
):
    if keyboard.just_pressed(KeyCode.KeyF):
        # Spawn a one-shot sound that auto-despawns when finished
        commands.spawn(
            AudioPlayer(asset_server.load("sounds/sfx.ogg", AudioSource)),
            PlaybackSettings.DESPAWN,
        )

Running the App

Note: This example requires audio files in your assets/sounds/ directory. Replace the file paths with your own .ogg or .wav files.

@entrypoint
def main(app: App) -> App:
    return (
        app
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, (setup, setup_music))
        .add_systems(Update, (music_controls, play_on_keypress))
    )
 
if __name__ == "__main__":
    main().run()