【VRChat】Creating a Mirror System: ON/OFF Toggle and Quality Settings

Created: 2025-12-19

Essential mirror control methods for VRChat worlds. VRCMirrorReflection on/off toggle, Reflect Layers, and quality preset implementation via resolution.

Overview

The mirror that allows players to see their own avatar is a very important object in VRChat's social experience. Many users look for a mirror first when entering a world. However, mirrors are also one of the elements that have the greatest impact on world performance.

Therefore, implementing mirrors not just by placing them, but enabling them only when needed and allowing performance/quality balance adjustment, is essential for creating comfortable worlds.

This article explains how to control the VRCMirrorReflection component with UdonSharp to build a practical mirror system with on/off functionality and quality settings.

Step 1: Setting Up the VRCMirrorReflection Component

  1. Create Object: Create a Quad in the scene to serve as the mirror's reflective surface. Quad faces forward (Z+) by default, so it can be used as a vertical mirror without rotation. Note: If you use a Plane and rotate X by -90 degrees, the mirror image may appear upside down.
  2. Add VRCMirrorReflection: Add the VRCMirrorReflection component to the created object. This alone makes the object function as a mirror.
  3. Set Initial State: For performance, it's common to have the mirror GameObject inactive by default. That means unchecking the checkbox at the top left of the Inspector.

Key VRCMirrorReflection Settings

VRCMirrorReflection component settings
  • m_ReflectLayers: Selects which layers of objects to reflect in the mirror. By default, it reflects everything, but limiting it to just the Player layer, for example, creates a mirror that only reflects player avatars (useful for dance practice), greatly reducing load. Note: The Water layer is not rendered in mirrors.
  • m_DisablePixelLights: When true, pixel lights (Point Lights, etc.) are disabled in mirror rendering, reducing load.
  • Mirror Resolution: The mirror's resolution setting. Auto matches the user's HMD or monitor resolution, but is limited to a maximum of 2048×2048. For VR, this is per-eye resolution. Lower values reduce load but result in coarser image quality. Unlimited has no limit but is very high load, so use with caution.

Example 1: Simple ON/OFF Button

A mirror with the most basic local control where clicking a button toggles the mirror's GameObject active/inactive. The mirror state only changes in each player's own environment and doesn't affect other players.

Mirror ON/OFF button demo

Unity Setup

  1. Create a mirror object and set it to inactive by default.
  2. Place a button (e.g., Cube) in the scene to control the mirror, and add Collider and Udon Behaviour. Set the Interaction Text to something like "Mirror ON/OFF".

Script: LocalMirrorSwitch.cs

using UdonSharp;
using UnityEngine;

public class LocalMirrorSwitch : UdonSharpBehaviour
{
    [Tooltip("Assign the mirror's GameObject to control.")]
    public GameObject mirrorObject;

    public override void Interact()
    {
        if (mirrorObject != null)
        {
            // Toggle the mirror GameObject's active state
            mirrorObject.SetActive(!mirrorObject.activeSelf);
        }
    }
}

Final Unity Setup

  • Assign LocalMirrorSwitch.cs to the button's Udon Behaviour.
  • Drag & drop the inactive mirror GameObject onto the script's Mirror Object field.

Now, each player can turn the mirror on/off for themselves at their own timing. This is essential functionality that should be in every world.

Example 2: Mirror System with Quality Settings

A more advanced mirror system that not only toggles on/off but allows selection of quality levels like "High," "Medium," and "Low." The layers reflected in the mirror change dynamically based on quality.

Multi-button mirror quality settings

This implementation uses two scripts: a manager script and a button script.

Script 1: MirrorSystemManager.cs (Manager)

The central script that manages mirror state and quality settings.

using UdonSharp;
using UnityEngine;
using VRC.SDK3.Components;

public class MirrorSystemManager : UdonSharpBehaviour
{
    [Tooltip("VRCMirrorReflection component of the mirror to control")]
    public VRCMirrorReflection mirror;

    // Layer mask definitions
    private int highQualityLayers = -1; // All
    private int mediumQualityLayers = (1 << 9) | (1 << 10); // Player and PlayerLocal only
    private int lowQualityLayers = (1 << 10); // PlayerLocal (self) only

    public void ToggleMirror()
    {
        if (mirror != null)
        {
            mirror.gameObject.SetActive(!mirror.gameObject.activeSelf);
        }
    }

    public void SetQualityHigh()
    {
        if (mirror != null)
        {
            mirror.m_ReflectLayers = highQualityLayers;
        }
    }

    public void SetQualityMedium()
    {
        if (mirror != null)
        {
            mirror.m_ReflectLayers = mediumQualityLayers;
        }
    }

    public void SetQualityLow()
    {
        if (mirror != null)
        {
            mirror.m_ReflectLayers = lowQualityLayers;
        }
    }
}

Script 2: MirrorButton.cs (For Each Button)

Attach to each button to call the manager's methods when clicked.

using UdonSharp;
using UnityEngine;

public class MirrorButton : UdonSharpBehaviour
{
    [Tooltip("Reference to MirrorSystemManager")]
    public MirrorSystemManager mirrorSystem;

    [Tooltip("Button action: 0=ON/OFF, 1=High, 2=Medium, 3=Low")]
    public int actionType;

    public override void Interact()
    {
        if (mirrorSystem == null) return;

        switch (actionType)
        {
            case 0:
                mirrorSystem.ToggleMirror();
                break;
            case 1:
                mirrorSystem.SetQualityHigh();
                break;
            case 2:
                mirrorSystem.SetQualityMedium();
                break;
            case 3:
                mirrorSystem.SetQualityLow();
                break;
        }
    }
}

Unity Setup Steps

  1. Manager Setup:

    • Create an empty GameObject (e.g., MirrorSystemManager) and attach MirrorSystemManager.cs
    • Assign the mirror's VRCMirrorReflection component to the Mirror field
  2. Button Setup (for each of the 4 buttons):

    • Create a button using a Cube, etc., and attach Collider and MirrorButton.cs
    • Assign the manager object above to the Mirror System field
    • Set Action Type:
      • ON/OFF button → 0
      • High Quality button → 1
      • Medium Quality button → 2
      • Low Quality button → 3

m_ReflectLayers is of type LayerMask, but since it can't be handled directly in Udon, we construct the layer mask as an int type using bit operations. 1 << layer number creates a mask containing only that specific layer, and | (bitwise OR) combines them.

Summary

  • Mirrors are very high load, so it's strongly recommended to have them off by default and implement functionality for players to optionally turn them on/off.
  • The simplest implementation is toggling the mirror GameObject's SetActive() in the button's Interact. Local processing is sufficient for this.
  • By changing the m_ReflectLayers property of VRCMirrorReflection from Udon, you can dynamically control what objects the mirror reflects. This enables quality settings based on performance and use case.
  • Handling layer masks in Udon requires knowledge of int types and bit operations.

Consideration for performance is essential for providing a comfortable VRChat experience. Mirrors, in particular, are one of the elements with the greatest impact. Implementing a user-friendly mirror system can be considered an important responsibility of world creators.