The right way to make a volume slider in Unity (using logarithmic conversion)

JohnUnity81 Comments

Creating a UI slider volume control in Unity is straightforward, by using exposed parameters, the Audio Mixer and a simple script. This is useful for creating UI audio volume controls for your player.

There’s just one problem…

…Unity’s standard tutorial, although good, misses out on a crucial step that makes the slider control much too sensitive and difficult to use.

This happens because the slider value is linear but the audio mixer value (the attenuation) is logarithmic.

Luckily, however, there’s a very simple fix. In this post I’ll show you how to prevent the sensitive slider issue and make a, smooth, audio control slider:

If you haven’t already seen it, you can check out Unity’s official tutorial here, or my own tutorial video below but, if not, don’t worry, because you’ll also find step by step instructions later on in this post that include a fix for the sensitive slider issue.

The Sensitive Slider Issue: Why is this a problem?

If you create an audio slider using the standard method, and reduce the slider to about halfway, you’ll find that the audio isn’t half as loud (as you might expect), in fact it’s virtually silent.

This is because the volume range of the Audio Group fader, which is in decibels, is logarithmic meaning that the increments are exponential. Each increment on the scale represents a greater and greater value: e.g. 10, 100, 1000 and so on. In comparison the slider scale is linear and the increments are evenly spaced: e.g. 1, 2, 3.

Normally we perceive half volume to be at around -6db. However, moving the slider to halfway doesn’t do this. In fact, at halfway, it actually sets a value of -40db which is almost at the bottom.

To fix this, the linear slider value needs to be converted.

The difference this makes is clear when comparing the two methods side by side:

Unity Volume Slider Example

On the far right is the fader response using the standard ‘direct’ method. To the left of this is a fader using the ‘converted’ method

The quick & easy fix to prevent the sensitive slider issue

If you already have this issue, and you just want to fix it, here’s what to do:

  • Use a slider value range of 0.0001 – 1
    instead of -80db – 0db (the 0.0001 is important, and stops the slider breaking at zero)
  • When passing the slider value to the SetFloat function, convert it using Mathf.Log10(value) * 20;
    e.g. mixer.SetFloat(“MusicVol”, Mathf.Log10(sliderValue) * 20);

For full instructions, including this fix, keep reading, and follow the steps below.

How to create a UI volume control slider in Unity (that actually works)

Create the slider

  1. In the hierarchy select Create > UI > Slider to create a UI Slider for your volume control
  2. Select the Slider object and set the Min Value to 0.0001 (the logarithmic conversion will not work correctly at zero)
  3. Set the Value to 1
    Unity Volume Slider Inspector Settings

Expose the Audio Mixer Group volume to scripting

  1. Select the Audio Mixer Group that you want to control with the slider
  2. In the inspector, under attenuation, right click on the volume label and select Expose ‘Volume (of Music)’ to script
    Expose Parameter in Unity
  3. In the Audio Mixer Panel, select the Exposed Parameters dropdown and give the, now exposed, volume parameter a name. e.g. “MusicVol”. You’ll need to use the parameter name given here when accessing it from a script.
    Exposed Parameters Menu

Connect the slider with a script

  1. Add a new C Sharp script to the slider object called SetVolume or similar.
  2. Open it for editing and type, or paste, the following script:
  3. Save the script and return to Unity.
  4. Use circle select to connect the mixer reference variable to the Mixer that contains the exposed parameter.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
public class SetVolume : MonoBehaviour {
    public AudioMixer mixer;
    public void SetLevel (float sliderValue)
    {
        mixer.SetFloat("MusicVol", Mathf.Log10(sliderValue) * 20);
    }
}

Important: If you’re just copying the function, not the whole script, remember to add the using UnityEngine.Audio namespace at the top of the script. This is required to access Audio Mixer functions.

Add the function to the slider

  1. Select the slider object and add a new ‘On value changed’ event.
  2. Drag the SetVolume script to the field or set it by using circle select.
  3. Select the function dropdown, where it currently reads ‘no function’. Select the SetVolume script and then select the SetLevel function (listed under Dynamic float*).
    Connect slider to volume control function in Unity

* If the function doesn’t appear under the dynamic float list make sure that 1. the access modifier for the function is set to public and 2. that the function takes a single float parameter.

Important: The Dynamic Float option is missing in Unity 2019.2.5 due to a suspected bug. Use the workaround at the end of this article to fix it.

Save the scene and hit play to test

The slider should now control the Audio Mixer Group fader.

Repeat to create music and audio volume controls. You can even use multiple mixer groups to give players more granular control over voices, sound effects, music and other sound categories.

