Server Exports¶
Call these from any server script.
play3d¶
Plays a sound at a world position for one player or for everyone, and registers it so it can be re-sent to players who enter its routing bucket later.
local soundId = exports['3d-audio']:play3d(
target, soundFile, x, y, z, range, volume, loop,
loopDurationSeconds, dimension, entityNetId
)
Parameters¶
| # | Name | Type | Default | Description |
|---|---|---|---|---|
| 1 | target |
number | required | Player server-id to play for, or -1 for all clients. |
| 2 | soundFile |
string | required | Path relative to soundPath (assets/sounds by default), e.g. alarm/vehiclealarm.mp3. Forward slashes. Not validated — a typo only produces a warning in the player's NUI console. |
| 3 | x |
number | required | World X. |
| 4 | y |
number | required | World Y. |
| 5 | z |
number | required | World Z. |
| 6 | range |
number | defaults.range |
Audible radius in metres. Volume falls to zero at exactly this distance (with the default linear distance model). Clamped to limits.maxRange. |
| 7 | volume |
number | defaults.volume |
0.0–1.0. Clamped to limits.maxVolume. |
| 8 | loop |
boolean | defaults.loop |
Whether to loop. |
| 9 | loopDurationSeconds |
number | 0 |
With loop = true, auto-stop after this many seconds. 0 means loop forever — see the warning below. Ignored when loop is false. |
| 10 | dimension |
number | defaults.dimension (-1) |
Routing bucket filter. -1 = audible in every bucket. |
| 11 | entityNetId |
number | 0 |
If > 0, the sound follows this entity instead of staying at x, y, z. See Entity-attached sounds. |
Returns¶
number — a positive sound ID, from a counter that starts at 1 and increments
for the lifetime of the resource. Keep it if you ever want to stop the sound.
Examples¶
Endless loops must be stopped explicitly
With loop = true and loopDurationSeconds = 0 there is nothing that will
ever clean the sound up. The browser never reports the end of a looping
sound (it would fire on every iteration), and no timer is armed, so the
server's registry keeps the entry forever — and keeps re-sending it to
players who change routing buckets.
Always pair an endless loop with a stop call you are certain will run,
including on error paths and when the owning entity is deleted. See
Known Limitations.
Entity-attached sounds¶
Pass a network ID in entityNetId and the sound follows that entity:
local netId = NetworkGetNetworkIdFromEntity(vehicle)
local soundId = exports['3d-audio']:play3d(-1, 'alarm/vehiclealarm.mp3',
0.0, 0.0, 0.0, -- (1)
150.0, 0.6, true, 180, -1, netId)
- The
x, y, zarguments are ignored whenentityNetId > 0— the position comes from the entity each tick. Pass the entity's current coords anyway if you prefer; it makes no difference.
Each client then tracks the entity itself: the sound's position updates at
tickIntervalMs, pauses when the entity leaves that client's streaming range,
and resumes when it comes back. A client that never streams the entity never
hears the sound at all.
Note
If the entity is permanently deleted, the sound stops on every client but the
server's registry entry stays. Call stop when you delete the entity.
Routing buckets¶
With dimension set to a bucket number, only players currently in that bucket
hear the sound. On top of plain filtering, the server re-sends such a sound to
any player who later moves into that bucket, so arriving late still means hearing
the alarm that is already ringing.
This requires your framework to call
setDimension on the client when the bucket changes —
FiveM has no client native for reading it. Without that call, bucket-scoped sounds
are silently dropped.
dimension = -1 (the default) needs no integration at all.
stop¶
Stops a sound and removes it from the server's registry.
Parameters¶
| # | Name | Type | Description |
|---|---|---|---|
| 1 | target |
number | Player server-id, or -1 for all clients. Use the same value you passed to play3d. |
| 2 | soundId |
number | The ID play3d returned. |
Returns¶
Nothing.
Notes¶
- Safe to call for an ID that already finished or never existed — it is a no-op.
- Also cancels a pending
loopDurationSecondstimer, so a timed loop stopped early does not fire its auto-stop later. - Two arguments, unlike the client's one-argument
stop. See the API overview.