Skip to content
Engine

Audio and Interface

AudioService

A complete audio system on Roblox's audio API. Named sounds, a mixer with buses and ducking, speakers, playlists, adaptive layered music, states and self-healing playback.

ServiceShared

Getting it

local AudioService = Engine:GetService("AudioService")

Overview

AudioService is built on Roblox's modern audio instances (AudioPlayer, AudioFader, AudioEmitter and friends) and gives you everything around them:

  • Sound definitions: name a sound once, with variations, volume and pitch ranges, limits and cooldowns, then play it by name.
  • A mixer of buses (Master, Music, Sfx, Ambience, UI, Voice, Speaker, Stinger by default) with volumes, muting, effects, ducking, sidechain compression and snapshots of the whole mix.
  • Speakers: parts in the world that play sounds or radio-style playlists.
  • Playlists with shuffle, crossfades and loop modes.
  • Adaptive music: tracks with stacked layers that fade in by intensity, sections, beat and bar events, quantized transitions and stingers.
  • States: one call that switches music, mix and intensity together, such as "Combat" or "Menu".
  • A watchdog that notices stalled, silent or broken sounds and repairs them, so music does not just stop.

Audio is heard on the client, so playing happens there. The server can define sounds and tell clients what to play with the Broadcast... methods and SetGlobalState.

Client: sounds and music
local AudioService = Engine:GetService("AudioService")

AudioService:Define("Footstep", {
	Assets = { "rbxassetid://111", "rbxassetid://112", "rbxassetid://113" },
	VolumeRange = NumberRange.new(0.4, 0.6),
	PitchRange = NumberRange.new(0.9, 1.1),
	Limit = 6,
})

AudioService:PlayAt("Footstep", character.HumanoidRootPart)

AudioService:DefineTrack("Exploration", {
	Bpm = 96,
	Layers = {
		{ Name = "Pads", Asset = "rbxassetid://201", Lead = true },
		{ Name = "Drums", Asset = "rbxassetid://202", MinIntensity = 0.5 },
	},
})

AudioService:PlayTrack("Exploration")
AudioService:SetIntensity(0.8, 2) -- drums fade in over 2 seconds

Sound definitions

Define

AudioService:Define(id: string, def: SoundDef)

Works on both sides. Define your sounds in a shared module so the server can broadcast them by name.

FieldDescription
Asset / AssetsOne asset id, or a list to pick from at random (never the same one twice in a row). Numbers are accepted.
Volume, VolumeRangeFixed volume, or a random volume per play.
PlaybackSpeed, PitchRangeFixed speed, or a random pitch per play.
LoopedLoop by default.
BusBus to play on (default DefaultBus, or DefaultLoopBus for :Loop).
AttenuationBoundsNumberRange of studs: where positional sound starts to fall off and where it is gone.
DistanceAttenuation, AngleAttenuation, EmitterPropsFine control over the AudioEmitter curves and properties.
MaxDistanceSkip positional plays farther than this from the listener.
PriorityHigher priority sounds win when the voice budget is full.
LimitMost copies of this sound at once; the oldest is stopped to make room.
CooldownSeconds before the same sound may play again.
EssentialNever stolen to make room for other sounds.
FallbackAnother sound id to play when this sound's assets fail to load.
MethodDescription
:Undefine(id), :IsDefined(id), :GetDefinitionIds()Manage definitions.
:PreloadAsync(ids?): Promise<PreloadReport>Loads assets ahead of time (client). Resolves with { Requested, Loaded, Failed }.
:Rehabilitate(assetId?)Gives an asset that was dropped after repeated failures another chance (all assets without an argument).

Playing sounds (client)

Play / PlayAt / Loop

AudioService:Play(id: string | number, config: PlayConfig?): Voice?
AudioService:PlayAt(id: string | number, at: Vector3 | CFrame | Instance, config: PlayConfig?): Voice?
AudioService:Loop(id: string | number, at: Target?, config: PlayConfig?): Voice?

