Simple Volumetric Audio by Rylvns

Hello developers!

I’m excited to share with you a new project I’ve been working on. I’ve created a game featuring a volumetric audio system that adds depth and immersion to your gameplay. You can check it out here: Game Link.

Feel free to explore and even copy the game if you want to experiment with it yourself. The magic happens in the StarterPack, where you’ll find the main script. It’s a small addition that can make a big difference in the overall atmosphere of your games.

I hope you enjoy playing around with it as much as I enjoyed creating it. Let me know your thoughts and any feedback you might have!


Modified & more optimized code:

Next version showcase

40 Likes

LETS GOO I really love this volumetric audio it does great in the game named evorooms

5 Likes

If your game has many sounds, then this version of the script might not be best for optimization. I plan to make a new, improved and optimized version of this volumetric audio system. Stabilizing optimization and realism is my goal in the new version.

2 Likes

can you describe what it does and the benefit in words?
Thanks

1 Like

If your game is realistic and you want a realistic sound experience then this is the simplest way. In this version of the script, it only checks to see if you’re behind a wall and muffles the sound, and adjusts the sound volume based on how far you are from the sound source.

In the next version though, it will have material “sound wave” absorption so let’s say the part that’s blocking the sound source is fabric, then it will only add a small muffle, but if it’s something like metal then it would be muffled.
As well as in the next version, I’m adding raycast bounce reflections, so it will add reverb based on how many parts each ray hits.

2 Likes

ah, cool, thanks for the details, ill check it out

EDIT: I checked it out and it seems to do the sound more based off of where the camera is at verses the player.

Like here, I am in a sounds dead zone, there is no line of sight from the player or the camera

but if I move just the camera to a line of sight, the player still is in the dead zone

but if I pan the camera near the sound , I hear the sound… even though my player should not.

is that true?

2 Likes

I thought that using the camera would be more appropriate, although if it really should be where the players head/body is then I can change it to that. I have no problem changing it since both ways makes sense to me (camera vs player head).

yes I agree!

Could be as is or a tunable, perhaps, one way or the other…

For me to figure that out, or notice it, in the demo I walked around a bit,

Listened…

moved…

then I thought it was line of sight on the player to the white balls…

and I hid

behind walls…

and it
went

away…

1 Like

umm… I did the EDIT version and…

  23:40:21.185  Lord_BradyRocks's Place Number: 1001 auto-recovery file was created  -  Studio
  23:40:23.737  [ACTION ITEM] Experience doesn't have permissions for sound asset 16491133121. Click here to grant experience permission to asset.  -  Studio
  23:40:23.737  Failed to load sound rbxassetid://16491133121: User is not authorized to access Asset. (x5)  -  Studio
  23:40:24.099  [ACTION ITEM] Experience doesn't have permissions for sound asset 16491133121. Click here to grant experience permission to asset.  -  Studio
  23:40:24.099  Failed to load sound rbxassetid://16491133121: User is not authorized to access Asset. (x5)  -  Studio

is the sound open source etc…
:slight_smile:

1 Like

The sound was uploaded to my profile so that’s why you can’t hear it, you can replace it with a Roblox free sound if you want, I will instead upload it to the game assets but I don’t think this will actually solve the issue.

1 Like

ummm, maybe just use a roblox free sound in the demo… I dont know…

1 Like

First off, I love this! I’ve needed something like this for so long and it works perfectly. I’m mainly replying however because I’ve actually taken your original script and modified it to be a bunch more optimized for games with larger workspaces.

  • No longer orients through workspace every heartbeat
  • All sounds in the workspace before the game begins must be tagged with “TrackSound”
  • Any instances added or removed from the workspace fire a set of functions that will check and see if it is a sound needed to be added to the ray casts of the volumetric audio
-- by Rylen
-- 2/23/24 @ 10:46 PM

local RunService = game:GetService("RunService");
local Players = game:GetService("Players");
local Player = Players.LocalPlayer;
local Camera = workspace.CurrentCamera;
local CollectionService = game:GetService("CollectionService");

local Sounds = {};

local Params = RaycastParams.new();
Params.BruteForceAllSlow = false;
Params.IgnoreWater = true;
Params.RespectCanCollide = false;
Params.FilterType = Enum.RaycastFilterType.Exclude;

function Lerp(Start, Goal, Alpha)
    return Start + (Goal - Start) * Alpha;
end;

