AudioSession
Declare what this page's audio is for, so the operating system ducks, mixes or interrupts it correctly - and find out when something else takes the audio away.
@inject Bit.Butil.AudioSession audioSessionMDN reference
Ambient for effects,
Playback for content the user came for, TransientSolo for something
that must be heard alone.
IsSupported is false and every call is a no-op.
True when the runtime exposes navigator.audioSession. During prerender/SSR the check returns false rather than throwing.
@inject Bit.Butil.AudioSession audioSession
var supported = await audioSession.IsSupported();Set it before playback starts - changing it mid-playback can make the OS re-evaluate the routing audibly. Playback interrupts other audio and survives the ringer switch; Ambient mixes with other audio and obeys it; Transient ducks other audio briefly; TransientSolo pauses it.
// game sound effects: never silence the user's music
await audioSession.SetSessionType(AudioSessionType.Ambient);
// a video the user chose to watch
await audioSession.SetSessionType(AudioSessionType.Playback);The case worth handling is Interrupted going back to Active: a phone call or another app took the audio, playback stopped, and the browser will not resume it for you. Fires once immediately with the current state. Dispose the returned subscription.
_sub = await audioSession.OnStateChange(state =>
{
if (state == AudioSessionState.Interrupted) PauseUi();
if (state == AudioSessionState.Active && _wasInterrupted) Resume();
InvokeAsync(StateHasChanged);
});API reference
ValueTask<bool> IsSupported()ValueTask<AudioSessionType> GetSessionType()ValueTask<bool> SetSessionType(AudioSessionType type)ValueTask<AudioSessionState> GetState()Task<ButilSubscription> OnStateChange(Action<AudioSessionState> handler)ValueTask DisposeAsync()