Play plays a 2D sound (UI, music stingers). PlayAt plays a positional sound at a point or on an instance. Loop plays a looping bed, positional when given a target. Passing an asset id instead of a definition id plays it with default settings. Each returns a Voice, or nil when the play was skipped (cooldown, limit, too far away, voice budget full); PlaySkipped fires with the id and the reason.

PlayConfigDescription
Volume, PlaybackSpeed, Looped, BusOverride the definition.
FadeIn, StartTimeFade in over seconds; start partway through.
Priority, EssentialVoice stealing rules.
NameName of the voice, for :Stop(name).
IgnoreCooldown, IgnoreDistanceBypass those checks.
AttenuationBounds, DistanceAttenuation, AngleAttenuation, EmitterProps, EmitterPositional settings, or your own AudioEmitter.

Stop / StopAll

AudioService:Stop(id: string, fadeTime: number?)
AudioService:StopAll(bus: string?, fadeTime: number?)

Voice

A Voice is one playing sound.

MemberDescription
:Play(fadeIn?), :Stop(fadeTime?), :Pause(), :Resume()Control.
:Seek(position), TimePosition, TimeLengthPosition in seconds.
:FadeTo(volume, seconds?, curve?)Fade the volume.
:SetBus(bus)Move to another bus.
:IsAudible(), :GetAudibility()Whether, and how loud, it is actually heard.
:GetWaveformAsync(range, samples)Waveform data for visualisers.
Volume, PlaybackSpeed, Looped, Asset, Bus, PreserveSettable.
Status"Idle", "Loading", "Playing", "Paused", "Ended" or "Failed".
Started, Ended, Failed(reason, kind), Recovered(reason)Signals.

The mixer

Every sound plays on a bus, and buses feed into their parent up to Master. The default layout comes from the Buses setting; by default Voice chat automatically pushes Music down through a sidechain compressor.

MethodDescription
:CreateBus(name, def?): BusAdds a bus (parent defaults to Master). def: { Parent?, Volume?, Muted?, Effects?, Sidechain?, Analyze?, Spatial? }.
:GetBus(name), :BusNames()
:SetBusVolume(name, volume, fadeTime?), :GetBusVolume(name)Bus volume (0 to 1).
:SetBusMuted(name, muted)
:Duck(bus, amount, config?): DuckHandleTemporarily lowers a bus by amount (0 to 1). Config: { Key?, Attack?, Hold?, Release? }. Call handle:Release() to end it early.
:DefineSnapshot(name, volumes)A named set of bus volumes, such as { Music = 0.3, Sfx = 0.5 }.
:ApplySnapshot(name, fadeTime?): booleanMoves every listed bus to its snapshot volume. The startup mix is saved as "Default".
:SnapshotNames()

Analyze = true on a bus adds an analyzer, so you can read its loudness:

MethodDescription
:GetLevel(bus?), :GetPeak(bus?)Current and peak level (Master by default).
:GetSpectrum(bus?)Frequency spectrum, for visualisers.

The volume properties (MasterVolume, MusicVolume, SfxVolume, AmbienceVolume, UIVolume, VoiceVolume, SpeakerVolume) and Muted make settings menus a one-liner:

musicSlider.Changed:Connect(function(value)
	AudioService.MusicVolume = value
end)

Speakers (client)

CreateSpeaker

AudioService:CreateSpeaker(part: Instance, def: SpeakerDef?): Speaker

Turns a part into a speaker: a radio, a boombox, a PA system.

SpeakerDefDescription
Sound / PlaylistWhat to play: one sound id, or a playlist definition.
AutoPlayStart right away.
Volume, Bus, RangeLoudness, bus, and audible range in studs.
Directional, ConeAngle, ConeOuterGainSound mainly in front of the part.
DistanceAttenuation, EmitterPropsFine control.

