How to Sync with Udon

Udon has 4 ways to sync

  1. Sync Position using an UdonBehaviour component

    • Used to sync the position & rotation of an object
  2. Sync Animation using an UdonBehaviour component (Coming Soon)

    • Used to sync the animator transitions of an object
  3. Udon Synced Variables

    • Used to sync individual Udon variables across all players
    • Currently supports bool, char, byte, int, long, unsigned byte, unsigned int, unsigned long, float, double, short, unsigned short, string, Color, Color32, Quaternion, Vector2/3/4
    • Array syncing is not yet supported
    • Supports Tweening (none, linear, smooth) for applicable types
    • NOTE - Only the network owner of an object can set a synced variable
    • NOTE - The owner is by default the master, and it can only change if:
      1. Master changes
      2. The object collides with another object
      3. The object is picked up
      4. Networking.SetOwner is used
  4. Custom Events

    • Used to execute events on an UdonBehaviour across the network
    • Currently does not support parameters
    • Like an RPC with zero parameters

Known Issues

  • Synced Variables - integers don’t sync correctly using the Linear or Smooth tweening
  • Synced Variables - floats don’t sync right using Smooth tweening

Syncing Examples
Please see the SyncUI and SyncValueTypes examples scenes included with SDK3. Checkout the Readme for info on how those scenes work!


Are there examples for how to use sendCustomNetworkEvent? It only seems to send the event to my local client and not other clients in the instance

1 Like

Just in case it isn’t clear. Literally all you have to do to sync the position is check the box.
Step by step picking up objects and syncing them:

  1. Make sure it has a collider and a mesh renderer
  2. Attach the VRC_Pickup script from add component
  3. Add an Udon Behaviour from Add Component
  4. Someone said you need to make a blank graph so just click new program in the udon behavior. I don’t know if that is really needed though.
  5. Click the check box that says Synchronize Position on the Udon Behavior component (Pictured Above)

So if I understand correctly, checking the ‘Synchronize Position’ will make the status of a game object or mesh collider to everyone in the map, not only just you? Like turning on a mirror for example for everyone?

Yep so others can see it changing.

I did some probing into the throughput limits of synced variables in Udon. tl;dr Udon is roughly as fast as a 56K modem in terms of throughput, if you pack your data into 8 UdonBehaviors each with 2 synced strings and update them every 200ms.

I set up a simple loop on an UdonBehavior that updates a synced string with random data, and a couple of sliders to control how fast new random data is loaded in and how long the synced string is. Testing locally with two clients and accounts, I slowly increased the synced string length until I got console errors on the master or client, e.g. . Furthermore, I tested updating random data on multiple synced strings on the same UdonBehavior. For ascii random data I just loaded (char)Random.Range(0,127) and short I loaded (char)Random.Range(0,65535). Results:

# of synced variables type of data max string length effective bytes
1 short 42 84
1 ascii 126 110.25
2 short 35 140
2 ascii 105 183.75
3 short 19 108
3 ascii 55 144.375
4 short 12 96
4 ascii 33 115.5

Curiously the maximum bytes before errors peak at 2 synced variables, probably because of per-string overhead. Also curious is how 7bit ascii is more efficient. It’s definitely more annoying to pack than straight shorts, but the extra 43 bytes could make a difference.

For sync speed I tested how fast you can update the string on the and still see the updated string on the other client, without missing updates (by using a simple counter in each update and checking the increments from the last received update). I found that I got near-0% “packet loss” at 200ms updates (with the packet read happening every Update()). At 150ms, I got maybe 20% packet loss, and further. I didn’t measure exact values, but 200ms seems like a safe bet. 200ms stayed consistent while changing the # of synced variables and string length.

Finally, I tested how many distinct UdonBehaviors with synced variables you can have in the scene before VRChat’s network layer stops processing events (with the “Death Run Detected: dropped N events” log messages). The answer is somewhere between 8 and 16 behaviors (each with 2 synced strings). At 16, I wasn’t getting total loss of all events, but a fair number were dropped. 8 was perfectly stable however. The update speed stays constant regardless on how many synced variables you have, with 200ms still getting through 99% of the time.

Thus, the max reliable throughput for udon is with 8 objects with 2 synced strings each, using only bytes for 183 * 8 = 1464 bytes , at 200ms, is 7320 Bps or 58.56kbps.

Setup for this experiement in UdonSharp is here:

Not totally reproducible since it involved manually changing the number of synced variables and reuploading a world, but a good starting spot, especially as new updates and bugfixes will probably change this math.


Strings are among the least efficiently packed data types at the moment; I’m not surprised by your findings. :wink:

FYI, I’ll be adding more sync options soon. I’ve been working on release bugs for the last few weeks, but should be getting back on features shortly.

@hiina you mention 200ms. That would translate to only 5 fps of updates. Im wondering what the maximum fps of the udon system would be. Would syncing only a single float allow faster updates?

No, it would not be any faster.

Did a ping pong check not too long ago

Values are updated every time either side sends a NetworkEvent

@PhaxeNor not sure I understand the data. Is that saying that updates are happening 1.23 seconds apart?

It’s the time it takes for each side to get a “reply” back.

Left starts a timer (for left side) and sends a request, right side starts a timer (for right side) and replies back.
When the left get the reply back, it stores the time and resets it to zero then sends a new ping.

As for the last value, that’s just the average between all 4 tests.

So its in seconds? thus fps is less then 1 update per second?

It’s in seconds using deltaTime yes.

This is just how long it takes for a user to get a reply back. It will not be the same for every user.

It updates faster than once per second, but there’s a delay in replaying it to avoid connection issues when communicating across the planet. That delay is roughly related to your round trip ping to the other user, through the relay servers, with a factor. There’s other factors: connection quality, for instance.

BTW, a lot of relevant info is in the new debug display … 8? I think it’s 8.

I did another test with synced variables, this time with a bunch of longs. Turns out you can sync a max of 15 longs per UdonBehaviour before the sync breaks, for 120 bytes, so still less than ascii strings despite the packing inefficiency. I suspect ascii strings are still the best for syncing arbitrary byte data for now. Vector4 would give 16 bytes each in theory, but I don’t think there’s an Udon-accessible way to pack bytes into floats (can’t reinterpret cast).

@_danly do you have documentation for the udon debug menu anywhere? I see the IS number changing around 205ms, but I don’t know what that means. Documentation on the FlatBuffer serialization limits would be nice too of course, would save me some more experimentation.

1 Like

Only drawback with using synched variables is that for it to update all players needs locally read and execute the change, else the variable changing will not do anything (constant update cycle’s).
It likely to fail for complex behaviours unless you able to assign count ID variables to the variable it self to make sure that packet delays/losses does not desynch the intended behaviour with 2 changes arriving out of order. Having the owner as a callcenter might be the best option.

Do Udon Synced Variable use delta compressed?
It seemed to always send the current value even without updating to new value.

I laid out many UdonSynced objects (about 50 objects), some parameters were dropped always.
How can I avoid sending values that haven’t changed?

We can’t do anything with it being sent even when it hasn’t been updated.

Until we get the ability to pass variables with custom events, there isn’t much other you can do than move stuff over to send network events and update values locally (numbers only sadly)