Develop
Develop
Select your platform

Mixed Reality Utility Kit - Space sharing

Updated: Oct 1, 2025

Learning Objectives

  • Identify when to use Unity’s Space Sharing APIs versus Shared Spatial Anchors.
  • Execute the Host workflow: advertise a session, share MRUK rooms, and serialize floor-anchor pose.
  • Implement the Guest workflow: discover session, receive room UUID & pose, and load & align the scene.
  • Apply coordinate frame alignment using MRUK’s LoadSceneFromSharedRooms with alignmentData.

Overview

Shared Spaces lets multiple users in the same physical environment see, interact with, and manipulate the same mixed reality content. The Unity Space Sharing APIs in MRUK let you share detailed Scene data, such as walls, floors, doors, furniture dimensions, across devices. When using Space Sharing, one device acts as Host (shares its Scene Model via a groupUuid), and others act as Guests (discover and load it).
Before continuing, it might be helpful to get familiar with Shared Spatial Anchors (SSA), and especially with the concept of group-based anchor sharing and loading, which Space Sharing is based on. For a step-by-step guide on how to use Spatial Anchors, SSA, as well as Space Sharing, see our MR Motif about Colocated Experiences.

Use Cases

Competitive Gameplay
Competitive Gameplay
Compete in mini-golf, tag, or strategy games sharing the same MR playfield.
Collaboration
Collaboration
Co-create furniture layouts, sketches, or designs in a shared augmented space.
Media Watching
Media Watching
Enjoy spatialized video, multi-screen displays, or 3D audio together.
Training
Training
Practice hands-on drills or assembly tasks with synchronized 3D overlays.

Requirements & Limitations

  • Unity Open XR Plugin is supported for v76 and later versions of Meta XR Core SDK. If an app uses v74 and earlier versions of Meta XR Core SDK, use the Unity Oculus XR Plugin instead.
  • Only the Host device must have a scanned Scene Model before sharing, Guests simply load the shared data.
  • The APK must be published to a release channel in the Developer Dashboard, and all participants (users or test users) invited or on your team.
  • Space Sharing cannot occur between two devices on the same Meta account. To test during development, either:
    • Use a second physical device (friend/colleague) entitled to the app.
    • Create and log in with a Test User on the second device, and invite that email to your release channel with proper entitlements.
  • Colocation Discovery: Make sure to check the Colocation Discovery setup instructions.
  • In addition to the above, set Shared Spatial Anchor Support to Required, ensure the device is connected to the internet, and enable Enhanced Spatial Services on the headset (Settings → Privacy & Safety → Device Permissions).

Best Practices

  • Health & Safety: Please refer to the Health and Safety and Design guidelines.
  • Privacy: Ensure users control what they share and with whom when implementing Shared Spaces.
  • Large Geometry: Anchor shared large objects up to the size of the room to prevent drift.
  • Bandwidth: Optimize operations for low uplink connectivity conditions based on your app’s business logic.

API Reference

Share Rooms (Host)

// Shares multiple MRUK rooms with a group
public OVRTask<OVRResult<OVRAnchor.ShareResult>> ShareRoomsAsync(
    IEnumerable<MRUKRoom> rooms,
    Guid groupUuid);

Load Shared Rooms (Guest)

// Loads scene data shared by Host, optionally aligned to their floor anchor
public async Task<LoadDeviceResult> LoadSceneFromSharedRooms(
    IEnumerable<Guid> roomUuids, Guid groupUuid,
    (Guid alignmentRoomUuid, Pose floorWorldPoseOnHost)? alignmentData,
    bool removeMissingRooms = true);

