Audio
Usage guide of audio.core: buses and player mix, the sound bank, cues from events to sounds, music and ambience, the service, modding, acoustics, engines.
Game audio (audio.core) #
The sound of a game on every target: mixing buses, music by state, 2D and 3D effects, ambience areas and a table "game event to sound". Readable offline: gripforge docs audio-core, or the MCP tool gripforge_docs (topic audio-core).
What it is for #
A new game has sound without any asset: every kit brings default correspondences from its events to sounds, and audio.core ships a set of procedural base sounds (interface clicks, footsteps, jumps, hits, pickups, a vehicle engine, three music loops). The kit decides nothing of the game: it plays what the events say. To change what is heard, change the table, not the code.
It is installed by default in every new game, with ui.menu, input.remap and settings.graphics. The player's mix is the Audio tab of the Options screen (ui.menu).
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "audio.core" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "audio.core",
config: { "volumes": { "music": 50 },
"sounds": { "footstep": { "src": ["lib:abc", "lib:def"] } } } }
It requires only game.engine. Only the keys that differ from the defaults need to be written. The full reference is gripforge kit api audio.core.
Buses #
master, music, sfx (effects), voice, ambience, ui (interface). Volume goes from 0 to 100 (gain = (v/100)^2), each bus can be muted, and master applies over the others. While the game is paused, sfx, voice and ambience are silenced (music and interface go on; voiceDuringPause: true keeps dialogue audible). The player's choices are saved in the player settings, outside game saves. Loading a save stops the sounds that were playing (past events are not replayed) and restarts the current music track.
Settings and defaults #
| Key | Default | Meaning |
|---|---|---|
volumes | master 100, music 70, sfx 100, voice 100, ambience 80, ui 80 | default mix, before the player changes it |
muted | none | buses muted by default |
maxVoices | 32 | sounds at once, music and ambience included (1 to 128) |
seed | 1 | seed of the variant and variation picks |
sounds | {} | the project sound bank (adds or replaces) |
defaultCues | true | use the default event to sound correspondences of the kits |
cues | [] | the project's correspondences, after the defaults |
music | states, playlists, fade 1.5, initial exploration | music by state |
ambience | [] | looping areas mixed by the listener's position |
voiceDuringPause | false | keep dialogue audible when the world is paused |
acoustics | disabled | Rust-only room, door and occlusion model (below) |
tab | every bus, mutes on | what the Audio tab shows |
Sounds #
The bank maps a sound id (footstep, ui.click, music.combat, my_door) to a definition: src (several sources are variants, one is picked at random, never the same twice in a row), bus, volume, pitch, volumeJitter, pitchJitter (random plus or minus fraction per play), loop, spatial (3D) with minDistance, maxDistance, rolloff (inverse or linear), priority (0 to 100), limit (instances at once), duration, tags.
A source is base:<name> (procedural), lib:<id> (an audio asset of the library), slot:<id> (an asset slot), an engine path (res://… on Godot, a Resources path on Unity, /Game/… on Unreal) or a URL on the web.
Base sounds: ui.click, ui.hover, ui.confirm, ui.back, ui.error, ui.open, jump, land, footstep, hit, impact, swing, shot, reload, pickup, coin, checkpoint, success, fail, door, explosion, thunder, vehicle.engine (loop), ambience.wind, ambience.night (loops), music.menu, music.exploration, music.combat (loops). A project sound with the same id replaces one.
Correspondences (cues) #
A cue is { event, action, sound | state, when, unless, ifVar, volume, pitch, key, param, range, pitchRange, volumeRange }:
play(default) playssoundwheneventfires.whenandunlessmatch payload fields,ifVargame variables. A 3D sound is placed at the payload'sx, y, z(orposition), else at the entity named bykey(defaultentity). A looping sound plays once perkey(one engine per vehicle).stopstops the looping voice ofsoundfor thatkey.paramdrives a sound from a payload number (rangetopitchRangeorvolumeRange): an engine pitch from the speed.musicswitches the musicstate.event: "var:<path>"fires when a game variable changes (payload{ value, previous }).
{ "cues": [
{ "event": "door.opened", "sound": "my_door", "volume": 0.8 },
{ "event": "surface.footstep", "sound": "" },
{ "event": "boss.started", "action": "music", "state": "combat" }
] }
An empty sound mutes the defaults of that event. defaultCues: false turns all the kits' defaults off.
Music and ambience #
music.states maps a state to a playlist (menu, exploration, combat by default), music.playlists holds { tracks, shuffle, loop }, music.fade is the crossfade in seconds, music.initial the state when the game starts without a menu. By default the title screen plays menu, play plays exploration, encounters and bosses play combat, a cleared encounter returns to exploration.
Ambience areas are { id, sound, x, y, z, radius, fade, volume }: radius 0 means everywhere, the gain is full inside the radius and fades out over fade.
Service, events, variables #
Service audio.core: play(sound, { position, volume, pitch, key }) (a voice id, 0 when not played), playAt(sound, x, y, z), stop(voice), stopSound(sound), stopAll(), trigger(event, payload), setMusicState(state), musicState(), playPlaylist(id), nextTrack(), stopMusic(), music(), setListener(x, y, z), gainAt(sound, x, y, z), ambience(), volume(bus), setVolume(bus, v), muted(bus), setMuted(bus, on), busGain(bus), mixer(), voices(), stats(), bank(), sound(id), search(tag), cuesFor(event), reset(), reload(). The options tab is settings.audio (optionFields(lang), setOption(id, value)).
Events: audio.mix, audio.reset, audio.music. Variables: audio.volume.<bus>, audio.muted.<bus>, audio.music, audio.voices. The kit listens to every event of the bus: that is the cue table.
Hooks and modding #
A service: it takes no decision, so it declares no hook. A game or a mod changes the table. A mod may override volumes, muted, defaultCues, cues, music and ambience, all naming sounds by their id in the bank, so it remaps sounds the game already has. Not moddable: sounds (a source may be a URL or a path, the bank is the game's), maxVoices, seed, tab. The player's mix is a player setting: a mod changes the defaults, never what the player chose.
{ "schema": "gripforge.mod/1", "id": "quiet_music", "name": "Quiet music", "version": "1.0.0",
"requires": { "audio.core": "^1" }, "config": { "audio.core": { "volumes": { "music": 40 } } } }
Acoustics (Rust only, off by default) #
acoustics: { enabled, rooms, portals, obstacles } turns on a shared propagation model: non-overlapping rooms in metres, door propagation, extra occluding boxes, reverb profiles and hearing for stealth. It is provided as the capability audio.acoustics (acousticSample(query), acoustics(command)) which consumers feature-detect. Only the Rust host computes it; the web player consumes the parameters, and the older engine scripts do not implement it. A game save holds only the acoustic scene.
Engines #
| Engine | Rendering |
|---|---|
| Web | Web Audio: a gain node per bus into the master, HRTF panner for 3D voices |
| Godot | audio buses GF Music, GF Effects… under Master; stream players, 3D players for spatial sounds |
| Unity | audio sources routed to a mixer (groups Master, Music, SFX, Voice, Ambience, UI) |
| Unreal | a sound class per bus under a master class, a sound mix with class adjusters |
When it does not do what you expect #
| Symptom | Cause and fix |
|---|---|
| A sound plays nothing | play returned 0: unknown id, bus muted, maxVoices reached or limit of the sound reached |
| An event stays silent | a cue with an empty sound mutes it, or defaultCues is false |
| Same footstep twice | a single source in src: give several variants |
| Sounds stop after a load | by design: past events are not replayed; the game resynchronises its own loops |
| The 3D sound has no place | the cue payload lacks x, y, z / position and the key entity is unknown |