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.
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/Script
Demonstrates
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:
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:
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:
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:
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