Develop

Unity Shared Spatial Anchors sample

Updated: May 11, 2026

Overview

This sample demonstrates two approaches to sharing spatial anchors between Meta Quest devices for multiplayer mixed reality experiences. You explore user-based anchor sharing with Photon networking and group-based colocation sessions using Bluetooth discovery. The sample also demonstrates two methods for aligning players to a shared coordinate system.

What you will learn

  • Share spatial anchors to specific users by User ID with Photon PUN2
  • Discover and join colocation sessions using Bluetooth and share anchors to groups without Photon
  • Align multiple players to a shared coordinate system using MRUK world locking or classic tracking space transforms
  • Persist and reload anchors across sessions using local storage with cloud fallback

Requirements

  • A Meta Quest headset (Quest 2, Quest 3, or Quest 3S)
  • A Unity development environment configured for Meta Quest
For SDK versions, build configuration, and detailed setup instructions, see the sample README and Before you begin.

Get started

Clone the repository from Unity-SharedSpatialAnchors and open the project in Unity 2022.3.15f1 or later. The project includes three scenes: Menu for navigation, SharedSpatialAnchors for Photon-based user sharing, and ColocationSessionGroups for Bluetooth-based group sharing. The SharedSpatialAnchors scene requires a Photon App ID — see the sample README for registration and configuration. Build the project to your Quest device and launch from the Menu scene to select which workflow to explore. For detailed build steps, see the sample README.

Explore the sample

The sample includes three scenes and 21 scripts organized by sharing approach.
Scene/ScriptDemonstrates
Menu.unity
Main menu for scene selection, local data management, and exit
SharedSpatialAnchors.unity
User-based anchor sharing with Photon PUN2 networking
ColocationSessionGroups.unity
Group-based anchor sharing with Bluetooth colocation discovery
SharedAnchor.cs
Per-anchor share, save, erase, and alignment operations
SharedAnchorLoader.cs
Loading pipeline: local-first with cloud fallback
PhotonAnchorManager.cs
Photon room management and anchor ID exchange via Room CustomProperties
SharedAnchorControlPanel.cs
Left-hand UI for lobby, room panels, and anchor controls
ColoDiscoMan.cs
Advertising and discovering colocation sessions, group-based anchor sharing
ColoDiscoAnchor.cs
Per-anchor UI for group-based sharing workflow
Alignment.cs
MRUK world locking and classic tracking space alignment
LocallySaved.cs
JSON-based anchor UUID persistence per scene
SampleController.cs
Singleton manager and anchor placement mode
Note: The sample marks key API calls with // KEY API CALL comments. Search for this string in your IDE to find critical API call sites across SharedAnchor.cs, SharedAnchorLoader.cs, ColoDiscoMan.cs, and Alignment.cs.

Runtime behavior

In the SharedSpatialAnchors scene, you join a Photon room and place anchors. You share them to specific users by User ID, and the scene exchanges anchor UUIDs and the host’s anchor pose via Photon Room CustomProperties for MRUK-based alignment. In the ColocationSessionGroups scene, you advertise or discover Bluetooth colocation sessions with custom metadata and join a group. You share anchors to all group members without Photon networking, and the scene uses classic tracking space transforms for alignment without transmitting pose data over the network.

Key concepts

User-based sharing with Photon

The SharedAnchor.cs script demonstrates sharing to specific users. After you create an anchor and wait for localization, the script calls the static share method with an array of OVRSpaceUser instances:
// Assets/Scripts/SharedAnchor.cs
OVRSpatialAnchor.ShareAsync(anchors, users);
The Photon workflow exchanges anchor UUIDs via Room CustomProperties, allowing users to load shared anchors from the cloud. See Shared Spatial Anchors API for the complete user-based sharing workflow.

Group-based sharing with colocation sessions

The ColoDiscoMan.cs script demonstrates group-based sharing without Photon. The script advertises a session with custom metadata and shares anchors to a group UUID:
// Assets/Scripts/ColoDiscoMan.cs
OVRColocationSession.StartAdvertisementAsync(bytes);
OVRSpatialAnchor.ShareAsync(anchors, groupUuid);
Other devices discover the session via Bluetooth and load anchors by group UUID. This approach eliminates the need for a networking layer like Photon. See Shared Spatial Anchors API for colocation session methods.

Loading anchors: local-first with cloud fallback

The SharedAnchorLoader.cs script implements a two-step loading pipeline. First, attempt to load from local storage, then fall back to cloud for any missing anchors:
// Assets/Scripts/SharedAnchorLoader.cs
OVRSpatialAnchor.LoadUnboundAnchorsAsync(uuids, unboundAnchors);
OVRSpatialAnchor.LoadUnboundSharedAnchorsAsync(uuids, unboundAnchors);
unboundAnchor.BindTo(spatialAnchor);
Binding must occur in the same frame as GameObject instantiation. This local-first pattern optimizes for offline scenarios while supporting cloud-based collaboration. See Spatial Anchors API for loading methods.

MRUK world locking alignment

The Alignment.cs script demonstrates the MRUK-based alignment approach. After the host shares an anchor and clients load it, the host transmits the anchor’s original pose via Photon. Clients then set the anchor as the MRUK world lock reference:
// Assets/Scripts/Alignment.cs
MRUK.Instance.SetCustomWorldLockAnchor(ovrAnchor, anchorPoseOnHost);
This approach eliminates manual tracking space math by delegating alignment to MRUK. See the SharedSpatialAnchors scene for the complete workflow.

Classic tracking space alignment

The Alignment.cs script also implements a non-networked alignment method. Compute the camera rig’s tracking space as the inverse of the anchor’s world-space pose and reposition non-anchored objects to the new coordinate system. The ColocationSessionGroups scene uses this approach, requiring no pose data transmission over the network. See the SetOrigin method in Alignment.cs for the full transform calculation.

Extend the sample

  • Implement custom colocation session metadata to filter discoverable groups by game mode, player count, or difficulty level
  • Build a hybrid workflow that uses colocation sessions for initial device pairing and Photon for ongoing gameplay state synchronization
  • Replace the JSON-based local persistence in LocallySaved.cs with a database to support anchor search and filtering by creation date or spatial region