SFX deprecated
Stream and shape audio. Built on rodio + cpal: cross-platform output via WASAPI / ALSA / PulseAudio / CoreAudio. Each Sound is independent, play, layer, position in 3D, attach DSP shaders.
local SFX = import("SFX")
local Asset = import("Asset")
local data = Asset.GetAsset("Sound", "sfx.explode")
local snd = SFX.LoadSound(data)
snd.Volume = 0.6
snd.Echo = { Delay = 120, Feedback = 0.4, Mix = 0.3 }
snd:Play()
SFX API
Wrap a SoundData into a playable Sound.
The same data can back many independent Sounds, each
with its own shader chain and Play/Stop state. To
free every Sound backed by a particular
SoundData, call
data:Free()
(or Asset.Drop("Sound:path") for path-loaded
sounds).
Standalone shader factories
Build a SoundShader userdata you can stack on any Sound
via sound:ApplyShader(shader). Useful for shader presets
you reuse across many sounds.
SFX.Volume(factor) | Linear gain. |
SFX.Speed(factor) | Time + pitch. |
SFX.Pan(amount) | Stereo pan, -1 to +1. |
SFX.FadeIn(seconds) | 0->1 envelope from start. |
SFX.FadeOut(seconds) | 1->0 envelope at end. |
SFX.LowPass(freq) | Biquad lowpass at the given Hz. |
SFX.HighPass(freq) | Biquad highpass. |
SFX.Delay(seconds) | Wait before producing audio. |
SFX.Repeat() | Loop indefinitely. |
SFX.Distortion(amount) | Soft saturation. |
SFX.Echo(delay_ms, feedback?, mix?) | Single-tap echo. |
SFX.Reverb(mix?, decay?) | Diffuse reverb. |
SFX.Tremolo(rate?, depth?) | Periodic amplitude wobble. |
Sound
Properties & signals
Fires when :Play() actually starts feeding audio.
Fires on natural completion or :Stop().
Fires every time a looped sound wraps back to the start.
Only fires while Looped = true; the first
:Play() doesn't fire it, only subsequent loop
restarts. If you used the legacy SFX.Repeat
shader (or snd:Loop()) instead of the
Looped property, this signal stays silent, the
source repeats internally with no boundary the engine can
observe.
When true, playback restarts at the beginning each time the
source ends, and DidLoop fires on each wrap.
Toggle live, setting to false mid-play lets the current
iteration finish naturally and then stops.
Current playback position in seconds. Reads track real-time
progress (modulo the source duration when
Looped is true). Writing while playing seeks
immediately; writing while stopped sets the start offset for
the next :Play() call.
-- Background music with seek + loop counter.
local music = SFX.LoadSound(Asset.GetAsset("Sound", "music.theme"))
music.Looped = true
music.TimePosition = 12.5 -- start at 12.5 s on next Play
music:Play()
local loops = 0
music.DidLoop:Connect(function()
loops += 1
print("theme has restarted " .. loops .. " times")
end)
Playback control
Begin playback. Re-applying shaders / volume before this changes
how this playback sounds, settings don't carry between
:Play() calls unless you re-apply them.
Drop all fluent shaders and attachments, back to a clean state.
Fires every interval seconds with elapsed playback
time. Useful for syncing animation / particles to audio.
Modifier properties
Each built-in modifier is exposed as a single live property on the
Sound. Assigning replaces the prior value (no stacking), reading
returns the current value, and assigning nil /
0 / false removes the modifier. Changes
apply on the next :Play(), settings are kept
between plays unless you explicitly clear them.
snd.Volume | number | 1.0 unchanged, 0.0 silence, >1 boosted. |
snd.Speed | number | 2.0 = 2× faster (also pitched up an octave). |
snd.Pitch | number | Alias for Speed, set either, both read the same value. |
snd.Pan | number | -1 left / 0 center / +1 right. |
snd.LowPass | number? (Hz) | Biquad lowpass at the given Hz. Set to nil to disable. |
snd.HighPass | number? (Hz) | Biquad highpass. Set to nil to disable. |
snd.FadeIn | number (s) | Linear envelope from start. Set to 0 to disable. |
snd.FadeOut | number (s) | Linear envelope at end. Set to 0 to disable. |
snd.Delay | number (s) | Wait before producing audio. Set to 0 to disable. |
snd.Loop | boolean | Legacy in-source repeat (no DidLoop signal). Prefer snd.Looped for engine-observed loops. |
snd.Distortion | number | Soft saturation. Set to 0 to disable, >1 hard-clips. |
snd.Echo | table? { Delay, Feedback?, Mix? } | Single-tap echo. Set to nil to disable. |
snd.Reverb | table? { Mix?, Decay? } | Diffuse reverb. Set to nil to disable. |
snd.Tremolo | table? { Rate?, Depth? } | Periodic amplitude wobble. Set to nil to disable. |
-- Live volume duck while a dialogue line plays.
music.Volume = 0.2
dialog:Play()
dialog.Stopped:Connect(function() music.Volume = 1.0 end)
-- Underwater preset.
snd.LowPass = 800
snd.Reverb = { Mix = 0.4, Decay = 3 }
3D positional audio
Position the sound in world space, the active
Renderable.Camera is the listener. Inside
MinFalloff the sound is at full volume, past
MaxFalloff it is silent, and in between it rolls off
smoothly. Setting Position to nil reverts
to non-positional (no attenuation, no spatial pan).
World-space position. Assign a Vector to place
in 3D, or nil to disable spatial audio.
Inner radius. Distances ≤ this play at full volume. Defaults
to 0.
Outer radius. Distances ≥ this are silent. Defaults to
20.
Convenience form, takes raw components plus an optional
override for MaxFalloff. Equivalent to setting
snd.Position (and optionally snd.MaxFalloff).
Same as snd.Position = nil.
Tweening
A Sound is a valid target for
TweenService.new.
Tweenable properties: Volume, Pitch /
Speed, Pan, Distortion,
Position, MinFalloff,
MaxFalloff.
local Tween = import("TweenService")
-- 2 s fade-out, then stop.
local t = Tween.new(music, 2, "Sine", { Volume = 0 })
t.Completed:Connect(function() music:Stop() end)
t:Play()
DSP shaders
Same shader contract as the GPU side, but the body runs once per audio sample. See Shaders > Sound shaders.
Stack a SoundShader built via SFX.Volume(...) /
etc. Stacks (unlike fluent calls).
SoundShader
Returned by every standalone shader factory
(SFX.Volume(0.6), SFX.Echo(120, 0.4, 0.3),
etc.). It's an opaque handle, the only thing you do with it is
pass it to sound:ApplyShader(shader). Multiple sounds can
share the same shader instance, and one sound can stack many shaders.
-- Build a "underwater" preset once and apply to many sounds.
local underwater_lpf = SFX.LowPass(800)
local underwater_rev = SFX.Reverb(0.4, 3)
for _, snd in all_underwater_sounds do
snd:ApplyShader(underwater_lpf)
snd:ApplyShader(underwater_rev)
end