You want visitors to be able to adjust the BGM volume, or set the lighting to their taste.
Building a settings panel like that needs UI floating inside the world. Follow Unity's standard steps, though, and you often end up with UI that's pressable in the editor but unresponsive in VRChat.
This article builds a panel that's reliably pressable in VRChat, calling Udon logic from a button and a slider.
What You'll Learn
- The four components that make UI pressable in VRChat
- The steps for calling an Udon method from a button
- How to receive a slider's value
- Causes of unresponsive UI, in order of frequency
Start from having tried methods and custom events.
Four components are required to make it pressable
UI built in Unity is clickable with the mouse in editor Play. Upload it to VRChat and it isn't.
That's because the component connecting VRChat's laser pointer to Unity UI is missing.

These four are needed.
| Component | Role | Where it goes |
|---|---|---|
| Canvas (World Space) | The panel holding the UI | You create it |
| Graphic Raycaster | Decides whether the pointer hit | Same object as the Canvas (added automatically) |
| VRC Ui Shape | Connects VRChat's pointer to Unity UI | Same object as the Canvas (you add it) |
| EventSystem | The receiver that distributes clicks | Exactly one per scene |
The most common failure is forgetting VRC Ui Shape. Without it you get the confusing state of pressable in the editor, unpressable in VRChat.
Adding it brings a Box Collider automatically. That Collider becomes the touchable surface, so you don't need to add your own.
Also, leave the layer at Default. Don't set it to UI. The VRChat client's own menu uses the UI layer, so putting it there stops the world's pointer from hitting it.
Exactly one EventSystem per scene. It's added automatically the first time you create UI. Multiple ones break things, so check that they haven't multiplied.
Hands-On: Build a volume and lighting panel
Build a panel with a slider for BGM volume and a button for toggling the lighting.
1. Place the panel
Create a "UI → Canvas" and name it ControlPanel.
| Field | Setting |
|---|---|
| Render Mode | World Space |
| Position | (0, 1.4, 2.8) |
| Rotation | (0, 180, 0) |
| Width / Height | 400 / 300 |
| Scale | (0.005, 0.005, 0.005) |
| Layer | Default |

Scale is small because a World Space Canvas treats one unit as one meter. Width 400 gives you a 400m panel. At 0.005 you get a realistic 2m by 1.5m.
Add VRC Ui Shape via "Add Component." Now it's pressable.
Confirm there's an EventSystem in the Hierarchy too. If not, it's added automatically when you create a UI element.
2. Place the slider and button
Right-click ControlPanel and add the following.
- Add a "UI → Slider," name it
VolumeSlider, and place it in the panel's upper half - Add a "UI → Button - TextMeshPro," name it
LightButton, and place it in the lower half
Set the Slider's Min Value to 0, Max Value to 1, and Value to 0.12, since it'll serve as the BGM volume.
Set the button's text to "Toggle lighting."
3. Write the code that receives the input
Create PanelController with "Create Empty" at (0, 0, 0).
In Assets/Scripts, choose "Create → U# Script" and make RoomControlPanel.
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class RoomControlPanel : UdonSharpBehaviour
{
[SerializeField] private AudioSource bgm;
[SerializeField] private Light roomLight;
[SerializeField] private Slider volumeSlider;
private bool lightOn = true;
private void Start()
{
ApplyVolume();
ApplyLight();
}
// Called from the slider. No arguments, so read the value yourself
public void OnVolumeChanged()
{
ApplyVolume();
}
// Called from the button
public void ToggleLight()
{
lightOn = !lightOn;
ApplyLight();
Debug.Log("[ControlPanel] light=" + lightOn);
}
private void ApplyVolume()
{
if (bgm == null || volumeSlider == null) return;
bgm.volume = volumeSlider.value;
}
private void ApplyLight()
{
if (roomLight != null) roomLight.enabled = lightOn;
}
}
The important part is that methods called from UI are public and take no arguments.
The slider's value doesn't arrive as an argument. The method reads volumeSlider.value itself. That's why it holds a reference to the slider.
4. Wire the UI to Udon
Add a Udon Behaviour to PanelController and set RoomControlPanel. Assign BGM, Room Light, and Volume Slider.
Now set up the calls from the UI side.

For the button:
- Select
LightButtonand press+under On Click () in the Inspector - Drag
PanelControllerfrom the Hierarchy into the empty field - Choose UdonBehaviour → SendCustomEvent from the dropdown on the right
- Type
ToggleLightinto the text field below
For the slider:
- Select
VolumeSliderand press+under On Value Changed (Single) - Drag
PanelControllerin - Choose UdonBehaviour → SendCustomEvent
- Type
OnVolumeChangedinto the text field
The method name is typed as text, so a misspelling means nothing happens. Compare it character by character with the method name in the code.
5. Confirm
Press Play and operate the panel.
| Action | Expected result |
|---|---|
| Press the button | The lighting goes off. Console shows light=False |
| Press again | It comes on |
| Move the slider | The BGM volume changes |
| Operate it from a distance | Too far and it doesn't reach |
Don't relax just because it works in ClientSim. A forgotten VRC Ui Shape only shows up in actual VRChat. Check with Build & Test too.
Causes of unresponsive UI
Listed in order of frequency.

- VRC Ui Shape isn't attached → Add it to the same object as the Canvas. When it's pressable in the editor but not in VRChat, start here
- The layer is
UI→ Set it back toDefault - No EventSystem, or several → Keep exactly one per scene
- Graphic Raycaster is gone → Check that it's on the same object as the Canvas
- The method name is misspelled → Compare the text in SendCustomEvent against the code
- The Canvas scale is extreme → Too large and the collision goes off. Aim around 0.005
If you change the Canvas size afterwards, the Collider added by VRC Ui Shape can be left at the old size. Remove and re-add it in that case.
Bonus: Good to Know Up Front
- Controls differ between VR and desktop: VR uses the controller's laser, desktop uses the pointer at screen center. Size things so both can press them. Buttons that aren't too small are a kindness
- This is local logic: Both the volume and the lighting change only on the operator's screen. Volume being per-person is the kinder design, so that's correct. Things you want shared go to networking basics
- Settings can be remembered for next time: A volume setting can carry over to the visitor's next visit. Covered in saving volume with PlayerData
- Watch the panel height: People playing seated have eye level down around 1.1m. A panel placed too high can't be operated from a seat
- It's useful all over: Volume control in social worlds, difficulty selection in games, language switching in exhibition worlds, lighting control at event venues. All built the same way
Summary
World-space UI only works with four components together.
- Canvas, Graphic Raycaster, VRC Ui Shape, EventSystem
- The layer is
Default. NotUI - In World Space, one unit is one meter. Adjust with Scale
- Buttons call methods by name with
SendCustomEvent. No arguments
The question to ask after building it is: "Was it pressable in VRChat?" Being pressable in the editor proves nothing.
To remember settings for next time, go to saving volume with PlayerData. To tune per-device usability, go to settings panels that work for everyone.