local function InitializeSound(Object)
    Sounds[Object] = {
        ["MaxVolume"] = Object.Volume,
        ["MaxAudibleDistance"] = Object:GetAttribute("MaxAudibleDistance") or 50,
    };
    local EqualizerSoundEffect = Instance.new("EqualizerSoundEffect", Object);
    EqualizerSoundEffect.Name = "Equalizer";
    EqualizerSoundEffect.Enabled = true;
    EqualizerSoundEffect.HighGain = 0;
    EqualizerSoundEffect.MidGain = 0;
    EqualizerSoundEffect.LowGain = 0;
    EqualizerSoundEffect.Parent = Object;
end;

-- Detect whether a sound is being added or removed from the workspace (Fired with connections below) --
local function OnSoundAdded(Object)
    if Object:IsA("Sound") and Object.Parent:IsA("BasePart") then
        if not Sounds[Object] then
            InitializeSound(Object);
        end
    end
end

local function OnSoundRemoved(Object)
    if Sounds[Object] then
        Sounds[Object] = nil;
    end
end

-- Fire above functions whenever something is added to the workspace --
workspace.DescendantAdded:Connect(OnSoundAdded);
workspace.DescendantRemoving:Connect(OnSoundRemoved);

-- SET UP PRE-EXISTING SOUNDS --
for _, Object in pairs(CollectionService:GetTagged("TrackSound")) do
    OnSoundAdded(Object)
end

RunService.Heartbeat:Connect(function()
    for Sound, Properties in pairs(Sounds) do
        local Part = Sound.Parent;

        local MaxVolume = Properties.MaxVolume;
        local Distance = (Camera.CFrame.Position - Part.Position).Magnitude;

        if Distance < Properties.MaxAudibleDistance then
            Params.FilterDescendantsInstances = (function()
                local Results = {Part};
                for _, User in pairs(Players:GetPlayers()) do
                    if User.Character then
                        for _, Object in pairs(User.Character:GetDescendants()) do
                            if Object:IsA("BasePart") then
                                table.insert(Results, Object);
                            end
                        end
                    end
                end
                return Results;
            end)();

            local RaycastResults = workspace:Raycast(Camera.CFrame.Position, Part.Position - Camera.CFrame.Position, Params);
            if RaycastResults then
                local MuffleFactor = 1 - (1 - math.min(Distance / Properties.MaxAudibleDistance, 1));
                Sound.Equalizer.HighGain = Lerp(Sound.Equalizer.HighGain, -80 * MuffleFactor, 0.25);
                Sound.Equalizer.MidGain = Lerp(Sound.Equalizer.MidGain, -80 * MuffleFactor, 0.25);
                Sound.Equalizer.LowGain = Lerp(Sound.Equalizer.LowGain, -20 * MuffleFactor, 0.25);
            else
                Sound.Equalizer.HighGain = Lerp(Sound.Equalizer.HighGain, 0, 0.15);
                Sound.Equalizer.MidGain = Lerp(Sound.Equalizer.MidGain, 0, 0.15);
                Sound.Equalizer.LowGain = Lerp(Sound.Equalizer.LowGain, 0, 0.15);
            end

            Sound.Volume = MaxVolume * (1 - (Distance / Properties.MaxAudibleDistance));
        end
    end;
end);

Again, thank you for making this, and I hope this helps anyone who may want to use this in games with a large workspace!

3 Likes

Hey! This system is great! I do have a few suggestions from looking at the source code and some adjustments from another similar system called “3d sound system”
Link: 3D Sound System - Realistic frequency dampening

Suggestions:
Use the multiraycast module for better and more accurate detection.
Link:MultiRaycast - Easy & optimized parallel raycast

Add sound dampening like 3d sound system to enhance immersion.
(Fork it from them lol)

Control sound volume(volumetric) behind a wall by firing a ray cast though the wall to detect how far away it is and / it by thickness of the wall to produce the perfect volumetric calculation.

These suggestions are just stuff I would be glad to see!

Thank you for your time.

2 Likes

Woah! Amazing work!
Thank you for this optimization for others, I will put it in the main message for them to see.

1 Like

Thank you for these suggestions, I will definitely place these in for the next version!!

I made a video for the next version. Trying to solve sound bounces.

4 Likes

Very nice! The use of rays seems much better now
Good luck on the project

1 Like

Preview of Version-2aa, recoded:

I will explain what this does tmr. (2am edt)

4 Likes

Hello, Any updates on the new version?

Not much since the video, I’ve just started school and I’m thinking exactly how this will work.
A clone of the main sound source will be parented to a pair of attachments which are: 5 closest to the player, and 5 farthest from the player (editable). The sounds will have reverb and the other sound effects that roblox offers. Using this will allow the sound bounces which I wanna add. If you have questions I’m sure I have an answer. If you’re a developer maybe we could work on this together.