audiostreamingsounds8 min read · updated 9/19/2026

Native audio in FiveM: AWC wave packs, dat54 sound data and custom soundsets

Most custom sounds on FiveM servers play through NUI: an HTML audio element in a browser layer, with volume faked from distance. It works, but it is not the game's audio engine. It ignores reverb in tunnels, does not duck under explosions, cannot attach to a moving vehicle properly and is heard at full clarity through walls. Native audio uses the real engine: your sound is packed into the same container format Rockstar uses and played with the same natives as a vanilla siren or door buzzer. It takes more setup, and this guide walks through every piece of it.

Short answer: Native audio needs two things streamed from a resource: an .awc wave pack holding the audio, and a .dat54.rel sound-data file that defines named sounds and a soundset pointing at that wave pack. Declare them with data_file 'AUDIO_WAVEPACK' (the folder) and data_file 'AUDIO_SOUNDDATA' (the path without 54.rel), load the bank in a script with RequestScriptAudioBank('folder/bank', false), then play sounds with PlaySoundFromEntity, PlaySoundFromCoord or PlaySoundFrontend using the sound name and soundset name.

FiveM Native Audio: AWC Banks and Custom Soundsets

#Native audio versus NUI audio

NUI audioNative audio
SetupDrop an .ogg in the resourceBuild .awc and .dat54.rel files
PositionalFaked with distance mathsReal 3D, attached to entities
EnvironmentNoneReverb, occlusion, game volume sliders
Follows a moving carOnly with constant updatesYes, when played from the entity
Best forUI sounds, music, simple effectsSirens, vehicle and weapon sounds, world effects

If you only need a phone ringtone or a notification ping, NUI audio is simpler and entirely adequate; the basics are in custom sounds in FiveM. Native audio pays off when the sound belongs in the world: a siren that fades behind buildings, a door alarm that sounds like it is coming from the door, an engine note.

#The files involved

FileWhat it isDeclared as
mybank.awcAudio Wave Container holding one or more wavesInside the AUDIO_WAVEPACK folder
mysounds.dat54.relSound data: sounds, soundsets, references to wavesAUDIO_SOUNDDATA
mygame.dat151.relGame data: vehicle, radio and other game objectsAUDIO_GAMEDATA, only when needed

A simple custom sound only needs the first two. Game data (dat151) is used for vehicle engine sounds, radio stations and similar game objects, and is not needed for sounds a script triggers by name.

Inside the sound data, two kinds of entries do the work for scripted sounds. A simple sound points at one wave inside a container, identified by the container name (the wave-pack folder and .awc name) and the wave name. A soundset maps script-facing names to those sounds. Scripts refer to the soundset name and the sound name inside it, never to the wave directly. More elaborate sound types exist for loops, randomised variations and crossfades, but a simple sound per wave covers alarms, sirens and one-shot effects.

#Building the audio

  1. Prepare mono, 16-bit WAV files. 32 kHz or 48 kHz sample rates are the safe choices; 48 kHz is expected for radio content.
  2. Trim silence and normalise loudness. Native sounds are mixed with the game, so a file mastered as loud as possible will clip next to gunfire.
  3. Pack the waves into an .awc. CodeWalker can import and export .awc via XML, and community tools such as native-audio-tool and AWCMaster automate the conversion and generate the matching sound data.
  4. Create the .dat54.rel with a simple sound per wave and a soundset listing them. Generating it with a tool avoids hand-editing hashes.
  5. Choose PCM for quality or ADPCM for roughly a quarter of the size. ADPCM is fine for most effects.

#The resource layout and manifest

resource layout
my_audio/
├─ fxmanifest.lua
├─ client.lua
├─ audiodirectory/
│  └─ my_sounds.awc
└─ data/
   └─ my_sounds.dat54.rel
fxmanifest.lua
fx_version 'cerulean'
game 'gta5'

client_script 'client.lua'

files {
    'audiodirectory/my_sounds.awc',
    'data/my_sounds.dat54.rel',
}

data_file 'AUDIO_WAVEPACK' 'audiodirectory'
data_file 'AUDIO_SOUNDDATA' 'data/my_sounds.dat'

Two details trip almost everyone. AUDIO_WAVEPACK points at the folder, not the .awc file. AUDIO_SOUNDDATA points at the sound-data file without the 54.rel part: the file on disk is my_sounds.dat54.rel, the manifest says data/my_sounds.dat. Audio data files cannot be globbed, so each needs its own data_file line, and both files must also be listed in files so clients download them.

Some resources place these files under stream/ instead; that works too, as long as the files and data_file paths match. Keeping audio out of stream/ makes it clearer which files are loaded through data-file entries.

#Loading the bank and playing sounds

client.lua
local BANK = 'audiodirectory/my_sounds'
local SOUNDSET = 'my_soundset'
local bankReady = false

CreateThread(function()
    local deadline = GetGameTimer() + 5000
    while GetGameTimer() < deadline do
        if RequestScriptAudioBank(BANK, false) then break end
        Wait(100)
    end
    bankReady = true
end)

local function playOnEntity(name, entity)
    if not bankReady then return end
    local id = GetSoundId()
    PlaySoundFromEntity(id, name, entity, SOUNDSET, false, 0)
    return id
