AudioEmitter/AudioListener:GetAudibilityFor() does not use Inverse Square Law

According to the documentation, if you don’t provide a custom distance attenuation curve, it will default to returning values determined by the inverse-square law. However after testing, this does not seem to be true and I’ve rather observed a linear inverse falloff (approximately 4 / distance instead of 1 / distance^2) with some weird clamping going on if distance <= 4 to always be 1 and distance >= 10000 to always be 0.

Distance: 0,	 Theoretical: inf,	 Roblox: 1.000000000
Distance: 1000,	 Theoretical: 0.000001000,	 Roblox: 0.004000000
Distance: 2000,	 Theoretical: 0.000000250,	 Roblox: 0.002000000
Distance: 3000,	 Theoretical: 0.000000111,	 Roblox: 0.001333333
Distance: 4000,	 Theoretical: 0.000000062,	 Roblox: 0.001000000
Distance: 5000,	 Theoretical: 0.000000040,	 Roblox: 0.000800000
Distance: 6000,	 Theoretical: 0.000000028,	 Roblox: 0.000666667
Distance: 7000,	 Theoretical: 0.000000020,	 Roblox: 0.000571429
Distance: 8000,	 Theoretical: 0.000000016,	 Roblox: 0.000500000
Distance: 9000,	 Theoretical: 0.000000012,	 Roblox: 0.000444444
Distance: 10000,	 Theoretical: 0.000000010,	 Roblox: 0.000000000

Distance: 3.9,	 Theoretical: 0.065746220,	 Roblox: 1.000000000
Distance: 4.0,	 Theoretical: 0.062500000,	 Roblox: 1.000000000
Distance: 4.1,	 Theoretical: 0.059488400,	 Roblox: 0.975609779
Distance: 4.2,	 Theoretical: 0.056689342,	 Roblox: 0.952381015
Distance: 4.3,	 Theoretical: 0.054083288,	 Roblox: 0.930232525
Distance: 4.4,	 Theoretical: 0.051652893,	 Roblox: 0.909090877
Distance: 4.5,	 Theoretical: 0.049382716,	 Roblox: 0.888888896
Distance: 4.6,	 Theoretical: 0.047258979,	 Roblox: 0.869565248
Distance: 4.7,	 Theoretical: 0.045269353,	 Roblox: 0.851063848
Distance: 4.8,	 Theoretical: 0.043402778,	 Roblox: 0.833333313
Distance: 4.9,	 Theoretical: 0.041649313,	 Roblox: 0.816326499
Distance: 5.0,	 Theoretical: 0.040000000,	 Roblox: 0.800000012

Distance: 9000,	 Theoretical: 0.000000012,	 Roblox: 0.000444444
Distance: 10000,	 Theoretical: 0.000000010,	 Roblox: 0.000000000
Distance: 11000,	 Theoretical: 0.000000008,	 Roblox: 0.000000000
Distance: 12000,	 Theoretical: 0.000000007,	 Roblox: 0.000000000
Distance: 13000,	 Theoretical: 0.000000006,	 Roblox: 0.000000000
Distance: 14000,	 Theoretical: 0.000000005,	 Roblox: 0.000000000
Distance: 15000,	 Theoretical: 0.000000004,	 Roblox: 0.000000000

The output was produced by following script:

local emitterPart = Instance.new("Part")
emitterPart.Name = "EmitterPart"
emitterPart.Anchored = true
emitterPart.Position = Vector3.new(0, 0, 0)
emitterPart.Parent = workspace

local audioEmitter = Instance.new("AudioEmitter")
audioEmitter.Parent = emitterPart

local listenerPart = Instance.new("Part")
listenerPart.Name = "ListenerPart"
listenerPart.Anchored = true
listenerPart.Position = Vector3.new(0, 0, 0)
listenerPart.Parent = workspace

local audioListener = Instance.new("AudioListener")
audioListener.Parent = listenerPart

audioEmitter:SetDistanceAttenuation(nil) -- Ensure AudioEmitter uses "Invere Square Law" according to documentation
audioListener:SetDistanceAttenuation(nil) -- Ensure AudioListener does not modify audibility

local function format(value: number)
	return string.format("%.9f", value)
end

local function getVolume(distance: number)
	return 1 / (distance ^ 2)
end

for distance = 0, 10000, 1000 do
	listenerPart.Position = Vector3.new(distance, 0, 0)

	local theoretical = format(getVolume(distance))
	local roblox = format(audioEmitter:GetAudibilityFor(audioListener))

	print(`Distance: {distance},\t Theoretical: {theoretical},\t Roblox: {roblox}`)
end

print()

for distance = 3.9, 5, 0.1 do
	listenerPart.Position = Vector3.new(distance, 0, 0)

	local theoretical = format(getVolume(distance))
	local roblox = format(audioEmitter:GetAudibilityFor(audioListener))

	print(`Distance: {string.format("%.1f", distance)},\t Theoretical: {theoretical},\t Roblox: {roblox}`)
end

print()

for distance = 9000, 15000, 1000 do
	listenerPart.Position = Vector3.new(distance, 0, 0)

	local theoretical = format(getVolume(distance))
	local roblox = format(audioEmitter:GetAudibilityFor(audioListener))

	print(`Distance: {distance},\t Theoretical: {theoretical},\t Roblox: {roblox}`)
end

System Info:

  • CPU: AMD Ryzen 5 3600X @3.8GHz
  • GPU: Nvidia GeForce RTX 2060 Super
  • RAM: 4x8GB DDR4 3200MT/s

Expected behavior

I expect :GetAudibilityFor() to use the inverse-square law.

Hey @abidbmt, sorry for the confusion – the docs reference the inverse-square law as sort of a shorthand, but the behavior is a bit more complicated

The inverse-square law applies to things like power or intensity, while GetAudibilityFor gives units in amplitude. Power is proportional to amplitude-squared, so Amplitude = 4 / Distance becomes something like Power = 16 / Distance^2 – this is following the inverse-square law; but after unit conversions, that’s not obvious.

The clamping

with some weird clamping going on if distance <= 4 to always be 1 and distance >= 10000 to always be 0 .

exists mostly for practical reasons – very large distances lose floating point precision, and we didn’t want very small distances to result in infinite amplitude, since 4 / x has a vertical asymptote at x = 0 :sweat_smile:

2 Likes

Ahh I see how it works now, thank you for the explanation! Can’t wait to see the awesome stuff that you guys are working on at the next RDC :slightly_smiling_face:

1 Like