okay so the material based reverb props are very cool and it performs very well but i dont think it worked as intended for me (it only changed the AmbientReverb in SoundService which is not customizable, it just has presets) and then i noticed another issue: it doesnt take into account the playerās surroundings - by that i mean walls and ceilings, there was reverb even in an open space.
so as a proof of concept i modified it:
i switched out the fibonacci sphere with a regular sphere (cuz it kinda messed up the weighing imo) and added a ācoverageā variable which uses the raycast info (pos and normal) to weigh down the WetLevel in the reverb:
local coverage = 0
-- Weight Properties:
local floorWeight = -1
local ceilingWeight = 0.1
local wallWeight = 0.1
and this is how it turned out (i know its not the perfect solution, it can definitely be better)
this is the full code (dont judge, i know write in spaghetti):
-- ===================================================================
-- // REVERBAPI BY SYLZYRICAL | ORIGINAL CONCEPT BY GibusWielder
-- // VERSION 1.2 (12/6/2025)
-- ===================================================================
-- !DO NOT TOUCH ANYTHING BELOW!
-- ===================================================================
-- // SERVICES & MODULES
-- ===================================================================
local Services = {
Workspace = game:GetService("Workspace"),
Players = game:GetService("Players"),
Debris = game:GetService("Debris"),
RunService = game:GetService("RunService"),
TweenService = game:GetService("TweenService"),
SoundService = game:GetService("SoundService")
}
local SettingsModule = script:WaitForChild("Settings")
local MaterialsModule = script:WaitForChild("Materials")
local LegacyReverbMapModule = script:WaitForChild("MaterialsLegacyAudio")
if not (SettingsModule and MaterialsModule and LegacyReverbMapModule) then
warn("ReverbAPI: Failed to load one or more required modules! Script will not run.")
return
end
local Settings = require(SettingsModule)
local Materials = require(MaterialsModule)
local LegacyReverbMap = require(LegacyReverbMapModule)
-- ===================================================================
-- // LOCAL VARIABLES & CONFIG
-- ===================================================================
local Player = Services.Players.LocalPlayer
local PlayerScripts = Player:WaitForChild("PlayerScripts")
local Character = Player.Character or Player.CharacterAdded:Wait()
local Head = Character:WaitForChild("Head")
local isReverbActive = PlayerScripts:WaitForChild("ReverbAPI_Active", 10)
local voiceReverbTarget = nil
local voiceChatReverbScript = script:FindFirstChild("VoiceChatReverb")
local activeReverbTweens = {}
local sphereDirections = {}
local heartbeatConnection = nil
local FALLBACK_KEY = Settings.Reverb.MaterialsFallbackKey
local FALLBACK_REVERB_SETTINGS = Materials[FALLBACK_KEY]
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Settings.Raycast.FilterType
raycastParams.IgnoreWater = Settings.Raycast.IgnoreWater
local NUM_RAYS = Settings.Raycast.NumRays
local RAY_LENGTH = Settings.Raycast.RayLength
local timeSinceLastUpdate = 0
local UPDATE_INTERVAL = Settings.Performance.UpdateInterval
-- ===================================================================
-- // AUDIO API SETUP
-- ===================================================================
local coverage = 0
-- Weight Properties:
local floorWeight = -1
local ceilingWeight = 0.1
local wallWeight = 0.1
local Output = Instance.new("AudioDeviceOutput")
local Reverb = Instance.new("AudioReverb")
local AudioListener = workspace.CurrentCamera:WaitForChild("AudioListener")
local ListenerWire = AudioListener:FindFirstChild("Wire") :: Wire
local OutputWire = Instance.new("Wire")
local ReverbWire = Instance.new("Wire")
Output.Player = Player
Output.Parent = Player
OutputWire.SourceInstance = Reverb
OutputWire.TargetInstance = Output
OutputWire.Parent = Output
local ReverbWire
if ListenerWire then
ListenerWire.TargetInstance = Reverb
Reverb.Parent = Output
else
ReverbWire = Instance.new("Wire")
Reverb.Parent = Output
end
if ReverbWire then
ReverbWire.SourceInstance = AudioListener
ReverbWire.TargetInstance = Reverb
ReverbWire.Parent = Reverb
end
-- ===================================================================
-- // FUNCTION DEFINITIONS
-- ===================================================================
local function getVoiceReverbTarget()
if voiceChatReverbScript and voiceChatReverbScript.Value then
if voiceReverbTarget and voiceReverbTarget.Parent then return voiceReverbTarget end
local target = voiceChatReverbScript:WaitForChild("PlayerVoiceReverb", 2)
if target and target:IsA("AudioReverb") then
voiceReverbTarget = target
return voiceReverbTarget
end
end
return nil
end
local function getMostHitMaterial()
if not Head or not Head.Parent then return nil end
local originPosition = Head.Position
local materialCounts = {}
local hits = 0
for _, direction in ipairs(sphereDirections) do
local result = Services.Workspace:Raycast(originPosition, direction * RAY_LENGTH, raycastParams)
if result and result.Material then
-- Check the normal and add (or subtract) to the coverage weight.
local normal = result.Normal
local pos = result.Position
local rootPart = Character.PrimaryPart
local rootPos = rootPart.Position
hits+=1
if math.abs(normal:Dot(Vector3.yAxis)) < 0.1 then
coverage+=wallWeight
else
if pos.Y < rootPos.Y then
coverage+=floorWeight
else
coverage+=ceilingWeight
end
end
if Settings.Debug.VisualizeHits then
local debugPart = Instance.new("Part")
debugPart.CanQuery = false
debugPart.Material = Enum.Material.Neon
debugPart.Color = Color3.fromRGB(255, 0, 0)
debugPart.Size = Vector3.new(0.15, 0.15, 0.15)
debugPart.Anchored = true
debugPart.CanCollide = false
debugPart.Position = result.Position
debugPart.Parent = Services.Workspace
Services.Debris:AddItem(debugPart, Settings.Debug.VisualizeLifetime)
if Settings.Debug.VisualizeHitMaterialName then
local billboardGui = Instance.new("BillboardGui")
billboardGui.Size = UDim2.fromScale(5, 1.5)
billboardGui.StudsOffset = Vector3.new(0, 1.5, 0)
billboardGui.AlwaysOnTop = true
local textLabel = Instance.new("TextLabel")
textLabel.Size = UDim2.fromScale(1, 1)
textLabel.Text = result.Material.Name
textLabel.TextColor3 = Color3.new(1, 1, 1)
textLabel.TextScaled = true
textLabel.BackgroundColor3 = Color3.new(0, 0, 0)
textLabel.BackgroundTransparency = 0.5
textLabel.Parent = billboardGui
billboardGui.Parent = debugPart
end
end
local materialName = result.Material.Name
materialCounts[materialName] = (materialCounts[materialName] or 0) + 1
end
end
coverage = (coverage/hits)
local mostFrequentMaterial, highestCount = nil, 0
for materialName, count in pairs(materialCounts) do
if count > highestCount then
highestCount = count
mostFrequentMaterial = materialName
end
end
return mostFrequentMaterial
end
local tweenInfo = TweenInfo.new(UPDATE_INTERVAL, Enum.EasingStyle.Linear)
local function applySettingsToVoiceReverb(settings, shouldBypass)
local targetReverb = Reverb
if not targetReverb then return end
if activeReverbTweens[targetReverb] then
activeReverbTweens[targetReverb]:Cancel()
end
local goalProperties = {}
if shouldBypass then
goalProperties.Bypass = true
else
for prop, value in pairs(settings) do
if prop == "WetLevel" then
goalProperties[prop] = value+coverage*20
print("Coverage Weight: "..tostring(coverage*20))
continue
else
goalProperties[prop] = value
end
end
goalProperties.Bypass = false
end
local reverbTween = Services.TweenService:Create(targetReverb, tweenInfo, goalProperties)
reverbTween:Play()
activeReverbTweens[targetReverb] = reverbTween
reverbTween.Completed:Connect(function() activeReverbTweens[targetReverb] = nil end)
end
local function updateReverb()
if not Character or not Character.Parent or not Head or not Head.Parent then return end
if not isReverbActive or not isReverbActive.Value then return end
local dominantMaterial = getMostHitMaterial()
local modernReverbSettings = Materials[dominantMaterial or ""] or FALLBACK_REVERB_SETTINGS
local legacyReverbPreset = LegacyReverbMap[dominantMaterial or ""] or LegacyReverbMap[FALLBACK_KEY] or Enum.ReverbType.NoReverb
local voiceReverbAvailable = (voiceChatReverbScript and voiceChatReverbScript.Value)
local voiceTarget = getVoiceReverbTarget()
if typeof(modernReverbSettings) == "table" then
applySettingsToVoiceReverb(modernReverbSettings, false)
else
warn("ReverbAPI: Invalid modern reverb preset for material '" .. tostring(dominantMaterial) .. "'. Bypassing voice reverb.")
applySettingsToVoiceReverb({}, true)
end
end
local function disconnectHeartbeat()
if heartbeatConnection then
heartbeatConnection:Disconnect()
heartbeatConnection = nil
end
end
local function connectHeartbeat()
if heartbeatConnection then return end
heartbeatConnection = Services.RunService.Heartbeat:Connect(function(deltaTime)
if not Character or not Character.Parent or not Head or not Head.Parent then
disconnectHeartbeat()
return
end
timeSinceLastUpdate = timeSinceLastUpdate + deltaTime
if timeSinceLastUpdate >= UPDATE_INTERVAL then
timeSinceLastUpdate = 0
updateReverb()
end
end)
end
local function onCharacterAdded(newCharacter)
disconnectHeartbeat()
Character = newCharacter
Head = Character:WaitForChild("Head", 5)
if not Head then
warn("ReverbAPI: CharacterAdded - Could not find Head!")
return
end
local newFilterList = {Character}
if Settings.Raycast.FilterDescendantsInstances then
for _, item in ipairs(Settings.Raycast.FilterDescendantsInstances) do
if item then table.insert(newFilterList, item) end
end
end
raycastParams.FilterDescendantsInstances = newFilterList
task.wait(0.1)
connectHeartbeat()
timeSinceLastUpdate = 0
updateReverb()
end
-- ===================================================================
-- // INITIALIZATION & EVENT CONNECTIONS
-- ===================================================================
local samples = NUM_RAYS
local rings = math.floor(math.sqrt(samples))
local segments = math.floor(samples / rings)
-- Changed from fibonacci to a basic sphere.
for i = 0, rings - 1 do
local phi = math.pi * (i / (rings - 1))
for j = 0, segments - 1 do
local theta = (2 * math.pi) * (j / segments)
local x = math.sin(phi) * math.cos(theta)
local y = math.cos(phi)
local z = math.sin(phi) * math.sin(theta)
table.insert(sphereDirections, Vector3.new(x, y, z))
end
end
if isReverbActive then
isReverbActive.Changed:Connect(function(isActive)
if not isActive then
local currentVoiceTarget = getVoiceReverbTarget()
if currentVoiceTarget then
applySettingsToVoiceReverb({}, true)
end
Services.SoundService.AmbientReverb = Enum.ReverbType.NoReverb
else
updateReverb()
end
end)
end
if voiceChatReverbScript then
voiceChatReverbScript:GetPropertyChangedSignal("Value"):Connect(function()
updateReverb()
end)
end
Player.CharacterAdded:Connect(onCharacterAdded)
Player.CharacterRemoving:Connect(disconnectHeartbeat)
if Player.Character then
onCharacterAdded(Player.Character)
end
print("ReverbAPI_V1.2 | Sylzyrical")
you should def make something similar for the script, something thats more accurate and robust.