Speaker methods: :Play(id?, { FadeIn?, Looped? }?), :PlayPlaylist(def), :Stop(fadeTime?), :Pause(), :Resume(), :Next(), :Previous(), :SetDirection(coneAngle?, outerGain?), :Destroy(); properties IsPlaying, CurrentTrack, Playlist, Level, Volume, Bus, Range; signals Started(track) and Stopped. :GetSpeakers() and :StopSpeakers(fadeTime?) work on all speakers.

Playlists

CreatePlaylist

AudioService:CreatePlaylist(name: string, def: PlaylistDef): Playlist
PlaylistDefDescription
Tracks: { string }Sound ids (definitions or asset ids).
ShuffleRandom order.
LoopMode"Off", "Track" (repeat one) or "Playlist" (repeat all).
CrossfadeSeconds, GapSecondsBlend between tracks, or wait between them.
Volume, Bus, StartIndex, Emitter, FallbackSoundId

:PlayPlaylist(name, index?) plays it as the music (stopping other music), :StopPlaylist(name?, fadeTime?) stops one or all, :GetPlaylist(name) and :PlaylistNames() look them up.

A Playlist has :Play(index?), :Stop(fadeTime?), :Pause(), :Resume(), :Next(fadeTime?), :Previous(fadeTime?), :JumpTo(trackOrIndex, fadeTime?), :Add(track, index?), :Remove(trackOrIndex), :Clear(), :Destroy(); the signals TrackStarted(track, index), TrackEnded(track, index), TrackFailed(track, reason), Finished and Exhausted (every track failed); and properties such as CurrentTrack, CurrentIndex, IsPlaying, TimePosition, Shuffle, LoopMode and Volume.

Tracks that fail repeatedly drop out of rotation for a while (TrackRetrySeconds) instead of breaking the playlist.

Adaptive music

DefineTrack

AudioService:DefineTrack(id: string, def: TrackDef)
TrackDefDescription
AssetA single audio file, or use Layers.
Layers: { LayerDef }Stems played in sync: { Name, Asset, Volume?, MinIntensity?, MaxIntensity?, Lead? }. A layer is heard while the intensity is inside its range. The lead layer keeps the beat clock; others are kept in sync with it.
Bpm, BeatsPerBarTempo for beat and bar events.
Sections: { SectionDef }Named parts: { Name, StartSeconds or StartBeat, EndSeconds or EndBeat, Next? }.
Bus, Volume, Looped

Playing and changing music (client)

MethodDescription
:PlayTrack(id, config?): booleanStarts a track. Config: { FadeTime?, StartTime?, Section?, Intensity? }.
:TransitionTo(id, config?)Changes track on the next beat, bar or section. Config: { Quantize?, Mode? ("Crossfade" or "Cut"), FadeTime?, Section? }.
:StopMusic(fadeTime?), :PauseMusic(), :ResumeMusic()Also apply to playlists.
:SetIntensity(value, fadeTime?)Changes which layers are heard.
:SetLayerActive(name, active?, fadeTime?)Forces a layer on or off; nil hands it back to intensity.
:JumpToSection(name, { Quantize? }?)Jumps to a section, on the next beat or bar when quantized.
:PlayStinger(id, config?)Plays a short musical hit on the beat, ducking the music. Config: { Quantize?, Bus?, Volume?, Duck?, DuckAmount?, DuckAttack?, DuckRelease? }.
:TimeUntilNext(quantize?)Seconds until the next beat, bar or section.
:GetTrackIds()

Quantize values: "Immediate", "Beat", "Bar", "Section".

EventDescription
OnBeat: Signal<(beat, track)>Every beat. Great for pulsing UI or lights.
OnBar: Signal<(bar, track)>Every bar.
SectionChanged: Signal<(section, track)>
MusicChanged: Signal<(newTrack?, oldTrack?)>
MusicFailed: Signal<(track, reason)>The music director gave up on a track.

States

AudioService:DefineState(name: string, def: { Track?, Playlist?, Snapshot?, Intensity?, FadeTime?, Quantize? })
AudioService:SetState(name: string) -- client
AudioService:GetStateNames(): { string }