Extending the script: How to save and load the volume slider value

Now that you’ve created a volume control, you may wish to save the value whenever it’s changed so that the volume level, and the slider position, stay the same if you load a different scene or restart the game.

Thankfully, this is easy to do by using the PlayerPrefs class, which allows you to store and reload player preferences between games. By adding just a few new lines to the same script we will be able to:

  • Save the slider value whenever it’s changed (this sets the Player Preference, using a string value to identify it).
  • Recall the saved slider value whenever the script is loaded or, if there isn’t a saved value, load a default value instead.

Here’s how to use PlayerPrefs to save the volume slider value:

  1. Add the using UnityEngine.UI; namespace to the script.
  2. Declare a public Slider variable, call it slider or similar then connect it in the inspector
  3. In the SetLevel function, add a new line: PlayerPrefs.SetFloat(“MusicVolume”,sliderValue);
    This will save the value of the slider whenever it’s changed.
  4. In the Start function (please note that this will not work in Awake, add a new line: slider.value = PlayerPrefs.GetFloat(“MusicVolume”, 0.75f);
    It’s important to include the default value parameter (in my case I’ve used 0.75f). Otherwise the default for the variable type will be used which, for a float, is 0.

Here’s what the new script looks like:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;
public class SetVolume : MonoBehaviour {
    public AudioMixer mixer;
    public Slider slider;
    void Start()
    {
        slider.value = PlayerPrefs.GetFloat("MusicVolume", 0.75f);
    }
    public void SetLevel (float sliderValue)
    {
	mixer.SetFloat("MusicVol", Mathf.Log10(sliderValue) * 20);
        PlayerPrefs.SetFloat("MusicVolume", sliderValue);
    }
}

Note that changing the slider value in Start also triggers the SetLevel function, so it updates the mixer to the correct loaded value too.

Why does the saved volume only load when I open the settings menu

In this example, the SetVolume script has been added to the Slider object itself.

In an isolated example this is fine, however, if, like many people, you show and hide the menu settings panel by disabling its parent object, then this will mean that the Slider script will not load when the Scene is first loaded and will instead only load once the player navigates to the menu panel.

The easiest way to fix this is by moving the SetVolume Script to an object that will not be disabled at the beginning of the Scene.

This will still work, setting both the Slider Value and the Audio Mixer Group correctly, even if the Slider object is disabled.

Slider Settings Example Unity

Adding the Set Volume Script to a different object means that it loads the saved setting at the Start of the Scene, not only when you open the menu.

Slider Dynamic Float option missing in Unity 2019.2.5? Here’s How to fix it

If you don’t see the dynamic float option when selecting the On Value Changed event on the slider, check first that the Set Level function takes only a single float parameter, as this is usually the issue.

At the time of writing this, however, there’s a suspected issue in Unity 2019.2.5 where the Dynamic Float option just doesn’t appear.

You will still be able to select the Set Level function and it still fires every frame, it’s just not using the live slider value, instead it only passes a static parameter to the script, which is no good.

It’s more than likely that this is just a bug and will be fixed in a patch, or in a future version of Unity. Luckily, however, it can easily be fixed right now too, by slightly modifying the script.

How to fix the dynamic float issue in Unity 2019.2.5

In the example in this article, the SetLevel function takes the slider value as a parameter. This only works when you select the function from the Dynamic Float section of the list. As this option appears to be missing in Unity 2019.2.5 we instead need to remove the parameter from the function and just retrieve the slider value ourselves instead.

Here’s what the modified script looks like:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.UI;
public class SetVolume : MonoBehaviour
{
    public AudioMixer mixer;
    public Slider slider;
    void Start()
    {
        slider.value = PlayerPrefs.GetFloat("MusicVolume", 0.75f);
    }
    public void SetLevel()
    {
        float sliderValue = slider.value;
        mixer.SetFloat("MusicVol", Mathf.Log10(sliderValue) * 20);
        PlayerPrefs.SetFloat("MusicVolume", sliderValue);
    }
}

This new version of the script isn’t passed the slider value like it was before.

Instead it just retrieves it by getting slider.value at the start of the function, leaving everything else the same.

by John Leonard French

Game Composer & Sound Designer

Comments

81 Comments on “The right way to make a volume slider in Unity (using logarithmic conversion)”

      1. This is beautiful, and I’m so glad I found this.

        I’m having a bit of a problem with the initial setting, though. The first time the screen opens, the slider is set to min. After that, it works fine.

        Slider is assigned in inspector, min value 0.0001, max value 1, default value in inspector 0.712.

        In the script, I have:
        music.value = PlayerPrefs.GetFloat (“Music”, 0.712f);

        I’m not seeing my mistake. :/

        Thanks.

        1. The default value parameter only gets used if there’s no PlayerPref value already set. Is it possible you’ve previously set the PlayerPref value (it persists, even in the editor) and it’s not being updated. Feel free to email [email protected] with details.

  1. I was curious if you had a similar scaling function for a Low Pass filter cutoff. Since the values are in Herz and not decibels, the numbers are different but I can’t quite figure it out. Thanks!

  2. Just a heads up, using 0.001 as the slider minimum will only allow the minimum volume to go to -60 dB. You’ll want an extra zero in there to make it go all the way to -80 dB.

    log(0.001) = -3 * 20 = -60
    log(0.0001) = -4 * 20 = -80

    1. Thanks for this. I actually hadn’t experienced this issue at all with Math.Log and a minimum value of 0.001 however this does happen when using Log10, as has been suggested in another comment so thank you for your advice, I’ve updated the article.

    1. Thanks for this, I’ve now tried this and I’ve updated the article to use Log10 instead as, when used with a minimum value of 0.0001, which Marshall suggested in another comment, creates a slightly smoother slider travel than before with an exact -80db lower limit (my original method would bottom out at more than -100db). Thanks for your advice.

  3. Hi,

    Thanks for this article.

    Quick question – why prevent going above 0db on the audio mixer? The above solution is great but means that 100% on the slider is equal to 0db in the mixer.

    Rather than the game offering 0db as a maximum, what if I wanted to offer +20db as the maximum (100%) but set defaults to whatever percentage 0db would be. This would give the player the ability to go louder, or quieter.

    I’m not particularly familiar with all things acoustic, so my apologies if this is a daft question, or if I’ve missed something obvious.

    Thanks in advance for any response 🙂

    1. Hi there, to answer your question 0db is the upper limit for digital audio. An audio signal that tries to go above 0db will clip and distort (where clipping is the flattening of an audio signal).

      The fader value only represents how much an audio signal is attenuated or increased so a single audio signal passing through a fader that’s set to 0db would play at its original level, meaning it can’t clip. If you were to increase the fader level above 0db, then the signal runs the risk of clipping, depending on how loud the original audio signal is.

      Put simply, you can increase the fader above 0db without clipping but, in most cases, you shouldn’t. If you’re finding that your audio signal is too low at 0db then it’s likely that the audio file itself is too quiet.

      Hope that helps!

      1. Hi, John.
        Even with the understanding of the 0dB clipping, my application do need that the user will be allowed to go to +6 or +10dB for example.
        Just to compensate some volumes that originally sound good together (as they were saved to wav), but the user might want to compensate and have some of them sounding louder instead of lowering all others and pumping up the master.

        Is there a suitable way to adapt the code for this?
        I tried to have the max slider value at 1.6 or 2, but then the movement becames not so smooth again.

        1. To answer your question, by changing the max slider value you’re essentially changing the factor of the max volume of the slider. So the movement of the linear slider is still correct, it just looks overly sensitive on the fader because the small amount of extra gain actually represents a massive jump in amplitude. For example, with a max slider value of 1, you get 0db, or unity gain. With a max of 2, you get a max amplitude of about +6db, which is basically double volume, meaning that 0db will now be at 0.5 on the slider. To get +10 db, you need around 3, which is 3 times louder and to hit the top of the Unity slider, +20db, you’d need to change the max slider value to 10. Ten times louder than unity gain.

  4. Hi John, thank you for such a nice tutorial but I have a question. So, I previously had all my child mixers mixed to the levels I wanted and by doing this I had to get rid of it. For instance, I want one of them to start at -15dB and for that value to be the max possible at all times, so kind of equivalent to 0dB on the slider. I’ve played around but haven’t managed to find a good way to do it yet. Could you please recommend a solution? Thank you!

    1. Hi Bruno, it’s possible to nest entire Mixers and Mixer Groups within one another which routes the audio through them. In theory you could keep all of your current mixers and settings as you want them and route the output into a separate mixer that’s just for player volume control. A -15db signal that’s routed into a fader set at 0db will stay at -15db, as no gain or attenuation is applied, making -15db the maximum for that signal in this example. I don’t know what’s best for your specific set up without seeing it but if you do want any advice email [email protected] or keep an eye out for my upcoming post on managing Audio Mixers in Unity.

  5. Hello. This is such a nice tutorial but I also have a question. It’s about the OnValueChange. Well, I followed exactly what is stated in your tutorial, but in my dropdown function, only “No function” and “MonoScript” were the only choices available. Could you please recommend solution? Thank you in advance.

    1. It sounds like you may have added the script asset from the project folder to the slider object, instead of the instance of the script that’s on the slider gameobject in the scene.

      If you’ve added the SetVolume script to the slider, as this example, be sure to drag that same instance of the script to the OnValueChange field.

      Hope that helps,
      John.

      1. Thank you for this. It helped me resolve my problem. But I have another question if you don’t mind. I’m really new to Unity and using this to create a school project that’s why. Lol. So here is my other question, I was wondering about how to this one… “Declare a public Slider variable, call it slider or similar then connect it in the inspector”. How do you connect the variable to the inspector? Thank you once again. 🙂

        1. Also, last question. I have another error regarding the “value” in slider.value(PlayerPrefs.GetFloat(“musicVol”, 1.0f));.

          Error is: “Non-invocable member ‘Slider.value’ cannot be used like a method”

          Can you help me out again with this? It would really be another big help. Thank you

          1. The error was a typo in the article (my bad) I’ve updated the article. The line needs to be:

            slider.value = PlayerPrefs.GetFloat(“MusicVol”, 0.75f);

        2. The easiest way to do this is to select the object with the script on it, which may also be the slider, and then click and drag the slider object from the scene hierarchy to the slider variable field. You can also use the circle select button to select the slider (remember to select the scene tab, not assets).

  6. I was looking fo this info six months ago and found nothing as straightforward as this. Googled randomly again tonight and this popped up, not only answering my logarithmic slider question in general but also sending me to improve my audio sliders. Thanks for the helpful advice 🙂

  7. Im new in C# so can you please help me with this errors?

    Assets/Scripts/MenuScripts/setVolume.cs(12,12): error CS0547: `setVolume.Start’: property or indexer cannot have void type

    and

    Assets/Scripts/MenuScripts/setVolume.cs(14,9): error CS1014: A get or set accessor expected

  8. Hi John,
    My music and sfx sliders work great using your article!.. But they only work in editor – in pc and mobile builds the sliders make no difference at all.. any ideas? I am on Unity 2017.3.0f3- could be a bug in that version maybe..
    Thanks
    Alex

    1. Hi Alex, I expect there’s possibly something else going on that’s causing it, but I’m not aware of any bug. If you want, email [email protected] and I’ll try to help you out.

      All the best,
      John.

  9. Hi,

    it works perfectly for me, thanks for this.
    I have just a question how you change percentage text when you move slide.
    I don’t know how do it, I’m not familiar with audio mixer.

    Thanks !

    1. Hi Linetaru, in this example the percentage is coming from the slider value, not the Audio Mixer. You’d be able to recreate this with something like the below script added to a Text object, then call it from the On Value Changed event on the Slider object :

      using System.Collections;
      using System.Collections.Generic;
      using UnityEngine;
      using UnityEngine.UI;
      public class UpdateText : MonoBehaviour {
      	public Text textObject;
      	void Start () {
      		textObject = this.GetComponent<Text> ();
      	}
      	public void UpdateTextObject (float value) {
      		textObject.text = "Percentage : "+ Mathf.Round(value*100) + "%";
      	}
      }
  10. Hello! Amazing tutorial! But I just have one question… how do I make it so the volume plays throughout all scenes?

    Thanks,
    Daxen.

  11. Great article. Unity official tutorials should definitely cover such details. You described how to convert slider value into audio mixer value. But what if I want to convert AudioMixer dB value of -80-0 range into the slider 0,0001-1 range (to display current volume level in UI, for example)?

    float dbVolume;
    audioMixer.GetFloat(“VolumeLevel”, out dbVolume);
    slider.value=//Magical math on dbVolume valiable?

  12. Ok, after some time googling abount logarithmes I found the solution which seems to work for me:

    float dbVolume;
    audioMixer.GetFloat(“VolumeLevel”, out dbVolume);
    slider.value=Mathf.Pow(10, dbVolume/ 20);

    1. Glad you found the answer. I’ll update the article to include this, I’ve been asked about it a couple of times now. Thanks!

  13. I’m trying to use an Enum to drive all channels with one script 🙂

    [code]
    public enum mixChan { VolumeMaster, VolumeFX, VolumeMusic }
    public mixChan MixChan;

    public void SetLevel(float sliderValue)
    {
    mixer.SetFloat(MixChan.ToString(), Mathf.Log10(sliderValue) * 20);
    PlayerPrefs.SetFloat(MixChan.ToString(), sliderValue);
    }

    [/code]

  14. Hi,

    I have unity3d version 2019.2.5f1 Personal.
    And in the On Value Changed when I select the function SetLevel there is no Dynamic option. I can choose the regular function SetLevel but not dynamic.

    I tried to uninstall and install older version of the unity3d editor 2018 version and then I have the Dynamic option but in the latest version it seems like the Dynamic option is not exist anymore.

    Am I doing something wrong ?

    This is my script now since the Dynamic is not exist :

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.Audio;
    using UnityEngine.UI;

    public class SettingsMenu : MonoBehaviour
    {
    public AudioMixer audioMixer;

    [SerializeField]
    private Slider _volumeSlider;

    [SerializeField]
    private Dropdown _dropDownQuality;

    public void SetVolume()
    {
    float volume = _volumeSlider.value;
    audioMixer.SetFloat(“Volume”, volume);
    }

    public void SetQuality()
    {
    int qualityIndex = _dropDownQuality.value;
    QualitySettings.SetQualityLevel(qualityIndex);
    }
    }

    But I wonder what happened the Dynamic option in the 2019 version ?
    Or maybe I have a bug in my editor ? But again when I installed older 2018 version it does have the Dynamic.

    The script is working fine.

    I just want to make sure the problem is not in my side.

    Thank you for the help.

    1. Hi Daniel, you’re absolutely right. The Dynamic float option doesn’t seem to be working in 2019.2.5 as yet. My guess is that this is a bug, as it would remove a fairly important function of the slider – passing the value to a script. I’ll try to find out and update this comment thread if I hear anything.

  15. Thanks for the tip about dynamic variables not working in 2019 2.5, would never have figured that one out by myself 😉

  16. Hey, I had a small workaround for the function not appearing—

    My slider was on a Canvas. So I attached the script to the Canvas, not the slider.

    Then I could reference the script from the OnValueChanged dropdowns.

  17. For those who do want to go above zero dB (even though there is a risk of clipping), it’s pretty easy to do.
    -80 db = 20 * Log10(0.0001)
    0 db = 20 * Log10(1.0)
    20 db = 20 * Log10(10.0)

    So if you want to match the range of values you see in the volume sliders of the AudioMixer (-80 to +20 dB) just have the linear range of your slider go from 0.0001 to 10.0

  18. Hello John!
    I quite recently found your tutorial and it was a godsend when I needed a solution to adjust the volume.

    However I have a problem with “Playerprefs” where I adjust the volume in my “Settings” menu, go to a new scene (the scene where my game takes place), return to main menu/settings, the changed volume has reverted back to full and the volume doesn’t change at all when attempting to adjust it.

    Here’s a pastebin of my code: https://pastebin.com/g65vt5Vi

    1. it’s because your line 47, you shouldn’t add that 1.0f behind the string musicVol, just write it like this:
      musicSlider.value = PlayerPrefs.GetFloat(“musicVol”);

  19. Hey I’ve been dealing with this problem almost a month! Thankyou! I am very thankful. Godbless!

    1. There could be all kinds of reasons you’re getting no audio. To fix it I’d suggest you start with a basic audio source, no mixer, no fader, no volume changes etc. and then add things back in until you find the cause, as it may be unrelated (particularly if it’s working on other platforms?). If you can share more details email [email protected] and I’ll try to help.

  20. Dumb Question: (I’m very bad at programming), how do I do different sliders for different channels? (Master, Music, SFX)??

    1. Create different groups on the mixer for each and then repeat this but with a different exposed parameter name for each of the mixer groups. Hope that helps.

  21. This is only true for the `AudioMixer`. If you set the volume to the `AudioSource`, there will be no problems.

  22. Hi John! Awesomely useful article, thank you! I was wondering if you knew how to go the opposite direction, i.e. from the mixer’s actual volume to a 0..1 range suitable for a slider?

    1. Yes, it’s slider.value = Mathf.Pow(10, faderValue/ 20);

      Which Efril kindly pointed out in an earlier comment.

    1. If you’re using a menu volume slider that exists in both Scenes, this may be resetting when you change between them which, in turn, will update the audio volume. Either use DontDestroyOnLoad to carry your menu over to the new scene or use a PlayerPref to load the value at the start of the new scene.

  23. Thanks for the article. Everything works as it should. But I found a bug. If i set the slider to 0, then after restarting the scene or game, the volume becomes maximum, although the slider is set to zero. Do you have the same or did I make a mistake somewhere?
    Fixed it with an extra if check, but would like to do without it.

  24. Thank you for sharing this article, it was helpful!
    I have one issue however that I am unable to solve. I have a video running with sound in my game and the Audio Mixer does not seem to affect the video volume at all. Do you know how I could control the volume of VideoPlayer.SetDirectAudioVolume from the slider? Thank you for your help.

Leave a Reply

Your email address will not be published. Required fields are marked *