timbre
One audio engine for React Native — the same libmpv core on iOS and Android.
A React Native audio player on libmpv, and a player-agnostic media session that keeps the lock screen, notification and background playback alive after the UI is gone. Four packages, no cross-dependencies.
npm install @afkcodes/timbre-player @afkcodes/timbre-audio-session @afkcodes/timbre-media-session react-native-nitro-modules
import { Player } from '@afkcodes/timbre-player'
// One mpv core. No singleton —
// create as many as you need.
const player = await Player.create({
replayGain: { mode: 'album', fallback: -6 },
})
await player.load('https://ex.com/01.flac')
player.play()
// Position is an anchor, not a stream:
// every surface projects it locally,
// with zero bridge traffic.
player.onStateChange((s) => {
const { value, rate } = s.positionAnchorMs
paint(value, rate, s.playing)
})
declare function paint(
ms: number,
rate: number,
playing: boolean
): void
How it compares
| Feature | track-player | expo-audio | rn-video | timbre |
|---|---|---|---|---|
| Engine | ExoPlayer / AVPlayer | ExoPlayer / AVPlayer | ExoPlayer / AVPlayer | libmpv + FFmpeg — our own build |
| One identical engine, both platforms | no | no | no | yes |
| Multiple players | no | yes | yes | yes — one core each |
| Session layer works with any player | no | no | no | yes |
| Gapless queue | partial | yes | no | yes — 25 ms handover |
| Signed / expiring URLs stay gapless | no | no | no | yes |
| EQ / DSP | no | no | no | 16 filters, 22 presets |
| Casting (Chromecast) | no | no | app-side | yes — both platforms |
Every cell is sourced from each project’s own docs. The full table, and where each competitor cell came from, is on the docs.