Step-by-Step Workflow

  1. Capture Scene: Host loads or scans the environment with MRUK.
  2. Generate a group UUID: Use Colocation Discovery to obtain a group UUID.
    public async void StartColocation()
    {
        var advertisement = await OVRColocationSession.StartAdvertisementAsync(null);
        Guid groupUuid = advertisement.Value;
    
        // TODO: Store groupUuid in a networked variable to later share with guests
    }
    
  3. Host: Share MRUK Room
    public async void ShareMrukRoom(Guid groupUuid)
    {
        var room = MRUK.Instance.GetCurrentRoom();
        await room.ShareRoomAsync(groupUuid);
    
        // TODO: Store the room's floor pose as a string in a networked variable to later share with guests
    }
    
  4. Guest: Load Shared Room
    public async void LoadSharedRoom(Guid groupUuid, Guid roomUuid, string poseString)
    {
        // TODO: Parse poseString into a Pose (see step 5)
        Pose pose = ParsePose(poseString);
        MRUK.Instance.LoadSceneFromSharedRooms(null, groupUuid, (roomUuid, pose));
    }
    
  5. Coordinate Frame Alignment: Once the Host and Guest have shared the scene anchors, use the floor anchor of any shared room as the basis for a shared coordinate frame. Serialize and send the floor anchor’s pose and room UUID from the Host to the Guest (e.g., via Photon Fusion, Unity Netcode, or Colocation Discovery data). The Guest then calls LoadSceneFromSharedRooms with the alignmentData parameter. Ensure EnableWorldLock = true so MRUK adjusts the camera’s tracking space, aligning both devices.
    // Host: Serialize and send floor-anchor pose
    public void SendAlignmentData(Guid roomUuid)
    {
        var room = MRUK.Instance.GetCurrentRoom();
        var floor = room.FloorAnchor.transform;
        string poseString = $"{floor.position.x},{floor.position.y},{floor.position.z},"
                          + $"{floor.rotation.x},{floor.rotation.y},"
                          + $"{floor.rotation.z},{floor.rotation.w}";
    
        // TODO: Send both 'roomUuid' and 'poseString' to Guests
    }
    
    // Guest: Parse received pose string back into a Pose
    public Pose ParsePose(string poseString)
    {
        var p = poseString.Split(',');
        return new Pose(
            new Vector3(
                float.Parse(p[0]),
                float.Parse(p[1]),
                float.Parse(p[2])
            ),
            new Quaternion(
                float.Parse(p[3]),
                float.Parse(p[4]),
                float.Parse(p[5]),
                float.Parse(p[6])
            )
        );
    }
    

☕ Complete Space Sharing Implementation using Photon Fusion

using System;
using System.Linq;
using Fusion;
using UnityEngine;
using Meta.XR.MRUtilityKit;

public class SpaceSharingManager : NetworkBehaviour
{
    [Networked] private NetworkString<_512> NetworkedRoomUuid { get; set; }
    [Networked] private NetworkString<_256> NetworkedRemoteFloorPose { get; set; }

    private Guid _sharedAnchorGroupId;

    public override void Spawned()
    {
        base.Spawned();
        PrepareColocation();
    }

    private void PrepareColocation()
    {
        if (Object.HasStateAuthority)
        {
            AdvertiseColocationSession();
        }
        else
        {
            DiscoverNearbySession();
        }
    }

    private async void AdvertiseColocationSession()
    {
        var result = await OVRColocationSession.StartAdvertisementAsync(null);
        if (!result.Success)
        {
            Debug.LogError($"[Host] Advertisement failed: {result.Status}");
            return;
        }

        _sharedAnchorGroupId = result.Value;
        Debug.Log($"[Host] Advertisement started. Group UUID: {_sharedAnchorGroupId}");
        ShareMrukRooms();
    }

    // Shares the current room of the host's MRUK instance.
    // If you would like to share all rooms follow these steps:
    // var rooms = MRUK.Instance.Rooms;
    // var result = await MRUK.Instance.ShareRoomsAsync(rooms, _sharedAnchorGroupId);
    // NetworkedRoomUuid = string.Join(",", rooms.Select(r => r.Anchor.Uuid));
    // var pose = room.FloorAnchor.transform;
    private async void ShareMrukRooms()
    {
        var room = MRUK.Instance.GetCurrentRoom();
        NetworkedRoomUuid = room.Anchor.Uuid.ToString();
        Debug.Log($"[Host] Sharing MRUK room: {room.Anchor.Uuid}");

        var result = await room.ShareRoomAsync(_sharedAnchorGroupId);
        if (!result.Success)
        {
            Debug.LogError($"[Host] Failed to share MRUK room: {result.Status}");
            return;
        }

        Debug.Log("[Host] MRUK room shared successfully.");

        var pose = room.FloorAnchor.transform;
        NetworkedRemoteFloorPose = $"{pose.position.x}, {pose.position.y},
                                     {pose.position.z}," +
                                   $"{pose.rotation.x}, {pose.rotation.y},
                                     {pose.rotation.z},{pose.rotation.w}";
        Debug.Log($"[Host] Set NetworkedRemoteFloorPose = {NetworkedRemoteFloorPose}");
    }