A state bundles a mix snapshot, an intensity and a track or playlist. SetState applies it all and fires StateChanged. If music is already playing, the track change is a quantized transition.

AudioService:DefineState("Combat", { Track = "Battle", Snapshot = "Default", Intensity = 1, Quantize = "Bar" })
AudioService:DefineState("Menu", { Playlist = "MenuMusic", Snapshot = "Menu" })

Broadcasting (server)

All broadcast methods send to every player, or only to players when given. Messages are batched.

MethodClient does
:Broadcast(id, at?, config?, players?)Play, or PlayAt when at is given.
:BroadcastNear(id, at, radius, config?)PlayAt for players within radius studs.
:BroadcastTrack(id, config?, players?)PlayTrack.
:BroadcastPlaylist(name, index?, players?)PlayPlaylist.
:BroadcastSnapshot(name, fadeTime?, players?)ApplySnapshot.
:BroadcastIntensity(value, fadeTime?, players?)SetIntensity.
:BroadcastStopMusic(fadeTime?, players?)StopMusic.
:SetGlobalState(name, players?)SetState.

Health and recovery

Several times a second the watchdog checks every voice, bus, speaker and playlist against what should be happening. It retries assets that time out, replays stalled sounds from where they were, rebuilds broken players, restarts music that went silent and skips playlist tracks that never start.

MemberDescription
Recovered: Signal<(what, how)>Something was repaired.
Failed: Signal<(what, reason)>Something was given up on.
:GetHealthReport()Counters (audits, repairs, rebuilds, failures, recoveries) plus voices, audible voices, speakers, playlists, current track, broken assets, active fades, master level and whether the output is connected.
:ResetHealth()Resets the counters.

Properties

PropertyDescription
MasterVolume, MusicVolume, SfxVolume, AmbienceVolume, UIVolume, VoiceVolume, SpeakerVolumeBus volumes.
MutedMutes Master.
IntensityMusic intensity.
CurrentTrack, CurrentSection, Beat, Bar, Bpm, IsMusicPlayingRead-only music state.
ActiveVoices, ActiveSpeakers, LevelRead-only.

Configuration

