Skip to content

OSC avatar control

OSC (Open Sound Control) lets a player read and write their own avatar's expression parameters from a separate app running on the same computer. You don't wire OSC up per avatar — you simply declare expression parameters, and any parameter you declare becomes reachable over OSC on that player's machine. Use it to build hardware controllers, face-tracking bridges, stream-deck toggles, or any external tool that drives an avatar's expressions.

What OSC controls

Each player's client can expose a small OSC surface on their own machine:

  • /avatar/parameters/<name> — read and write a single expression parameter.
  • /avatar/change — switch to a different avatar by its id.
  • /chatbox/input and /chatbox/typing — post a chatbox message and drive the overhead typing indicator.

OSC only ever touches the local player's own avatar — it can't reach out and control anyone else. Changes still reach the other players around you, but through the normal avatar sync, not over OSC itself — see What other players see.

Expose a parameter

A parameter is reachable over OSC only if you declare it in an Expression Parameters asset and assign that asset to your avatar. To set one up:

  1. Create the asset from the menu: Social Scape > Avatars > Expression Parameters.
  2. Add an entry to the parameters list and fill in its fields:
  3. name — must match the animator parameter name on your avatar's controller.
  4. valueTypeInt, Float, or Bool.
  5. saved — whether the value persists between sessions (defaults to on).
  6. defaultValue — the value the parameter starts at.
  7. Select your avatar's Avatar Descriptor and drag the asset into its Expression Parameters field.

That field is the single link between your avatar and its OSC-exposed parameter set. If a parameter isn't in that asset, it is not synced and not reachable over OSC.

Names must match your animator

The name you enter is looked up on your avatar's animator controller. If no parameter with that exact name exists on the controller, nothing drives it. Keep the two in lockstep.

How names and types map to OSC

Each declared parameter maps directly to an OSC address and argument type:

  • The parameter name becomes the address suffix: a parameter named Wave is reachable at /avatar/parameters/Wave.
  • The valueType becomes the OSC argument type: Int → integer, Float → float, Bool → boolean.

Only the parameters in your Expression Parameters asset are exposed. Built-in animator parameters (for example a locomotion state such as Grounded) are not exposed automatically — if you want one reachable over OSC, add a parameter with that same name to the asset.

The parameter budget

Every parameter costs against a fixed budget, MAX_PARAMETER_COST = 128, summed across all parameters in the asset:

Parameter type Cost
Int 8
Float 8
Bool 1

Prefer Bool for on/off toggles to stay well under the cap — you can fit many more booleans than ints or floats.

Turning it on and ports

OSC is a per-player setting, not something you configure on the avatar. Players turn it on for themselves from the client Settings panel, and it is off by default. When a player enables it:

Setting Default Meaning
OSC enabled off Master on/off for the whole subsystem.
Listen (in) port 9000 The client listens here; external apps send to this port.
Send (out) port 9001 The client sends here; external apps listen on this port.

OSC only ever talks to your own computer (loopback, 127.0.0.1) — both the sending and the listening side. Another machine on your network can't reach it, so you don't need to open or forward any ports; just run your OSC app on the same PC. Ports are clamped to the range 165535, and changing either one rebinds the transport immediately.

When a player's avatar loads, the client writes a small address-map file so external OSC apps can auto-discover the parameter names and types for that avatar:

%APPDATA%/SocialScape/OSC/Avatars/<guid>.json

The file lists each synced parameter with its address and type, for example:

{
  "id": "<guid>",
  "name": "<guid>",
  "parameters": [
    {
      "name": "Wave",
      "input":  { "address": "/avatar/parameters/Wave", "type": "Bool" },
      "output": { "address": "/avatar/parameters/Wave", "type": "Bool" }
    }
  ]
}

What other players see

OSC drives only the local player's avatar. Parameter changes still reach everyone else, but through the normal avatar-parameter network sync, not over OSC: the client streams changed synced parameters at roughly 10 Hz, so a toggle a player flips over OSC is seen by everyone around them. From an observer's point of view there's no difference between a parameter changed via OSC and one changed through the in-app expression menu.

Chatbox over OSC

Two addresses drive the overhead chatbox on the local player:

  • /chatbox/input (string) — shows the overhead bubble and sends the message to nearby players. The text commits immediately; any trailing arguments are ignored.
  • /chatbox/typing (bool) — toggles the overhead typing indicator and networks it.

Both are receive-only. Chatbox text and typing state travel to other players over the game-server connection, the same path other chat uses.

Scope and what's not supported

OSC is a local, per-player control surface by design — it is not part of NexusScript. World and avatar scripts never send or receive OSC; it only ever drives the local player's own avatar and chatbox from an app on that player's own machine.

Beyond that, some address namespaces aren't handled yet:

No input, tracking, camera, or MIDI over OSC

OSC input control (movement, look, jump, run), tracking (trackers, eye, VR system), and camera control are not currently supported — there are no handlers for the /input/*, /tracking/*, or /camera/* namespaces. MIDI control is also not currently supported.

Quick reference

Available OSC addresses

Address Argument Direction Notes
/avatar/parameters/<name> Float | Int | Bool read + write Drives one declared synced parameter. Unknown names are dropped. Outbound updates stream at ~10 Hz.
/avatar/change String (avatar id) read + write Read loads that avatar; the client also sends it when the player swaps avatars.
/avatar/parameters/Seated matches your Sitting parameter read Receive-only alias that drives your Sitting parameter (whatever type you declared it), active only when your avatar has one. This is the only alias.
/chatbox/input String read Shows the overhead bubble and sends the message.
/chatbox/typing Bool read Toggles the overhead typing indicator.

Ports and files

Item Value
Listen (in) port 9000 (default)
Send (out) port 9001 (default)
Enabled off by default
Binding loopback 127.0.0.1 only (both directions)
Port range 165535
Address-map file %APPDATA%/SocialScape/OSC/Avatars/<guid>.json