VRC Object Sync: Sharing a Thrown Object's Position

Created: 2026-09-08Last updated: 2026-09-09

Adding VRC Object Sync to a holdable prop so its thrown position is shared with everyone. Also covers why position travels automatically while state like a light being on needs separate syncing.

You built a holdable cup. Then you go in with a friend and the cup you set on the desk is still on the floor on their screen.

Pickup is a component that makes things grabbable and nothing more — it has no function for sharing position. VRC Object Sync adds that.

This article builds up to a thrown object's position lining up for everyone. Along the way we'll look at why state other than position is handled separately.

A thrown cup flying along the same path on two screens

What You'll Learn

  • What Object Sync carries and what it doesn't
  • How ownership behaves when combined with Pickup
  • How to return objects to their starting position
  • What to attach it to, and what not to

Start from having tried a flashlight you can hold and networking basics.

Sponsored


Only position and rotation are carried

VRC Object Sync is a component that keeps sending an object's position and rotation from the owner to everyone else.

Object Sync carries position and rotation, not state like a light being on

That's all it carries. State like a color change or a light turning on isn't included.

Get this wrong and you end up with "carrying is shared, but only I can see the flashlight's beam." The beam needs separate synchronization.

The structure looks like this.

ComponentRole
Collider + RigidbodyColliding, falling
VRC PickupMakes it grabbable
VRC Object SyncShares position and rotation
Holdable, droppable, and position-shared: three components combined

The other convenience of Pickup is that ownership transfers automatically. Whoever grabs it becomes the Owner the moment they do, so the role of sending position switches automatically. No extra code needed.

Sponsored

Hands-On: Build a throwable cup

Build a holdable cup so its thrown position is the same for everyone.

1. Build the cup

Create SharedCup as a "3D Object → Cube" at (0, 1, 1), Scale (0.15, 0.2, 0.15).

Add the following via "Add Component."

  1. VRC Pickup (a Rigidbody comes with it)
  2. VRC Object Sync

For VRC Pickup, turn Auto Hold on and Pickupable on.

For the Rigidbody, leave Use Gravity on and Is Kinematic off. That gives the natural motion of falling when thrown.

That's everything you add. No code.

2. Check with two people

Launch Build & Test with Number of Clients set to 2.

OrderActionExpected result
1A and B enterThe cup is in the same place on both screens
2A grabs itThe cup moves into A's hand. On B's screen it moves near A's hand too
3A throws itIt flies and lands the same way on both screens
4B picks it upOwnership moves to B. It appears in B's hand on A's screen too
5A leaves while B holds itThe cup stays in B's hand

All of that works without a single line of code. Pickup carries ownership and Object Sync carries position.

3. Add a way to reset the position

A cup thrown below the floor or past a wall becomes unreachable. A reset button is reassuring.

Returning a held object to its starting position

Create ResetButton as a "3D Object → Cube" at (1.5, 1, 1).

Create CupResetter in Assets/Scripts.

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

[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class CupResetter : UdonSharpBehaviour
{
    [SerializeField] private VRCObjectSync targetSync;

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

        // Become the owner before resetting
        if (!Networking.IsOwner(targetSync.gameObject))
        {
            Networking.SetOwner(Networking.LocalPlayer, targetSync.gameObject);
        }

        targetSync.Respawn();   // Return it to its starting position
        Debug.Log("[CupResetter] reset");
    }
}

Respawn() is the method that returns the object to the starting position Object Sync remembers. You don't have to record the position yourself.

Taking ownership before resetting is the key point. Only the owner can move the position, so to reset a cup someone else is holding, you take ownership first.

Add a Udon Behaviour to ResetButton and drag SharedCup into Target Sync.

State other than position, you sync yourself

Now recall the flashlight built with Pickup — the one that lit up on Use while held.

Add Object Sync to it and carrying gets shared while the beam is visible only to you.

You can probably see why now. Object Sync carries position and rotation, and Light.enabled isn't carried.

To share the beam too, add one synced variable.

[UdonSynced] private bool beamOn;

public override void OnPickupUseDown()
{
    // The holder is already the owner, so we can write directly
    beamOn = !beamOn;
    ApplyBeam();
    RequestSerialization();
}

public override void OnDeserialization()
{
    ApplyBeam();
}

private void ApplyBeam()
{
    if (beam != null) beam.enabled = beamOn;
}

Exactly the same shape as the networking introduction. Hold one state and apply it on receive. Only the target changed, from a door to a light.

Note that the sync mode here is Manual. Position is carried separately by Object Sync, so "only when it changes" is enough on this side.

Sponsored

Common Pitfalls

  • Position isn't shared → Check that VRC Object Sync is attached. Pickup alone doesn't share it
  • You hold it but it doesn't move on their screen → Ownership may not have transferred. Check that Pickup is attached correctly
  • The object jitters or flies off → Look at the Rigidbody settings. Also check whether several scripts are touching the same object's position
  • The reset button does nothing → Check that you call Respawn() after taking ownership
  • Only the beam isn't shared → That's by design. State other than position you sync yourself

Bonus: Good to Know Up Front

  • Keep the count down: Object Sync keeps sending position, so more of them means more traffic. Keep rolling props to a handful and don't attach it to static decorations
  • Don't attach it to static objects: On furniture or walls it does nothing and only adds traffic
  • It doesn't suit switches or chairs: There's a misconception that "putting Object Sync on everything shares it," but it's a position-sending component and doesn't suit things whose state you want shared. Those want synced variables
  • Physics won't match exactly: Physics runs on each PC, so fine rolling behavior differs slightly. Position gets lined up periodically, and the motion in between isn't strictly identical
  • It's useful all over: Board game pieces, balls to throw around, a cafe's cups, an escape game's key. Anything you move whose position you want shared

Summary

Object Sync is a component dedicated to carrying position.

  • It carries position and rotation only. State like a light being on isn't carried
  • Combined with Pickup, whoever grabs it becomes the owner automatically
  • Respawn() returns it to the starting position. Call it after taking ownership
  • State other than position you share yourself, with synced variables

The question to ask before attaching it is: "Is this something whose moved position I want shared?" If you only want state shared, synced variables suit better.

To keep each person's settings until next time, go to saving volume with PlayerData. To lend out a fixed number of objects, go to VRCObjectPool.

VRChat Notes in this section63