Defaults from DefaultConfiguration/Services/AudioService46 settings
SettingDefaultWhat it does
AuditIntervalSeconds0.25Seconds between watchdog passes that reconcile every voice, bus, speaker and playlist against its intent.
WarnOnAuditErrortrueWarn when an audit pass itself errors.
LoadTimeoutSeconds8Seconds an AudioPlayer gets to become ready before it counts as broken.
LoadRetries1Fresh-player retries of the same asset after a load timeout before the voice gives up.
StallSeconds0.75Seconds TimePosition may sit still while a voice thinks it is playing before a replay from the last position.
EndEpsilonSeconds0.06How close to TimeLength counts as finished.
EndGraceSeconds0.5Seconds past a computed end to wait before ending a voice whose Ended event never arrived.
RepairAttempts3In-place replays before a broken voice is rebuilt.
RebuildAttempts2Outright player rebuilds before a voice is declared failed.
FadeOverrunSeconds1Seconds past its duration a fade may still run before it is snapped to its target.
SilenceGraceSeconds1.5Seconds of nothing audible while something should play before a playlist forces its next track / music restarts.
TrackStartTimeoutSeconds4Seconds a playlist track gets to actually make sound before its owner moves to the next one.
TrackRetrySeconds30Seconds a dropped playlist track waits before rejoining rotation.
AssetRetrySeconds60Seconds a dropped asset id waits before it may be used again.
ExhaustRetrySeconds10Max backoff between whole-list retries when a playlist has nothing left.
AssetFailureLimit2Consecutive failures before an asset id drops out of rotation.
TrackFailureLimit2Consecutive failures before a single playlist track drops out of rotation.
MusicRestartAttempts2Restarts before the music director gives up on a track and reports it.
WarnOnFailuretrueWarn when a voice, track or asset is given up on.
WarnOnMissingAssettrueWarn when :PreloadAsync finds an id that does not fetch.
FallbackSoundId""Looped when a playlist has no working tracks left; empty accepts the silence and warns.
MasterBus"Master"Bus everything ultimately routes through.
DefaultBus"Sfx"Bus a sound with no bus of its own ends up on.
DefaultLoopBus"Ambience"Bus :Loop puts a bed with no bus of its own on.
MusicBus"Music"Bus adaptive music and playlists play through.
StingerBus"Stinger"Bus stingers play through.
SpeakerBus"Speaker"Bus speakers play through.
Buses{ { Name = "Master", Volume = 1, Analyze = true }, { Name = "Music", Parent = "Master", Volume = 0.6, Analyze = true, Sidechain = { From = "Voice", Threshold = -25, Ratio = 4, Attack = 0.05, Release = 0.4 }, }, { Name = "Sfx", Parent = "Master", Volume = 1, Spatial = true }, { Name = "Ambience", Parent = "Master", Volume = 0.7, Spatial = true }, { Name = "Speaker", Parent = "Master", Volume = 1, Spatial = true }, { Name = "UI", Parent = "Master", Volume = 0.8 }, { Name = "Voice", Parent = "Master", Volume = 1, Spatial = true }, { Name = "Stinger", Parent = "Master", Volume = 0.9 }, }Signal graph in creation order; a bus may only name a parent above it. Spatial adds an AudioListener, Analyze an AudioAnalyzer.
Snapshots{ Menu = { Music = 0.7, Sfx = 0.35, Ambience = 0.2, Speaker = 0.2 }, Cutscene = { Music = 0.8, Sfx = 0.5, Ambience = 0.3, UI = 0 }, Muffled = { Music = 0.25, Sfx = 0.3, Ambience = 0.15, Speaker = 0.15 }, }Named bus-volume maps for :ApplySnapshot; the boot state is captured as "Default" automatically.
MaxConcurrentVoices64Voice budget; only non-essential one-shots are ever stolen to make room.
DefaultAttenuationBoundsNumberRange.new(10, 120)Studs where a positional sound starts falling off and where it stops being heard.
SpeakerRangeNumberRange.new(10, 120)Studs where a speaker starts falling off and where it stops being heard.
SpeakerConeAngle90Degrees of full-volume cone when a speaker is Directional.
SpeakerConeOuterGain0.15Gain (0..1) left behind a Directional speaker's cone.
DefaultCrossfadeSeconds2Crossfade between playlist tracks; 0 is a hard cut.
DefaultGapSeconds0Silence held between playlist tracks when not crossfading.
DefaultBpm120Tempo a music definition gets when it sets none.
DefaultBeatsPerBar4Beats per bar a music definition gets when it sets none.
DefaultMusicFadeSeconds1.5Fade when music starts, stops or switches track.
LayerFadeSeconds0.75Fade of a layer coming in or out on an intensity change.
LayerSyncToleranceSeconds0.08Drift from the lead layer past which a layer is nudged back into place.
BeatResyncToleranceSeconds0.12Drift from the audio past which the beat clock re-anchors.
StingerDuckAmount0.5How far (0..1) the music drops under a ducking stinger.
StingerDuckAttack0.08Seconds a stinger duck takes to reach full depth.
StingerDuckRelease0.4Seconds a stinger duck takes to release.
BroadcastRate20Packets per second the shared Remote's Stream flushes at.

Types

export type SoundDef
export type PlayConfig
export type Target = Vector3 | CFrame | Instance
export type Voice
export type Bus
export type BusDef
export type DuckConfig
export type DuckHandle
export type Snapshot = { [string]: number }
export type Speaker
export type SpeakerDef
export type Playlist
export type PlaylistDef
export type LoopMode = "Off" | "Track" | "Playlist"
export type TrackDef
export type LayerDef
export type SectionDef
export type Quantize = "Immediate" | "Beat" | "Bar" | "Section"
export type TransitionConfig
export type StingerConfig
export type MusicPlayConfig
export type PreloadReport
export type HealthReport
export type StateDef