Safari background audio
Select which package(s) are affected
@livekit/components-react
Describe the bug
Hi LiveKit team đź‘‹
I’ve run into an issue specific to Safari (both desktop and iOS) when rendering each participant’s audio track in a separate
When the LiveKit room is active in the background (e.g. the tab is not focused), new participants’ audio tracks are not audible until the user returns to the tab. Existing audio tracks continue to play fine, but any new audio elements created while the page is in the background remain muted/suspended by Safari’s autoplay policy.
This happens even if:
- The user has already interacted with the page (e.g. clicked “Join room”).
- The existing audio elements are already playing successfully.
- Each new
Reproduction
const tracks = useTracks([
Track.Source.Microphone,
Track.Source.ScreenShareAudio,
Track.Source.Unknown,
]).filter((ref) => !isLocal(ref.participant) && ref.publication.kind === Track.Kind.Audio);
return (
<div style={{ display: 'none' }}>
{tracks.map((trackRef) => (
<AudioTrack
key={getTrackReferenceId(trackRef)}
trackRef={trackRef}
volume={volume}
muted={muted}
/>
))}
</div>
);
Logs
System Info
LiveKit packages:
- @livekit/components-react: 2.9.15
- @livekit/components-styles: 1.1.6
- livekit-client: 2.15.14
Safari version: 17.x
Platform: macOS
Severity
serious, but I can work around it
Additional Information
Is there any recommended workaround or LiveKit-specific API pattern to handle this Safari behavior? For example:
- Using a single shared
- Combining remote tracks into one MediaStream?
- Managing playback through AudioContext instead of
Any suggestions or best practices for reliable background audio playback in Safari would be greatly appreciated 🙏
Thanks for the report!
We are (painfully) aware of this issue, but haven't found a suitable workaround so far.
- Combining all audio tracks in the same audio element would render any volume controls that we have useless.
- Piping audio through AudioContext creates its own set of auto play issues on Safari unfortunately.
As @lukasIO mentioned, the code already accounts for that Safari bug, and it’s something quite difficult to fix at the framework level: https://github.com/livekit/client-sdk-js/blob/main/src/room/track/Track.ts#L402
At the application layer, you could listen for any captured errors related to NotAllowedError. If that happens, you may require the user to interact (e.g. a button, a blurred overlay?) and then trigger a re-render of the component.
It’s not the ideal solution, but it might still work for you.