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
| Method | Returns | Description |
|---|---|---|
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_sound — 100 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() | number | Current playback position in seconds. |
get_sound_state() | string | One 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
- sound-config.md — declaring sounds
- lua-client-globals.md —
play_sound - lua-client-entity.md —
lua_dataand hook contexts