    private async void DiscoverNearbySession()
    {
        OVRColocationSession.ColocationSessionDiscovered += OnSessionDiscovered;
        var result = await OVRColocationSession.StartDiscoveryAsync();
        if (!result.Success)
        {
            Debug.LogError($"[Client] Discovery failed: {result.Status}");
        }
        else
        {
            Debug.Log("[Client] Discovery started successfully.");
        }
    }

    private void OnSessionDiscovered(OVRColocationSession.Data session)
    {
        OVRColocationSession.ColocationSessionDiscovered -= OnSessionDiscovered;
        _sharedAnchorGroupId = session.AdvertisementUuid;
        Debug.Log($"[Client] Discovered session: {_sharedAnchorGroupId}");
        LoadSharedRoom(_sharedAnchorGroupId);
    }

    private static Pose ParsePose(string poseString)
    {
        var parts = poseString.Split(',');
        if (parts.Length == 7)
        {
            return new Pose(
                new Vector3(float.Parse(parts[0]), float.Parse(parts[1]),
                            float.Parse(parts[2])),
                new Quaternion(
                    float.Parse(parts[3]), float.Parse(parts[4]),
                    float.Parse(parts[5]), float.Parse(parts[6]))
            );
        }

        Debug.LogError("Invalid pose string: " + poseString);
        return default;
    }

    // Loads the rooms previously shared with the user.
    // If you want to load multiple rooms, parse all room guids and
    // include them in the LoadSceneFromSharedRooms method instead of "null".
    private async void LoadSharedRoom(Guid groupUuid)
    {
        Debug.Log($"[Client] Loading shared MRUK room: {groupUuid}");

        var roomUuid = Guid.Parse(NetworkedRoomUuid.ToString());
        var remotePoseStr = NetworkedRemoteFloorPose.ToString();
        var remoteFloorWorldPose = ParsePose(remotePoseStr);

        var result = await MRUK.Instance.LoadSceneFromSharedRooms(
            null,
            groupUuid,
            (roomUuid, remoteFloorWorldPose));
        if (result == MRUK.LoadDeviceResult.Success)
        {
            Debug.Log("[Client] Successfully loaded and aligned to shared room.");
        }
        else
        {
            Debug.LogError($"[Client] Failed to load shared MRUK room: {result}");
        }
    }
}
  


Space Sharing Motif Sample
Colocated Experiences MR Motif
The Space Sharing scene in this sample project demonstrates how to share the room layout with other users. This MR Motif guides you from the basics of Spatial Anchors, through network sharing with Colocation Discovery and Shared Spatial Anchors, to building fully co‑located experiences with MRUK’s Space Sharing API.
View sample
Unity-SpaceSharing
This Unity sample demonstrates the concept of Space Sharing, a feature provided in Meta's Mixed Reality Utility Kit (MRUK). Space Sharing APIs allow Colocated Multiplayer apps to quickly and easily synchronize real-world Scene entities and geometry among clients.
View sample
Unity Space Sharing Sample

Troubleshooting Guide

🛠️ OpenXR Plugin Support

Unity Open XR Plugin is supported for v76 and later versions of Meta XR Core SDK. If an app uses v74 and earlier versions of Meta XR Core SDK, use the Unity Oculus XR Plugin instead.

🛠️ Scene Anchor Misalignment

If anchors misalign, have the Host move their headset between sessions or clear anchors under Settings → Privacy & Safety → Device Permissions → Clear Physical Space History.

🛠️ Local Anchor Storage Limit

Guests may store too many anchors locally. Clear unused anchors via Settings → Privacy & Safety → Device Permissions → Clear Physical Space History, then restart the Meta Quest device.


Previous: Track Objects in MRUK
Explore more MRUK documentation topics to dive deeper into spatial queries, content placement, manipulation, sharing, and debugging.
Core Concepts
  • Overview Get an overview of MRUK’s key areas and features.
  • Getting Started Set up your project, install MRUK, and understand space setup with available Building Blocks.
  • Place Content without Scene Use Environment Raycasting to place 3D objects into physical space with minimal setup.
  • Manage Scene Data Work with MRUKRoom, EffectMesh, anchors, and semantic labels to reflect room structure.
Content & Interaction
Debugging
  • Debug Your MRUK App Use tools like the Scene Debugger and EffectMesh to validate anchor data and scene structure.
MRUK Samples & Tutorials
Mixed Reality Design Principles
Did you find this page helpful?
Thumbs up icon
Thumbs down icon