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.

Preloading

At scene load the player loads every file named by an audio_source component, then preloads every audio file it finds under audio/ (searching the scene directory, then its parent, the game root) so that play_sound("name") from a script never stalls on decode. A scene with a large stem library pays for that in load time. Opt out per scene:

[scene]
name = "Silent Corridor"
preload_audio = false

With preload_audio = false only audio_source files load, and music sessions resolve their stems through their own path. The default is true (ADR 0066, scene audio preload opt-out).

Mixer Buses

Every ordinary sound routes through one of two buses — music and sfx — both children of Kira’s main track. (A running music session adds its own six-bus stem mixer on the same device; that mixer is owned by flint-music, not by audio_source, and is described on its own page.)

main  ──┬── music     (audio_source.bus = "music")
        └── sfx       (everything else, including all one-shots)

Opt a source into the music bus in the scene:

[entities.score.audio_source]
file = "audio/theme_night.ogg"
bus = "music"
loop = true
autoplay = true

Initial gains come from the CLI:

flint play scene.toml --music-volume 0.8 --sfx-volume 1.0
flint play scene.toml --music-volume 0     # mute the score, keep the world

Bus gain multiplies underneath per-sound volume. A script crossfading between two music beds keeps working at any bus gain, and a player who has turned the music down does not have their fades overridden.

The master low-pass applies to both buses, so set_audio_lowpass muffles the entire mix — score included — which is what you want when the effect is “something is between you and the world” rather than “the sfx got quieter”.

One-shots (play_sound, play_sound_at) are always sfx.

Music Sessions

A scene that carries a music_session component turns the audio engine into the host for a rhythm-driven chart: flint-music opens a ChartSession on the same Kira manager the ordinary buses use (ADR 0017, shared audio manager), so the world’s sounds and the suite’s stems mix together and one master low-pass muffles both. For the length of the session the gamepad is handed to a 1 kHz capture thread that stamps every stick and button event with the audio clock (ADR 0018, gamepad handoff).

While a session is active, only the lean stick and the pulse button reach InputState. Every other gamepad control is consumed by the capture thread; keyboard input still flows through winit as normal. Scripts that need the pad for anything else during a chart should read the conducted parameters instead.

The component, its configuration files, the ladder and reintegration mechanics, and the seven flint subcommands that go with it are on the Music Sessions page.

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
busstring"sfx"Mixer bus: "sfx" or "music"

Looping component sources must keep autoplay = true. Components have no play/stop API — scripts can only fade their volume. If you need a sound that starts on cue, use a one-shot instead.

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 through a mixer bus for non-positional 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