Player Chimes and Toggle Buttons in VRChat
Add an entry chime to a doorway or zone, then build buttons that switch lights, audio, signs, or other scene objects. The video demonstrates the CyanTrigger setup; this guide explains when the result should stay local, when it should be synchronized, and how to keep shared toggles correct for players who join later.
Make the chime and one toggle work locally before adding shared state or network events.
- Create a trigger zone with one spatial Audio Source for the chime.
- Create a physical button with a collider and Udon or CyanTrigger interaction.
- Choose local or synchronized behavior, then test with two clients.
A personal accessibility or preference toggle should usually stay local. A light switch or sign that represents shared world state should be owner-controlled and synchronized. A short chime heard by players currently in the instance can use a network event because it does not need to persist.
Video Companion
Creator: Fionna
Video: Player Chime and Toggle Buttons
Choose Local or Shared Behavior
Use the smallest networking model that matches the interaction.
| Interaction | Recommended behavior | Why |
|---|---|---|
| Entry sound heard only by the arriving player | Local audio | No other client needs the event |
| Door chime heard by everyone nearby | Network event to current players | The sound is temporary and does not need late-join recovery |
| Personal music, mirror, comfort, or accessibility toggle | Local state | Each player can keep a different choice |
| Shared light, door, sign, or scene mode | Synchronized variable | Every player, including late joiners, needs the current value |
Network events execute once for players currently in the instance and are not replayed for late joiners. Synchronized variables retain their latest value and are delivered to late joiners.
Scene Layout
Separate the entry detector, audio source, controls, and controlled objects:
PlayerChimeAndToggles
├── EntryChime
│ ├── EntryTrigger
│ │ ├── Box Collider (Is Trigger)
│ │ └── Udon Behaviour or CyanTrigger
│ └── ChimeAudio
│ ├── Audio Source
│ └── VRC Spatial Audio Source
├── Controls
│ ├── LocalToggleButton
│ └── SharedToggleButton
├── SharedStateController
└── ControlledObjects
├── Lights
├── Sign
└── AmbientAudio
Use a child object for the chime Audio Source so its position and falloff can be adjusted independently from the trigger volume.
Build the Player Chime
Add a collider to EntryTrigger, enable Is Trigger, and size it across the doorway or zone. VRChat recommends OnPlayerTriggerEnter for detecting when a player's capsule enters a trigger collider.
For a local-only chime:
OnPlayerTriggerEnter(player)
↓
Is player the local player?
↓ yes
Play ChimeAudio locally
Checking player.isLocal prevents the same client from treating remote players as its own entry. It also gives you one clear place to add a short local cooldown if the player moves repeatedly across the trigger boundary.
For a shared chime:
OnPlayerTriggerEnter(player)
↓
Is player the local player?
↓ yes
Send one network event to all current players
↓
Each client plays ChimeAudio once
The local-player gate is important: without it, multiple clients can report the same player crossing and produce duplicate sounds.
VRChat documents that player trigger events can be skipped when a player teleports across a trigger or moves through it very quickly. Use the chime as feedback, not as the only source of important game state or access control.
Configure Spatial Audio
Add VRC Spatial Audio Source to the chime's Audio Source. VRChat uses it to add 3D spatialization and provides Near and Far gizmos for judging the audible area.
| Setting | Practical check |
|---|---|
| Play On Awake | Disable it so the chime waits for the trigger |
| Loop | Disable it for a one-shot entry sound |
| Near and Far | Keep the sound focused around the doorway or zone |
| Position | Place the source where the chime should appear to originate |
| Clip level | Leave headroom and test beside the source as well as near the falloff edge |
VRChat recommends spatial audio for real-world point sources. Check the gizmos in the Scene view, then test the result in VRChat because listener position and avatar scale can affect how the falloff feels.
Build a Physical Toggle Button
A physical world interaction needs a collider and an Udon Behaviour on the interactive GameObject. The Interact event fires for the local player who uses that object.
ToggleButton
├── Mesh Renderer
├── Collider
└── Udon Behaviour or CyanTrigger
Keep the interaction collider large enough for Desktop, VR, and mobile users to target without overlapping the next button. Use button text and a visible state indicator rather than relying only on color.
If the control is a Unity UI button instead of a physical scene object, VRChat's event documentation directs creators to use VRC UI Shape with the Button component.
Create a Local Toggle
For a local preference, keep a normal unsynchronized Boolean:
Interact
↓
localEnabled = not localEnabled
↓
ApplyLocalState
├── Set local object active/inactive
├── Update button label
└── Update indicator
Do not send a network event or transfer ownership. Each client runs its own copy of the interaction and may choose a different value.
Suitable local controls include:
- personal ambient audio;
- a local-only mirror or visual effect;
- comfort effects;
- optional instructions or interface panels.
Create a Synchronized Toggle
For a shared object, store the value on one controller using a manually synchronized Boolean. Only the owner can change synchronized variables.
Player interacts with SharedToggleButton
↓
Request reaches SharedStateController owner
↓
Owner reverses sharedEnabled
↓
Owner calls RequestSerialization
↓
All clients apply sharedEnabled
Use the received Boolean to update the controlled objects and every copy of the button indicator. Reapply it during deserialization so a late joiner sees the current state immediately.
Do not use only “Toggle Active” as a network event for persistent state. A player who joins after that event would not know whether the object should currently be on or off.
Keep Presentation and State Together
Use one ApplyState action or method for all visual and audio changes:
ApplyState
├── Enable or disable the controlled object
├── Set button label to On or Off
├── Set indicator material, sprite, or animation
└── Start or stop the intended audio
Call it after a local toggle, after the owner changes shared state, and whenever synchronized data is received. This prevents the object, label, and indicator from disagreeing.
For an Audio Source, explicitly decide whether “off” pauses, stops, mutes, or disables it. Those actions produce different results when the source is enabled again.
Prevent Chime and Button Spam
Use a short local cooldown for repeat chimes and reject button presses while a shared request is already being processed. The cooldown should suppress accidental rapid repeats without making the control feel unresponsive.
Also check:
- the entry trigger does not overlap the player's spawn point;
- neighboring button colliders do not overlap;
- the chime is not triggered again by standing still inside the volume;
- a shared button cannot send an unlimited stream of network events;
- an indicator shows when the requested state has actually been accepted.
Multiplayer Test Matrix
Test locally first, then use a VRChat instance with at least two clients.
| Test | Expected result |
|---|---|
| Player A enters the local chime zone | Only Player A hears the chime |
| Player A enters the shared chime zone | Every current client hears one chime |
| Player A moves across the boundary repeatedly | The cooldown prevents a burst of duplicate sounds |
| Player A uses a local toggle | Player B's setting does not change |
| Player A uses a shared toggle | Both clients show the same object and indicator state |
| Player B joins after a shared toggle | Player B immediately receives the current state |
| Shared controller owner leaves | The replacement owner preserves the current value |
| Desktop, VR, and mobile input | The button can be targeted and activated reliably |
Help! The chime plays several times for one player.
Gate the trigger with player.isLocal before sending a shared event, and add a short local cooldown. Confirm that duplicate trigger colliders are not covering the same doorway.
Help! The chime plays as soon as the world loads.
Disable Play On Awake on the Audio Source. Also move the entry trigger away from the spawn position so joining players do not begin inside it unless that behavior is intentional.
Help! The chime can be heard across the whole world.
Confirm the Audio Source has a VRC Spatial Audio Source, then inspect its Near and Far gizmos. Keep the source spatialized and set its audible range around the intended doorway or zone.
Help! The physical button does not show an interaction prompt.
Confirm the interactive GameObject has both a collider and an Udon Behaviour or CyanTrigger. Move the collider in front of decorative geometry and verify it is on an active GameObject.
Help! The shared toggle changes for only one player.
Check that the owner changes the synchronized Boolean and calls RequestSerialization() on a manually synchronized behaviour. Every client should apply the received value instead of changing only local objects.
Help! Late joiners see the default toggle state.
Store the state in a synchronized variable rather than relying on a past network event. Apply the synchronized value when deserialization runs.
Help! The object state and button label disagree.
Update the object, label, and indicator through the same ApplyState action. Call it after every accepted change and after synchronized data arrives.
Official References
- Udon event nodes
- Player collision and trigger events
- VRChat Player API
- VRC Spatial Audio Source
- VRChat networking
- Network events
- Network variables
- Late joiners and synchronization
Related Guides
- Collision Audio with CyanTrigger
- World Settings Menu
- Udon Networking Decision Guide
- ClientSim World Testing
- VRChat Prefabs
- World Creation
Topics: VRChat worlds, CyanTrigger, Udon, interactions, spatial audio, networking