Skip to main content

Sound3D


The Sound3D object​


Sound3D is an invisible scene object that plays a WAV file with automatic volume attenuation based on the distance between the object's position and the camera. It is the recommended way to place ambient sounds, environmental audio, and zone music inside a 3D world.

Unlike ComponentSound, which plays sounds globally, Sound3D is a first-class scene object: it can be placed in the editor, saved in scene files, and created at runtime from Lua.


How it works​


Every frame, Sound3D computes the distance from the camera to its position and derives a volume using two radii:

dist ≤ innerRadius          → vol = baseVolume          (full volume)
innerRadius < dist < outerRadius → vol = baseVolume × (1 − t) (linear fade)
dist ≥ outerRadius → vol = 0 (silence, channel released)

Where t = (dist − innerRadius) / (outerRadius − innerRadius).

When the volume crosses from zero to positive an audio channel is acquired automatically. When it drops back to zero the channel is released so other sounds can use it.


Properties​


PropertyTypeDefaultDescription
sourceFilestring—Path to the WAV file (relative to the executable)
innerRadiusfloat15.0Distance within which volume is at maximum
outerRadiusfloat60.0Distance beyond which the sound is completely silent
baseVolumeint128Maximum volume (range: 0–128)
loopbooltrueWhether the sound loops indefinitely

Scene file format​


{
"name": "sound_tavern",
"type": 13,
"position": { "x": 100, "y": 0, "z": 200 },
"sourceFile": "../assets/sounds/tavern_ambient.wav",
"innerRadius": 15,
"outerRadius": 60,
"baseVolume": 100,
"loop": true
}

Creating from Lua​


Use ObjectFactory.Sound3D to create a positional sound source at runtime:

local snd = ObjectFactory.Sound3D(
"../assets/sounds/tavern_ambient.wav", -- WAV file path
Vertex3D.new(100, 0, 200), -- world position
15, -- innerRadius
60, -- outerRadius
100 -- baseVolume (0–128)
)
snd:setName("sound_tavern")
snd:setSoundLoop(true)
Brakeza:addObject3D(snd, "sound_tavern")

Lua methods​

MethodDescription
snd:setSoundLoop(bool)Enable or disable looping
snd:setInnerRadius(float)Change the full-volume radius at runtime
snd:setOuterRadius(float)Change the silence radius at runtime
snd:setBaseVolume(int)Change the maximum volume (0–128)

These methods take effect immediately on the next frame update.


Zone music / ambient crossfade​


The recommended pattern for zone-based ambient music is to place multiple Sound3D objects with overlapping outer radii. In the overlap zone both sounds play simultaneously — one fading in, the other fading out — producing a natural crossfade with no extra scripting.

-- Residential district — calm ambient
ObjectFactory.Sound3D("../assets/sounds/ambient_residential.wav",
Vertex3D.new(0, 0, 0), 30, 80, 90)

-- Market district — busy ambient
ObjectFactory.Sound3D("../assets/sounds/ambient_market.wav",
Vertex3D.new(150, 0, 0), 30, 80, 100)

-- Port district
ObjectFactory.Sound3D("../assets/sounds/ambient_port.wav",
Vertex3D.new(300, 0, 0), 40, 100, 85)
Residential       Market            Port
|←inner→| |←inner→| |←inner→|
|←——outer——→| |←——outer——→| |←——outer——→|
↑ crossfade ↑ crossfade

Global Ambience Volume​


Sound3D::ambienceVolumeScale is a C++-side static float (default 1.0) that acts as a global multiplier applied on top of every Sound3D's distance-computed volume before it is passed to the audio engine. It lets you fade all positional audio simultaneously — for example to duck world sounds during a cutscene or a menu overlay — without touching individual baseVolume values.

How the volume is computed​

distance_vol = baseVolume × (1 − t)    -- linear fade from inner to outer radius
final_vol = distance_vol × ambienceVolumeScale

Where t = (dist − innerRadius) / (outerRadius − innerRadius) for sources between the two radii, t = 0 inside innerRadius, and the source is silenced beyond outerRadius.

Controlling it from Lua​

Use Components:Sound():setAmbienceVolume(percent) where percent is in the range 0–100:

-- Silence all Sound3D sources (e.g. entering a menu)
Components:Sound():setAmbienceVolume(0)

-- Restore to full volume
Components:Sound():setAmbienceVolume(100)

-- Gentle duck to 40 % while a cinematic plays
Components:Sound():setAmbienceVolume(40)

The method converts the percentage to a 0.0–1.0 scale internally (ambienceVolumeScale = percent / 100.0).

note

setAmbienceVolume only affects Sound3D positional sources. Music played via ComponentSound:PlayMusic() and flat sound effects played via ComponentSound:PlaySound() are not affected.


Background music (non-positional)​


To use Sound3D as a global background music track that plays at full volume regardless of camera position, set innerRadius to a very large value:

local bgm = ObjectFactory.Sound3D(
"../assets/sounds/bgm_main_theme.wav",
Vertex3D.new(0, 0, 0),
99999, -- inner: always at full volume
100000, -- outer: never silenced
100
)
bgm:setName("bgm")
bgm:setSoundLoop(true)
Brakeza:addObject3D(bgm, "bgm")

Limitations​


LimitationCauseWorkaround
32 simultaneous sourcesEngine channel limitIncrease MAX_CHANNELS in ComponentSound.h
Linear attenuationVolume curve is 1 − tChange exponent in Sound3D::onUpdate() for a more natural roll-off
No occlusionThe audio engine has no geometry awarenessNot applicable for ambient music use cases

Test scene​


assets/scenes/Sound3DTest.json ships with the engine and demonstrates three Sound3D objects placed at different positions:

  • Radio Beep (left) — small inner radius (5), medium outer radius (22).
  • Day Ambient (center) — larger radii (8 / 35), lower volume.
  • Close Beep (right) — tight inner radius (3), fast fall-off to outer (15).

A HUD overlay shows the live distance and volume percentage for each source as you walk toward them with WASD.