Run a VRChat World Optimization Pass
An optimization pass begins with evidence. Capture the problem in a repeatable scene, determine whether CPU time, GPU time, memory, loading, or build size is limiting the world, then change one relevant system and measure again.
This guide organizes the repair work. If you need an introduction to the Unity Profiler and frame timing first, read Measure and Improve VRChat World Performance.
Use the same location, camera route, active features, and platform before and after every change.
- Reproduce the slow scene and capture CPU, GPU, rendering, and memory evidence.
- Change one cost category that the evidence identifies.
- Repeat the capture and keep the change only when the target build improves without breaking the world.
Unity Editor Play Mode is useful for quick comparisons, but Unity documents that Editor profiling is only an approximation. Verify important changes in VRChat on the intended PC, VR, Android, or Quest hardware and repeat tests with representative avatars and world features active.
Define the Test Scene
Write down the conditions before collecting numbers:
- Unity scene and world version.
- Build target: Windows or Android.
- Test device and graphics settings.
- Starting position and camera route.
- Active mirror, video, particle, lighting, and post-processing states.
- Number and type of test clients or representative avatars.
- Whether the capture is Unity Play Mode, a development player, ClientSim, or VRChat Build & Test.
Use the same conditions for the comparison. A result from an empty desktop scene cannot replace a test of a crowded mirror area in VR.
Capture a Baseline
Open Window > Analysis > Profiler and record the problem long enough to include both normal frames and the slowdown. Start with the modules related to the suspected issue instead of enabling every profiling option.
| Evidence | What it helps identify |
|---|---|
| CPU Usage | Scripts, physics, animation, rendering submission, and main-thread stalls |
| GPU Usage | Expensive rendering work when GPU profiling is available |
| Rendering | Draw calls, batches, triangles, vertices, and rendering activity |
| Memory | Texture, mesh, audio, managed, and native memory pressure |
| Game view Statistics | A quick view of batches, SetPass calls, geometry, and frame information |
| Frame Debugger | The ordered rendering events that construct one frame |
Do not enable Deep Profile by default. Unity warns that it adds overhead. Use normal samples and targeted call stacks first.
Record the original capture or write down the relevant values before editing the scene. A before-and-after comparison is more useful than an isolated number.
Identify the Limiting System
| Evidence | Likely area to inspect |
|---|---|
| CPU frame time is high | Scripts, physics, animation, many active objects, or CPU rendering submission |
| GPU frame time is high | Resolution, shaders, transparency, shadows, mirrors, post-processing, or excessive visible geometry |
| Batches or SetPass calls are high | Material changes, many renderers, multiple passes, lights, shadows, or poor batching opportunities |
| Texture memory is high | Oversized source textures, platform overrides, uncompressed formats, lightmaps, or duplicated content |
| Spikes occur during interaction | Object activation, instantiation, video, network events, physics, or script work |
| Only one room is slow | Visibility, mirrors, lights, particles, video, or dense content local to that room |
| Only Android is slow | Mobile shaders, texture formats, transparency, geometry, lighting, or device memory limits |
CPU and GPU work can overlap. Confirm which frame time controls the result before committing to a large visual rewrite.
Pass 1: Render Less
Unity's graphics guidance recommends reducing the number of objects rendered when visibility is the problem.
Check:
- Camera far clipping distance.
- Rooms, walls, corners, and other scene structure that block visibility.
- Occlusion culling for static occluders and occludees.
- LOD Groups for objects whose detail can decrease with screen size.
- Renderer enable states for distant or optional areas.
- Particle and effect bounds that are much larger than their visible result.
Do not combine the entire world into one mesh. Large combined meshes can remain visible when only a small part is on screen and can reduce the usefulness of occlusion culling. Group geometry according to rooms, visibility, materials, lightmapping, and replacement needs.
After baking occlusion data, test doors, windows, moving objects, mirrors, and viewpoints near occlusion boundaries. Missing or flickering geometry is not a performance improvement.
Pass 2: Review Materials and Draw Calls
Each renderer and material pass can contribute rendering work. Material count alone is not the complete draw-call count, but unnecessary material slots and shader passes are useful places to investigate.
- Use Rendering Statistics and the Frame Debugger at the slow viewpoint.
- Identify repeated renderers and material changes.
- Remove unused material slots.
- Reuse the same material asset where objects genuinely share settings and textures.
- Atlas related textures when it reduces materials without creating an oversized, inflexible mesh.
- Enable GPU instancing only on compatible repeated meshes and materials, then verify that Unity actually batches them.
- Recheck shadows and additional light passes affecting the objects.
VRChat's Android optimization guide warns against combining world materials and meshes too aggressively because Unity still needs useful object boundaries for batching and occlusion. Measure the actual result.
Pass 3: Reduce Texture and Lightmap Memory
Select important textures and review their Import Settings:
- Max Size for each supported platform.
- Compression format and quality.
- Alpha channel requirement.
- Mipmaps for textures viewed at changing distances.
- Read/Write access, which should remain disabled unless a feature requires CPU access.
- Normal-map type for normal textures.
Continue with Optimize Texture Imports in Unity 2022.3 for exact importer steps.
Also inspect:
- baked lightmap count and resolution
- reflection-probe cubemaps
- video thumbnails and large UI textures
- duplicate texture files from imported packages
- textures included by unused scenes or prefabs
Crunch compression reduces downloaded or stored texture data, but VRChat's Android guide notes that it does not reduce the texture's in-memory size. Use platform texture formats and appropriate dimensions to address runtime memory.
Pass 4: Bake Static Lighting
VRChat recommends baked lighting for static world geometry and warns against overusing real-time lights.
- Mark eligible geometry appropriately for the selected baking workflow.
- Use baked or mixed lights according to the scene's actual dynamic-lighting needs.
- Use light probes for moving objects that must respond to baked surroundings.
- Remove decorative real-time lights that do not materially improve the scene.
- Reduce shadow distance, range, and shadow-casting objects where real-time shadows remain necessary.
- Check whether multiple pixel lights affect the same renderer.
- Rebuild lighting and inspect lightmaps after changing geometry, UVs, or LODs.
Measure both the visual result and frame time. Baking transfers work away from runtime lighting but adds lightmap textures and build data, so lightmap settings still require review.
Pass 5: Simplify Shaders, Transparency, and Effects
GPU cost depends on shader work, the number of passes, the number of affected pixels, and how often pixels are redrawn.
Inspect:
- transparent surfaces layered over each other
- full-screen or screen-space effects
- particles covering large areas
- animated or multi-pass shaders
- real-time reflections and reflection probes
- materials with unused features enabled
- effects visible through mirrors
VRChat's Android guidance recommends avoiding transparency where possible because it is costly on mobile GPUs. Use opaque or cutout solutions when they produce the required result.
Test custom shaders in stereo VR. A shader that looks correct in the Unity Game view may still be unsuitable for single-pass stereo rendering or the target mobile platform.
Pass 6: Control Mirrors, Video, and Extra Cameras
Mirrors and cameras render additional views. Video players add decoding, textures, audio, networking, and rendering work.
- Keep optional mirrors inactive until a player requests them or approaches.
- Limit mirror reflected layers and resolution.
- Avoid multiple active mirrors showing the same busy room.
- Disable unused video players rather than hiding only their display mesh.
- Stop video and audio work when the feature is not in use.
- Remove helper cameras that remain enabled after setup.
- Test the combined case when mirrors can reflect video, particles, avatars, or transparent effects.
Use Create and Optimize Mirrors for VRChat Worlds for the current VRC_MirrorReflection controls.
Pass 7: Inspect Scripts, Physics, and Animation
When CPU Usage identifies non-rendering work:
- Expand the expensive Profiler samples and locate the responsible system.
- Disable the suspected object in a test copy and capture again.
- Avoid work every frame when an event or state change can trigger it.
- Reduce unnecessary active rigidbodies, joints, colliders, constraints, animators, and particles.
- Prefer simple primitive colliders when they represent the required boundary.
- Check whether disabled visual objects still have active scripts, audio, physics, or animation.
- Review Udon networking separately when spikes correspond to ownership or synchronization.
Do not remove components based only on their presence. Capture the behavior and verify that the change affects the measured bottleneck.
Build Size and Runtime Memory Are Different
A smaller upload does not guarantee lower runtime memory or faster rendering.
| Change | Primarily affects |
|---|---|
| Crunch texture compression | Download or package size |
| Texture dimensions and runtime compression format | Runtime memory and bandwidth |
| Fewer shader variants or unused assets | Build size and loading, depending on what the build includes |
| Fewer visible renderers and passes | CPU/GPU rendering work |
| Baked lighting | Runtime lighting cost, plus lightmap storage and memory |
| Occlusion culling | Visible rendering work, plus occlusion-data storage |
Review build size, runtime memory, and frame timing as separate results.
Create an Android-Specific Pass
Do not publish the unchanged Windows world as the Android version.
For Android and Quest:
- Switch to the Android platform through the VRChat SDK workflow.
- Apply Android texture overrides and supported mobile materials.
- Bake lighting and remove unsupported or excessively expensive effects.
- Reduce geometry, material, texture, transparency, particle, and physics cost for mobile hardware.
- Build and test on an Android or Quest device.
- Confirm the compressed world remains within VRChat's current Android world-size limit.
VRChat currently documents a 100 MB maximum compressed world size for Android. Treat the official Android pages as the source of truth because limits and platform behavior can change.
Verify the Completed Pass
- The original slow scene was captured before changes.
- The limiting CPU, GPU, memory, loading, or build-size category was identified.
- Each retained change has a before-and-after result.
- Spawn, social areas, mirrors, video areas, and other hotspots were retested.
- Lighting, collisions, interactions, audio, and networking still work.
- Windows and Android builds were optimized and tested separately where both are supported.
- VR and desktop interaction paths were checked.
- Representative avatars and enabled world features were included in the final test.
- The project backup or version-control history can restore removed content.
Troubleshooting
The Profiler numbers change too much between tests
Use the same device, graphics settings, route, camera direction, world state, and active features. Capture several passes and compare representative sections rather than one isolated frame.
Play Mode improved, but the VRChat build did not
Editor profiling is approximate and does not reproduce the complete VRChat client. Recreate the test in Build & Test with the same mirror, avatar, video, and platform conditions, then investigate the limiting system shown there.
Draw calls increased after combining materials
Use the Frame Debugger to inspect the actual passes. Different shaders, keywords, light passes, shadows, material properties, or lost batching compatibility can prevent the expected reduction.
Occlusion culling makes objects disappear
Review the affected renderer's static flags, occlusion settings, bounds, and the baked occlusion data. Test openings and moving viewpoints, then rebake after correcting the scene setup.
The Android build is small but still uses too much memory
Package compression and runtime memory are different. Review texture dimensions, Android texture formats, mipmaps, lightmaps, meshes, and audio rather than relying on the compressed upload size.
Removing objects did not improve performance
The removed objects may not have contributed to the limiting system or may already have been culled. Return to the baseline capture, identify the largest relevant samples or rendering events, and test a change tied to that evidence.
Continue Learning
- Measure and Improve VRChat World Performance
- Optimize Texture Imports in Unity 2022.3
- Set Up LOD Groups in Unity 2022.3
- Bake and Test Occlusion Culling
- Create and Optimize Mirrors for VRChat Worlds
- VRChat World Optimization Checklist
Official References
- Unity 2022.3: Graphics performance fundamentals
- Unity 2022.3: Graphics performance and profiling
- Unity 2022.3: Profiler window
- Unity 2022.3: Frame Debugger
- Unity 2022.3: LOD Group
- VRChat: World creation and optimization tips
- VRChat: Android content optimization
- VRChat: Android content limitations
- VRChat: Cross-platform setup
- VRChat: Build and Test for Android Mobile
Next Step
Open Measure and Improve VRChat World Performance, capture a baseline at the slowest repeatable location, and select the first optimization pass from the limiting system.