Client Sound API

Controlling sounds started from Lua — volume, panning, looping, and the cleanup rules that stop sounds from playing forever.

The client global play_sound(sound_name, volume) (see lua-client-globals.md) starts a sound declared in a sound-config.md file and returns a sound object — a handle to that one playing instance. Keep the handle if you want to control or stop the sound later; each call to play_sound creates an independent instance.


Methods

MethodReturnsDescription
pause()Pause playback (position is kept).
resume()Resume after a pause.
stop()Stop playback permanently. Safe to call more than once.
set_volume(volume)Change volume. Same 0–100 percent scale as play_sound100 is full volume, 0 is silent.
set_panning(p)Set stereo position: 0.0 = fully left, 0.5 = center, 1.0 = fully right.
set_looping(bool)true loops the whole sound from the start; false clears looping.
set_looping_region(start_s, end_s)Loop only between two time points, in seconds — e.g. skip an intro and loop the body.
get_position()numberCurrent playback position in seconds.
get_sound_state()stringOne of "Playing", "Pausing", "Paused", "WaitingToResume", "Resuming", "Stopping", "Stopped".

Cleanup — Don’t Leak Sounds

Losing the handle does not stop the sound. If you start a looping sound and drop the handle, it plays forever (until the session ends). This is the number one cause of stacked, ever-louder audio after mobs respawn.

The engine gives you one automatic cleanup path: any sound object stored as a top-level value of an entity’s lua_data table is stopped automatically when that entity disappears from the client (walks out of range, dies, despawns):

-- a mob's scripts.client-on-create
return function(entity)
    local music = play_sound("MyMod:boss_theme", 70)
    if music ~= nil then
        music:set_looping(true)
        entity.lua_data.boss_music = music   -- auto-stopped when the boss goes away
    end
end

Not covered by auto-cleanup — stop these yourself:

  • Sounds nested inside a sub-table of lua_data (e.g. entity.lua_data.sounds.music) — keep handles top-level instead.
  • Sounds stored on a static object’s lua_data — interactables don’t auto-stop their sounds; stop looping sounds in your own hooks (e.g. when the object is toggled off).
  • Sounds you keep in ordinary Lua variables.

One-shot (non-looping) sounds simply end on their own; leaking those handles is harmless.


See Also