kira: an expressive game audio library for Rust
Library for expressive game audio.
At a glance
- What is it?
- kira is a backend-agnostic Rust library for game audio, providing tweens for smoothly adjusting sound properties, a track mixer with effects, a clock system for precisely timing audio events and spatial audio support. It is dual licensed Apache-2.0 or MIT, the current release is v0.12.5 from 2026-09-26, and it is aimed mainly at desktop platforms.
- Who is it for?
- kira fits a Rust game developer who wants sample-accurate musical timing, tweened parameter changes and a mixer with effects without writing an audio engine, and it fits a team that wants the audio backend to be swappable. It does not fit a web game that streams its audio or loads sounds from files in a wasm build, since both are documented limitations, and it does not fit a project that has already committed to another Rust audio stack.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 27 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 21, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Four capabilities, and the design choice behind them
kira is a backend-agnostic library to create expressive audio for games, and the four things it provides are worth reading as a set rather than a list.
It provides tweens for smoothly adjusting properties of sounds, a flexible mixer for applying effects to audio, a clock system for precisely timing audio events, and spatial audio support.
Each of those addresses something a game audio engine typically does badly. Tweens are the difference between a sound that starts and a sound that ramps, and a game that fades a track in over two seconds as a scene changes needs exactly that. The mixer is the difference between playing a sound and playing a filtered, spatialised version of it. The clock is the difference between a sound that plays when you trigger it and a sound that plays on the beat. And spatial audio is the difference between a footstep that is in front of the camera and one that is behind you.
The backend-agnostic part is a deliberate abstraction. The `AudioManager` is generic over a backend type, `AudioManager::<DefaultBackend>::new(...)` in the examples, so the same game code runs on whatever the default backend is and can be pointed at a different one. The manager is the thing that plays sounds and manages resources, and the examples all follow the same shape: create a manager, load sound data, play it.
The library is published on crates.io as `kira` with documentation on docs.rs, is dual licensed under Apache-2.0 or MIT at your option, and the contribution terms state that any contribution is dual licensed the same way unless you explicitly say otherwise. The release history is a steady 0.x line, v0.12.5 on 2026-09-26, v0.12.4 on 2026-08-27 and v0.12.3 on 2026-08-09.
Playing a sound twice needs no extra memory, and that is a design choice
The first example in the documentation is the simplest case, and it contains a detail that is easy to miss.
let mut manager = AudioManager::<DefaultBackend>::new(AudioManagerSettings::default())?;
let sound_data = StaticSoundData::from_file("sound.ogg")?;
manager.play(sound_data.clone())?;
// After a couple seconds...
manager.play(sound_data.clone())?;
// Cloning the sound data will not use any extra memory.The comment at the end is the point. The sound data is cloned twice, and the clones share the underlying samples, so a game that fires the same gunshot or footstep sound forty times a second does not allocate forty copies of the audio.
That matters more in a game than in ordinary software, because the sounds a game plays most often are exactly the ones it plays most often, and a naive engine either copies the buffer per playback or decodes the file per playback. Kira separates the sample data from the play handle, and the play handle is what `manager.play` returns.
The type is `StaticSoundData`, and the `static_sound` module path tells you there are other kinds. A static sound is one whose samples are known up front, which is the right model for a sound effect loaded from a file. The other kind, implied by the name and by the platform notes, is a streaming sound, where the samples arrive over time rather than being in memory.
The `?` on each call is the error handling, and the `AudioManagerSettings::default()` is where you would configure sample rate, buffer size and the command queue depth, and those settings are the main thing you will be tuning when you adopt the library.
Tweens and effects are how a sound becomes expressive
The second and third examples are the ones that show what expressive means in practice.
Gradually speeding up a sound over time:
let mut sound = manager.play(sound_data)?;
// Start smoothly adjusting the playback rate parameter.
sound.set_playback_rate(
2.0,
Tween {
duration: Duration::from_secs(3),
..Default::default()
},
);The play handle is mutable and the change is described by a `Tween` with a duration, so the playback rate moves to 2.0 over three seconds rather than jumping. The `..Default::default()` is the tween's other fields, and the fact that they have defaults is what makes a tween a one-liner rather than a struct literal every time.
The third example is the mixer:
let track = manager.add_sub_track({
let mut builder = TrackBuilder::new();
builder.add_effect(FilterBuilder::new().cutoff(1000.0));
builder
})?;
let sound_data = StaticSoundData::from_file("sound.ogg")?.output_destination(&track);
manager.play(sound_data)?;A sub-track is created from a builder, an effect is added to it, and the sound's `output_destination` points it at that track instead of the default output. The comment notes that the filter makes the audio sound muffled, which is the low-pass filter at a 1000 hertz cutoff doing the obvious thing.
So the mixer is a routing graph. You create a track, give it effects, and send sounds into it. A game that wants one global reverb for interior scenes and a different reverb for the menu creates two tracks and routes each sound to the right one, and the alternative is hard-coding a reverb into every sound file.
Together, tweens and effects are what turn a library of one-shot samples into something that responds to the game state, which is the difference between an asset player and an audio engine.
Clocks are what make music timing work
The fourth example is the one that justifies the library for a music-driven game, and it is the least obvious of the four.
const TEMPO: f64 = 120.0;
let mut clock = manager.add_clock(ClockSpeed::TicksPerMinute(TEMPO))?;
let sound_data_1 = StaticSoundData::from_file("sound1.ogg")?
.start_time(clock.time() + 2);
manager.play(sound_data_1)?;
let sound_data_2 = StaticSoundData::from_file("sound2.ogg")?
.start_time(clock.time() + 4);
manager.play(sound_data_2)?;
clock.start()?;The clock ticks 120 times per minute, which in this case means each tick is one musical beat, and a tick can be used to represent any arbitrary amount of time. The first sound is scheduled two ticks from now, the second four ticks from now, and then the clock is started.
That ordering is the design. Nothing is scheduled at an absolute time; the sounds are scheduled at offsets from the clock's current time, and starting the clock is what begins the sequence. That means the timing is defined in beats, so the music and the sound effects stay together no matter what the frame rate does or how long the game takes to start.
It also solves the problem that naive scheduling gets wrong. If you schedule a sound by asking the audio thread for a timestamp, every scheduling decision has to account for the latency of the command queue and the buffer, and the errors accumulate. A clock gives a common reference that all the sounds are scheduled against, so they land together.
The `ClockSpeed` type is the abstraction that makes it work for more than music. The clock is started explicitly, which means it can be started at a musically meaningful moment rather than when the game booted, and a game that needs to pause the soundtrack or to re-sync after a loading screen has a handle to do it with.
A workspace of examples, and what is still missing
The repository is a Cargo workspace, and the members list is a catalogue of what the library does.
[workspace]
members = [
"crates/kira",
"crates/benchmarks",
"crates/examples/dynamic-music",
"crates/examples/ghost-noise",
"crates/examples/metronome",
"crates/examples/score-counter",
"crates/examples/seamless-loop-with-intro",
"crates/examples/simple-sound-playback",
"crates/examples/spatial-audio",
]
resolver = "2"
package.edition = "2024"The examples are named after the problems they solve, and that list is more informative than the feature list. Dynamic music is the case where the soundtrack changes with the game state, and a separate example for a seamless loop with an intro covers the case where a track has a lead-in that must not restart when it loops. A metronome and a score counter are the two halves of a rhythm game. Ghost noise and spatial audio cover the positioning cases, and simple sound playback is the baseline.
There is a `crates/benchmarks` member as well, so the performance of the library is measured rather than assumed, and the resolver is set to version 2 with the 2024 edition.
The top-level listing also has a `todo.md` and a `changelog.md`, alongside `Cargo.toml`, `rustfmt.toml`, a `.prettierrc.json` and the two licence files.
The roadmap in the readme is short and honest. The features the author would like to have are a C API, and spatial audio features, specifically a Doppler effect. The Doppler effect is an interesting omission to notice, because spatial audio without Doppler is unusual for a library that otherwise goes deep on realism, and its absence suggests spatial audio is positioned as panning and attenuation rather than as a full acoustic simulation.
Where kira is the wrong choice
The first case is a web game that streams its audio. The wasm limitations are stated explicitly: static sounds cannot be loaded from files, and streaming sounds are not supported because they make heavy use of threads. A browser game with a long music track that must start on demand rather than after a full download is outside what the wasm path can do, and the project says it would like help improving wasm support.
The second case is a project that needs a specific platform feature the library does not expose. The roadmap lists a C API as something the author would like, which tells you there is no stable C interface today, so a game engine or audio tool written in C cannot call kira directly.
The third case is a mobile game. Kira is mainly meant for desktop platforms, and while most testing has occurred on Windows and it has been used successfully on Mac and Linux, mobile is not the target. The AudioManager is generic over a backend, so a mobile backend is theoretically possible, but the platform notes do not claim it.
The fourth is a team that has already chosen an audio stack in Rust. There are other options in the ecosystem, and kira's pitch is expressive game audio with tweens, mixers and clocks, which is a specific set of opinions. If your game is a linear audio player with no musical timing, those opinions are overhead.
One more practical point on the versioning. The library is at 0.12.x, so the version number is still pre-1.0 and the API can change between minor versions. The dual licence is a genuine advantage for a game, because a commercial game shipped on Steam or a console has a distribution agreement that many open source licences conflict with, and Apache-2.0 or MIT at your option removes that problem entirely. The contribution terms mirror that, dual licensing any contribution unless a contributor says otherwise, so the project does not end up with a mixed-licence codebase.
Editorial conclusion
kira fits a Rust game developer who wants sample-accurate musical timing, tweened parameter changes and a mixer with effects without writing an audio engine, and it fits a team that wants the audio backend to be swappable. It does not fit a web game that streams its audio or loads sounds from files in a wasm build, since both are documented limitations, and it does not fit a project that has already committed to another Rust audio stack. Before you adopt it, read the platform support section, because most testing has been on Windows, and check whether your game needs streaming, which the wasm path does not support.
Frequently asked questions
What is the kira crate for?
It is a backend-agnostic Rust library for expressive game audio, providing tweens for smoothly adjusting sound properties, a flexible mixer for applying effects, a clock system for precisely timing audio events, and spatial audio support.
How do I schedule game sounds to a musical beat with kira?
Create a clock with `manager.add_clock(ClockSpeed::TicksPerMinute(tempo))`, schedule each sound with `.start_time(clock.time() + offset)` so the offsets are in ticks, and then call `clock.start()` to begin the sequence. The `crates/examples/metronome` and `crates/examples/dynamic-music` examples show the pattern.
Does kira support WebAssembly?
Yes, with limitations. Static sounds cannot be loaded from files and streaming sounds are not supported because they make heavy use of threads. The project says it would like help improving wasm support.
What licence is kira under?
Dual licensed, either the Apache License 2.0 or the MIT license at your option, with the files in the repository. Contributions are dual licensed the same way unless the contributor explicitly states otherwise.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/tesselode-kira)Was this useful?