Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Audio

Flint’s audio system provides spatial 3D sound via the flint-audio crate, built on Kira 0.11. Sounds can be positioned in 3D space with distance attenuation, played as ambient loops, or triggered by game events like collisions.

Spatial Audio

Spatial sounds are attached to entities via the audio_source component. The sound’s volume attenuates with distance from the listener (the player camera):

  • min_distance — full volume within this radius
  • max_distance — silence beyond this radius
  • Volume falls off smoothly between the two

The listener position and orientation are updated each frame to match the first-person camera, so sounds pan and attenuate as you move through the scene.

Ambient Loops

Non-spatial sounds play on the main audio track at constant volume regardless of listener position. Set spatial = false on an audio_source to use this mode — useful for background music, ambient atmosphere, and UI sounds.

Audio Schemas

Three component schemas define audio behavior:

audio_source (audio_source.toml) — a sound attached to an entity:

FieldTypeDefaultDescription
filestringPath to audio file (relative to scene directory)
volumef321.0Playback volume (0.0–2.0)
pitchf321.0Playback speed/pitch (0.1–4.0)
loopboolfalseLoop the sound continuously
spatialbooltrue3D positioned (uses entity transform)
min_distancef321.0Distance at full volume
max_distancef3225.0Distance at silence
autoplaybooltrueStart playing on scene load

audio_listener (audio_listener.toml) — marks which entity receives audio:

FieldTypeDefaultDescription
activebooltrueWhether this listener is active

audio_trigger (audio_trigger.toml) — event-driven sounds:

FieldTypeDefaultDescription
on_collisionstringSound to play on collision start
on_interactstringSound to play on player interaction
on_enterstringSound when entering a trigger volume
on_exitstringSound when exiting a trigger volume

Dynamic Parameter Sync

Audio source parameters (volume and pitch) can be changed at runtime via set_field() and the engine automatically syncs changes to the playing audio each frame. This enables dynamic audio effects like engine RPM simulation or distance-based volume curves:

#![allow(unused)]
fn main() {
// Adjust engine sound pitch based on speed
let rpm_ratio = speed / max_speed;
set_field(engine_sound, "audio_source", "pitch", 0.5 + rpm_ratio * 1.5);
set_field(engine_sound, "audio_source", "volume", 0.3 + rpm_ratio * 0.7);
}

Changes are applied with a 16ms tween for smooth transitions (no clicks or pops).

Scene Transition Behavior

When a scene transition occurs (via load_scene() or reload_scene()), all playing sounds are explicitly stopped with a short fade-out before the old scene is unloaded. This prevents audio bleed between scenes — sounds from the previous scene won’t continue playing into the new one.

Architecture

The audio system has three main components:

  • AudioEngine — wraps Kira’s AudioManager, handles sound file loading, listener positioning, and spatial track creation. Sounds route through spatial tracks for 3D positioning or the main track for ambient playback.
  • AudioSync — bridges TOML audio_source components to Kira spatial tracks. Discovers new audio entities each frame and updates spatial positions from entity transforms.
  • AudioTrigger — maps game events (collisions, interactions) to AudioCommands that play sounds at specific positions.

The system implements the RuntimeSystem trait, ticking in the update() phase of the game loop (not fixed_update(), since audio doesn’t need fixed-timestep processing).

Graceful Degradation

AudioManager::new() can fail on headless machines or CI environments without an audio device. The engine wraps the manager in Option and silently skips audio operations when unavailable. This means scenes with audio components work correctly in all environments — you just won’t hear anything.

Adding Audio to a Scene

# A crackling fire with spatial falloff
[entities.fireplace]
archetype = "furniture"

[entities.fireplace.transform]
position = [5.0, 0.5, 3.0]

[entities.fireplace.audio_source]
file = "audio/fire_crackle.ogg"
volume = 0.8
loop = true
spatial = true
min_distance = 1.0
max_distance = 15.0

# Background tavern ambience (non-spatial)
[entities.ambience]

[entities.ambience.audio_source]
file = "audio/tavern_ambient.ogg"
volume = 0.3
loop = true
spatial = false

Supported audio formats: OGG, WAV, MP3, FLAC (via Kira’s symphonia backend).

Scripting Integration

Audio can be controlled from Rhai scripts using deferred commands. The script API produces ScriptCommand values that the player processes after the script update phase:

FunctionDescription
play_sound(name)Play a non-spatial sound at default volume
play_sound(name, volume)Play a non-spatial sound at the given volume (0.0–1.0)
play_sound_at(name, x, y, z, volume)Play a spatial sound at a 3D position
stop_sound(name)Stop a playing sound
#![allow(unused)]
fn main() {
// In a Rhai script:
fn on_interact() {
    play_sound("door_open");                          // Non-spatial
    play_sound_at("glass_clink", 5.0, 1.0, 3.0, 0.8); // Spatial at position
}
}

Sound names match files in the audio/ directory. All .ogg, .wav, .mp3, and .flac files are automatically loaded at startup.

Further Reading

  • Scripting — full scripting API including audio functions
  • Animation — animation system that can trigger audio events
  • Physics and Runtime — the game loop and event bus that drives audio triggers
  • Schemas — component and archetype definitions