UI Volume Control Graphic

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

In Articles by John51 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, 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.

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.

Comments

  1. Avatar

    Thank you so much for this! It’s so obvious once you’ve explained it so clearly 🙂

      1. Avatar

        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. Avatar Author

          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.

  2. Avatar

    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!

  3. Avatar

    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. Avatar Author

      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. Avatar Author

      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.

  4. Avatar

    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. Avatar Author

      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!

  5. Avatar

    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. Avatar Author

      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.

  6. Avatar

    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. Avatar Author

      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. Avatar

        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. Avatar

          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. Avatar Author

            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. Avatar Author

          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).

  7. Avatar

    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 🙂

  8. Avatar

    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

    1. Avatar Author

      There was a typo in the article. Add a set of parentheses () after “void Start”, that will fix it.

  9. Avatar

    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. Avatar Author

      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.

  10. Avatar

    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. Avatar Author

      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 ();
      	}
      
      	public void UpdateTextObject (float value) {
      		textObject.text = "Percentage : "+ Mathf.Round(value*100) + "%";
      	}
      }
      1. Avatar

        Hi,

        It’s that I missed that, Mathf.round(value*100).
        Thanks it’s working.

  11. Avatar

    Hello! Amazing tutorial! But I just have one question… how do I make it so the volume plays throughout all scenes?

    Thanks,
    Daxen.

  12. Avatar

    RE: My last reply!

    Nevermind! I figured this out by using the DontDestroyOnLoad function!

  13. Avatar

    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?

  14. Avatar

    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. Avatar Author

      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!

  15. Avatar

    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]

  16. Avatar

    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. Avatar Author

      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.

  17. Avatar

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

  18. Avatar

    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.

  19. Avatar

    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

  20. Avatar

    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

Leave a Comment