Audio & communication
Package audio: 2D and spatial sounds
Play package MP3/WAV assets locally or through server-owned network sounds. Control playback, pause, seek, volume and position with resource-owned handles, routing buckets and late-join timelines.
On this page6 sections
Play .mp3 and PCM .wav files bundled with a resource, without a WebUI surface. Decoding is asynchronous. For named engine/Wwise events, see Visual and audio effects.
Available in client/server 2.31.13+op77.62, protocol 1.25. Local playback needs the updated client; network playback needs both the updated client and server. Sharing protocol 1.25 with an older build does not make these Lua APIs available there.
Manifest and first sound
files { 'audio/lock.mp3', 'audio/click.wav' }
permissions { 'audio.play', 'audio.network' }
audio.play is the client capability; audio.network is the server capability.
Include only those your scripts need. Paths must be declared in files, relative
to the resource, and remain inside it. URLs, absolute paths and traversal are rejected.
-- Client: private UI sound.
local id, reason = Play2DSound('audio/click.wav', {volume=0.5})
if not id then print(reason); return end
-- Client: sound at a fixed world location, heard only by this client.
local lock = Play3DSound('audio/lock.mp3', {x=10,y=20,z=30},
{volume=0.7, minDistance=1, maxDistance=15})
Both return a resource-owned numeric handle, or nil, reason. Allocation success
does not mean decoding finished: use GetSoundState(id) to inspect loading/errors.
Networked playback
-- Server: every ready player in bucket 0 can hear it within 15 metres.
local id = assert(Play3DSound('audio/lock.mp3', {x=10,y=20,z=30},
{network=true, bucket=0, volume=0.7, maxDistance=15, duration=1.2}))
-- Server: private non-spatial notification for one player in their current bucket.
local notice = Play2DSound('audio/click.wav', {
network=true, target=playerId, bucket=GetPlayerRoutingBucket(playerId), duration=0.3
})
Server playback is networked by default; network=false is rejected there.
Client playback is local by default; network=true on the client returns
nil, 'network_audio_requires_server'. A client cannot authorize a broadcast by
adding an option. For a lock action, request the gameplay action on the server;
after validating ownership/distance/permissions, the server creates the sound.
The server owns the sound handle, timeline and audience. Late joiners receive the current timeline rather than restarting from zero; players leaving the bucket lose the sound. Pauses and seeks are included in the next reliable snapshot. Starting a sound may be delayed by asset decoding; the client catches up when decoding completes. This is game-event synchronization, not sample-accurate music synchronization. Repeated changes are coalesced into snapshots at up to 20 Hz; a new resource that was not ready for the first snapshot is retried by the periodic snapshot (up to 5s).
Playback options
| Option | Default | Contract |
|---|---|---|
volume |
1 | 0β1 |
loop |
false | Loop entire clip |
paused |
false | Allocate without starting playback |
offset |
0 | Initial seconds, finite and nonnegative |
minDistance |
1 | Full-gain radius, metres |
maxDistance |
30 | Silence radius, greater than minDistance, up to 1000m |
network |
client false / server true | Runtime authority described above |
bucket |
0 | Server audience routing bucket |
target |
0 | Server-only optional player ID; bucket still applies |
duration |
600 | Server timeline length in seconds, greater than 0 and at most 600; required for loops |
Use the actual clip duration for server loops to keep late-join phase consistent.
For non-looped sounds, supplying the actual duration also makes the server's ended
state meaningful; without it, the server cannot know the decoder's media length.
Client getState().duration is the actual decoded duration.
Spatial sounds use distance attenuation and camera-relative stereo panning. Stereo
assets are downmixed to a point source. No automatic wall occlusion, HRTF, Doppler,
vehicle attachment or environmental reverb is implied. Update the position through
SetSoundPosition when a source moves; that does not restart the sound.
Handles and playback control
| Global | Open77.audio method |
Arguments |
|---|---|---|
Play2DSound |
play2D |
asset, options? |
Play3DSound |
play3D |
asset, position, options? |
PlaySound |
play |
id β resume; restart an ended local clip |
PauseSound |
pause |
id β preserve cursor |
StopSound |
stop |
id β stop and rewind |
SeekSound |
seek |
id, seconds |
SetSoundVolume |
setVolume |
id, volume |
SetSoundPosition |
setPosition |
id, {x,y,z} β spatial only |
GetSoundState |
getState |
id |
DestroySound |
destroy |
id β release permanently |
Controls return true on success, or a failure plus reason. Local and server handles
belong to different runtimes; never pass a local handle to a server expecting it to
identify a network sound. Other resources cannot mutate a handle they do not own.
local state, why = GetSoundState(id)
if state then
print(state.status, state.position, state.duration, state.error)
end
assert(PauseSound(id))
assert(SeekSound(id, 0.2))
assert(PlaySound(id))
-- Release after use. Do not allocate a new sound every frame.
assert(DestroySound(id))
Client states: loading, playing, paused, ended, error. cached indicates
reuse of decoded PCM; error contains a stable diagnostic code, not a filesystem
path. Server state describes desired playback, not an acknowledgement that every
client has working speakers or decoded the asset.
Limits and lifecycle
- 16 handles per resource/owner and 64 overall, local and network sharing the client budget. Finished local handles remain reusable until destroyed or resource stop.
- 16 MiB encoded file, 32 MiB decoded PCM per clip, up to 600 seconds; mono/stereo PCM16 at 8β96 kHz. Windows Media Foundation must support the decoder.
- Bounded decoded cache and active-voice memory budget. Decode failures are visible; invalid audio cannot hold a Lua VM or the game thread waiting.
- Resource stop, disconnect and game-world teardown release owned playback. Server non-looping sounds are collected 30 seconds after their configured duration ends.
Compatibility limits
Output-device recovery is not automatic after an audio output failure. Decoder and device support depends on the installed Windows media components.
// For agents & LLMsthis page as Markdownllms.txtllms-full.txt
