Bevy: audio with Kira¶
Written 2026-10-27 from the project's own docs.
Fboard started with Bevy's built-in audio, then moved all sound to Kira (version 0.12), a Rust audio library with mixer tracks, effects and clocks. The trigger was adaptive music: music written as patterns whose notes must land on the beat while gameplay changes the section, intensity and tempo.
Exercised muted
The project's notes say the live Kira path (clock scheduling, layer fades, the pause filter) runs without warnings but has only been exercised with the volume at zero. Only its timing arithmetic is unit-tested. Nobody has listened to it yet.
Swapping out Bevy's audio¶
- Bevy's
AudioPluginis disabled when addingDefaultPlugins, and the game's own audio plugin takes its place. - At startup it opens a Kira
AudioManagerand stores it as a non-send resource. If no output device opens, it logs a warning and the game runs silently. - Kira is pulled in with default features off and only its
cpalbackend enabled. - The
bevycrate still compiles its own backend (rodio) by default. The project notes this could be trimmed later; it hasn't been.
How the mix is organized¶
- A sound-effects track carries one-shot effects and the rolling and grinding loops. All of these are synthesized in code at startup; there are no shipped audio files yet.
- A music track carries the score, with a low-pass filter used on pause.
- Each music layer gets its own sub-track, so raising or lowering intensity fades whole layers smoothly.
- Master volume is applied once, on Kira's main track. An earlier version applied it twice (Bevy's global volume plus a manual multiply), which made music and loops quieter than intended below full volume.
Beat-accurate scheduling¶
The engine-side problem: Bevy's Update runs once per frame, and frame times wander. If music notes were started from Update, they would land up to a frame late and jitter. Fboard's approach:
- A Kira clock runs at 3,840 ticks per bar. When the tempo lifts with intensity, the clock's speed is changed with a short tween.
- Each frame, the music system asks the pattern engine for every note in the next ~120 ms and hands them to Kira with a start time expressed in clock ticks (plus a fraction of a tick). Kira starts them exactly on time, whatever the frame rate.
- Kira's internal buffer is set to 64 frames, about 1.5 ms of timing resolution at 44.1 kHz.
- A unit test checks that musical times map onto clock ticks exactly.
The music engine itself, fboard_music, has no engine and no audio device; it only produces timed voices. The game schedules them on Kira, and an offline renderer mixes the same voices into WAV for video captures and tests. Captures therefore hear the same music as the game, with one difference the notes record: the offline mix applies layer volume per note at trigger time, so its fades are coarser than live.
Connecting gameplay to sound¶
Gameplay doesn't call the audio code directly. It sends PlaySfx messages, and:
- the audio plugin plays the effect;
- some effects also fire a musical stinger (bail, goal, banked combo, gap);
- the video-capture plugin reads the same messages to build its offline soundtrack.
Separately, each frame the music conductor reads a small struct of gameplay inputs (scene, speed, air or grind, combo multiplier, time left) to choose the section and intensity. See Fboard: adaptive music for the musical side, and Music as code for the broader approach.