end

local function stop(id)
    if not id then return end
    StopSound(id)
    ReleaseSoundId(id)
end

AddEventHandler('onResourceStop', function(res)
    if res == GetCurrentResourceName() then
        ReleaseNamedScriptAudioBank(BANK)
    end
end)

The bank name is the wave-pack folder and the .awc name without extension, joined with a slash. Some resources write it with a backslash, as in DLC_MYSIRENS\\SIRENPACK_ONE in Lua source; both forms refer to the same bank.

The deadline loop matters. RequestScriptAudioBank is documented to return true once the bank is loaded, but there is an open FiveM issue where it keeps returning false even though the bank loaded and sounds play. A while not RequestScriptAudioBank(...) do Wait(0) end loop can therefore spin forever. Retry for a few seconds, then carry on.

NativeUse
PlaySoundFrontend(-1, name, soundset, true)Non-positional UI sound
PlaySoundFromEntity(id, name, entity, soundset, false, 0)Attached to a ped, vehicle or object
PlaySoundFromCoord(id, name, x, y, z, soundset, false, range, false)Fixed world position with a range
GetSoundId() / ReleaseSoundId(id)Allocate and free a handle for stoppable sounds
StopSound(id), HasSoundFinished(id)Stop a looping sound, check completion

Pass -1 as the sound id for fire-and-forget sounds. Anything that loops, such as a siren or an alarm, needs a real id from GetSoundId so you can stop it, and the id must be released afterwards or you slowly run out of sound slots.

#Making other players hear it

The isNetwork flag on the play natives exists, but it relies on every client having the same bank loaded at the same time, and it gives you no control over who hears what. The dependable pattern is to let the server fan the event out and have each nearby client play the sound locally from the right entity.

server.lua
RegisterNetEvent('my_audio:alarm', function(netId)
    local src = source
    local entity = NetworkGetEntityFromNetworkId(netId)
    if entity == 0 then return end
    local origin = GetEntityCoords(entity)
    for _, id in ipairs(GetPlayers()) do
        local ped = GetPlayerPed(id)
        if ped ~= 0 and #(GetEntityCoords(ped) - origin) < 120.0 then
            TriggerClientEvent('my_audio:playAlarm', id, netId)
        end
    end
end)
client.lua
RegisterNetEvent('my_audio:playAlarm', function(netId)
    local entity = NetToEnt(netId)
    if not DoesEntityExist(entity) then return end
    playOnEntity('alarm_loop', entity)
end)

Validate on the server who may trigger a sound; an event that any client can fire at everyone within 120 metres is an invitation to abuse. Because each client plays from the entity, the engine handles distance, occlusion and Doppler on its own.

#Vehicle and siren sounds

Add-on engine sounds use the same wave-pack mechanism plus game data. A typical vehicle sound resource streams .awc files, a dat54.rel and a dat151.rel, and declares AUDIO_WAVEPACK, AUDIO_SOUNDDATA and AUDIO_GAMEDATA. The car then points at the sound through <audioNameHash> in its vehicles.meta, as described in vehicle meta files.

  • Start the sound resource before the vehicle resources that reference it.
  • Keep the original file names from the sound pack; the names are baked into the data files.
  • Test a sound on any car with ForceUseAudioGameObject(vehicle, 'soundname') before editing metas.
  • Siren resources usually expose a soundset and sound names, and your emergency-lighting script plays them with PlaySoundFromEntity.

#Troubleshooting silence

CheckHow
Files downloadedBoth listed in files, resource started, no errors in F8
Data-file pathsFolder for AUDIO_WAVEPACK, .dat without 54.rel for AUDIO_SOUNDDATA
Bank namefolder/awcname with no extension
Sound and soundset namesExactly as in the sound data; open the .rel as XML in CodeWalker to confirm
Wave formatMono 16-bit; re-export stereo files
Game volumeThe SFX slider affects native sounds, unlike NUI audio

Native audio adds download size for every player. Keep effects short, prefer ADPCM, and group related sounds into one bank rather than one bank per sound. A bank only occupies memory while it is loaded, so resources with many situational sounds, such as a heist script, can request their bank when the activity starts and release it with ReleaseNamedScriptAudioBank when it ends.

Questions

What is an .awc file in FiveM?
An Audio Wave Container, the game's own format for audio data. It is streamed through an AUDIO_WAVEPACK data file and played through soundsets defined in a .dat54.rel.
Why is AUDIO_SOUNDDATA path missing the 54.rel?
The data-file entry names the .dat path and the game appends the type suffix itself, so data/my_sounds.dat54.rel is declared as data/my_sounds.dat.
What bank name do I pass to RequestScriptAudioBank?
The wave-pack folder and the .awc name without extension, for example audiodirectory/my_sounds.
Why does RequestScriptAudioBank always return false?
There is a reported FiveM issue where it returns false even when the bank loaded. Retry with a timeout rather than looping until it returns true.
How do other players hear my native sound?
Send a server event to nearby players and have each client play the sound from the same entity.

Ready to pick a map?

Twelve themes on three base map styles, $8 each, instant download.

Relevant to what you just read