Skip to content

Avatar Animation

The AvatarDescriptor is the brain of your avatar's movement. It doesn't play a single Animator Controller — instead it merges several controllers into layers that stack on top of each other, so walking, hand gestures, emotes, and on/off toggles can all happen at once without fighting. This page explains how that layering works and how to configure every layer, parameter, and menu control.

If you haven't set up the descriptor yet, start with Avatars — this page picks up at the Animation layers section.

The mental model

Think of your avatar's pose as a fixed stack. Each layer is one Animator Controller, and the engine always stacks them in this order (see Is it any order? below):

┌─────────────────────────────────────────────┐
│  Expressions / FX   (toggles, blendshapes)   │  ← overrides only the objects it animates
│  Action             (full-body emotes)       │  ← overrides the whole body, on demand
│  Gesture            (hand poses)             │  ← overrides the hands
│  Additive           (breathing, lean)        │  ← ADDS on top
├─────────────────────────────────────────────┤
│  Locomotion         (walk / run / idle)      │  ← the BASE, full body
└─────────────────────────────────────────────┘

Locomotion is the foundation — it drives the whole body every frame. Every other layer sits on top and changes only the part it's responsible for. The trick that makes this work is how each layer composites:

  • Override — the layer replaces what it touches, scoped to a mask (a list of the bones/objects it's allowed to write). A Gesture layer masked to the hands replaces only the hands; locomotion keeps the body. This is the mode for Gesture, Action, and Expressions/FX.
  • Additive — the layer adds its change on top of whatever's underneath. This is for secondary motion (breathing, sway) on the Additive layer only — it can't blank the body because it only contributes a delta.

You almost never author a mask yourself

Social Scape derives the mask for you — from the controller's own internal masks (gestures) or, for a toggle/FX layer, from the exact objects your clips animate (the hat, the blendshape, the material). So an Expressions layer that flips a hat overrides only the hat and leaves your body to locomotion, with no mask on your part. See Masks.

This changed — toggles are now override, not additive

Earlier builds blended a maskless toggle layer additive, which could turn a default-off object on but could not turn a default-on object off (additive has no way to force an object's Active state down). Toggles now run as a scoped override, so a default-enabled outfit piece can be toggled off correctly — matching what you see with the controller on a plain Animator. If you author default-enabled toggles, read Default state.

The layers

Each layer is an Animator Controller you assign to a typed slot on the AvatarDescriptor (Base Animation Layers).

Layer Type value What it's for How it composites
Locomotion 0 Walk, run, idle, crouch, jump, sit, fly — the base body motion Override base — full body, weight 1
Gesture 1 Hand poses (fist, point, open) driven by hand input Override, scoped to the hands
Action 2 Full-body emotes (dances, sit-downs) — temporary, on demand Override, full body, weight-controlled
Additive 3 Secondary motion — breathing, sway, lean Additive (the only additive layer)
Expressions / FX 4 On/off toggles, accessory objects, blendshape expressions, material swaps Override, auto-scoped to the objects it animates

Social Scape gives you Locomotion and Gestures for free

You don't have to build a locomotion or gesture controller — Social Scape supplies defaults (SS_LocomotionLayer, SS_Gestures) that walk, run, idle, crouch, and pose the hands out of the box. You only add the layers you actually want to customize; leave a slot empty to use the default (Locomotion) or nothing (the others).

Is the slot order up to me?

No — and that's deliberate. Each of the five slots is a fixed role with its own composite behavior. You choose which role a controller fills (by assigning it to the Locomotion / Gesture / Action / Additive / Expressions slot); you don't choose the stacking order. Social Scape stacks them in the canonical order above regardless of the array order on the descriptor, so re-ordering the list never changes how your avatar looks.

The role names and stack order (Base → Additive → Gesture → Action → FX) follow the layout avatar authors already know from other platforms, so an existing controller set drops into the matching slots. The Expressions layer is where Social Scape does more for you:

  • Your Expressions controller doesn't have to avoid animating transforms or muscles, and doesn't need a hand-authored mask.
  • Social Scape auto-derives a scope mask from the objects your clips actually touch, so the layer overrides only those objects — safely, with no setup.

The takeaway: put a controller in the slot that matches its job. The slot picks override-vs-additive and the mask for you.

Base parameters

Locomotion is driven by a set of base parameters that Social Scape sets for you from the player's movement and menu. You don't drive these — but if you build a custom locomotion or gesture controller, use these exact names so it responds. (Verified against the current runtime drivers AvatarLocomotionDriver, VrGestureDriver, and the emote menu.)

Parameter Type Driven by Meaning
Horizontal, Vertical Float movement Strafe / forward (−1…1)
Grounded Bool movement On the ground (true for remotes)
Crouching, Prone Bool movement Posture states
GestureLeft, GestureRight Int hand input Active hand gesture per hand
GestureLeftWeight, GestureRightWeight Float hand input (VR) Analog grip, 0–1
Emote Float the Emotes menu Selected emote (0 = none)

Other names the default controller understands

The bundled SS_LocomotionLayer also exposes states like Sitting, Flying, Pose, and CancelEmote. These are not auto-driven by the movement driver today — drive them from the relevant system (a station/seat, the emote menu) or from your own controller logic. Prefer the standard parameters below (Seated, InStation, Upright) for posture your controller reacts to, since those are actively driven.

Social Scape also drives a set of standard parameters — using the conventional names avatar controllers already expect — from replicated player state, so a custom controller can react to speed, posture, and platform without any wiring. Every one is a no-op unless your controller declares it, so add only the ones you use:

Parameter Type Meaning
VelocityX, VelocityY, VelocityZ Float Avatar-local velocity per axis
VelocityMagnitude Float Overall speed
IsLocal Bool True on the wearer's own client (e.g. hide a HUD from everyone else)
VRMode Int 1 in VR, 0 on desktop
TrackingType Int 1 = desktop/generic, 3 = VR (3-point)
InStation, Seated Bool The player is seated in a station
Upright Float Standing = 1, crouch ≈ 0.6, prone ≈ 0.15

Your own parameters (toggles, expressions) are separate — you declare those in Expression Parameters (below).

Expression Parameters

Expression Parameters (Assets ▸ Create ▸ Social Scape ▸ Avatars ▸ Expression Parameters) is the list of named values your avatar reacts to — the bridge between the action menu and your animator controllers. Assign the asset to the descriptor's Expression Parameters field.

Each parameter has these fields:

Field What it does
Name Must exactly match the animator parameter name your controller uses (case-sensitive).
Type Bool (on/off toggles), Int (multi-choice, e.g. an outfit index), or Float (a 0–1 dial / puppet).
Default The value the avatar loads with. This is authoritative — see Default state.
Saved On by default. The player's value is remembered per avatar across avatar swaps and sessions. Leave on for customization (outfits, colors); turn off for momentary/gameplay-only state.
Synced On by default. The value replicates to other players, so everyone sees your hat. Uncheck for a local-only parameter — still driven locally (menu/OSC) and saved if Saved is on, but never sent over the network and free of sync budget.

Parameter budget

Synced parameters cost against a budget: Bool = 1, Int = 8, Float = 8, and the total across all synced parameters must stay under 512 (local-only parameters are free). The descriptor inspector shows a live cost bar. Keep toggles as Bools where you can.

Default state — the #1 cause of a toggle starting wrong

The avatar loads every parameter at its Expression Parameters Default — and that default wins over the animator controller's own default. So if you build a default-enabled outfit piece (object active in the scene, controller parameter defaulting to true) but leave the Expression Parameter Default at 0, the toggle loads in the wrong state: it reads "Off" in the menu and can hide the piece on load.

Rule: set each Expression Parameter's Default to the state the toggle should start in1/On for a default-enabled item, 0/Off for a default-hidden one.

The SDK catches this for you

The Avatar Descriptor inspector runs a check: if an Expression Parameter's Default disagrees with the animator controller's own default for that parameter, it shows a warning listing each mismatch and a one-click “Set Expression defaults to match controllers” fix. It's a warning, never a block — a creator can always upload. Reset/Default in the action menu returns everything to these defaults.

Expression Menu

Expression Menu (Assets ▸ Create ▸ Social Scape ▸ Avatars ▸ Expressions Menu) is the radial action menu players open in-world. Assign it to the descriptor's Expressions Menu field. Each menu holds up to 16 controls per page; move overflow into a Sub-menu.

Every control has: a Name (label), an optional Icon (Texture2D), a Type, the Parameter it drives, a Value, and — for puppets/sub-menus — Sub Parameters, a Sub Menu, and axis Labels.

The control types

Type How the player uses it What it writes (verified against the runtime)
Button Press and hold Sets Parameter → Value while held; released when you let go. Good for momentary effects.
Toggle Click on/off Sets Parameter → Value when on, → 0 when off. The everyday outfit/accessory switch.
Sub-menu Opens another page Opens the assigned Sub Menu. If a Parameter is set, it holds Parameter → Value while that page is open and restores the previous value on Back.
Radial Puppet Turn a dial 0→1 Drives the Rotation parameter (a Float) with 0…1 (e.g. a blendshape slider, a brightness). You turn around the ring — the fill sweeps clockwise from the top and the value reads out live.
Radial Int Turn a dial to a whole number Drives an Int parameter across a range Min…Max, snapping between integer steps and committing that whole number (e.g. a palette index 0–99, a mode 0–3). A Social Scape original with no equivalent on other platforms; it saves you a wall of buttons or a Float a script has to round.
Two-Axis Puppet A joystick Drives Horizontal and Vertical parameters, each −1…1 (e.g. look/lean, eye direction).
Four-Axis Puppet A joystick Drives four parameters — Up, Right, Down, Left — each 0…1 at once from the stick, so a diagonal blends two (e.g. a 4-way face/emote blend).
Preset Click Sets several parameters at once from one control — enable some, disable others, set an Int/Float. See one-button looks.

Every puppet has two parameter roles

Both are optional to think about — set the first, ignore the second unless you need it:

  • The value(s) the dial/stick drives: Radial → the Rotation parameter (0–1); Two-AxisHorizontal + Vertical (each −1…1); Four-AxisUp/Right/Down/Left (each 0–1, blended — a diagonal drives two).
  • An optional "active while open" main Parameter: if you set the control's Parameter (+ Value), it's held at that Value while the dial is open and restored when it closes — so your animator can react to the dial simply being open (light up a UI, enable an effect), independent of the value. Leave it empty if you don't need it.
  • Direction labels: Two- and Four-Axis puppets each have four optional Direction Labels (Up / Right / Down / Left), each with a name and an icon, shown at the ring's cardinal points while the player dials — set the ones you want in the puppet control's inspector.

Radial Int is the odd one out — one role, on purpose

A Radial Int has no sub-parameters and no "active while open" role. The dial value is the main Parameter (which must be an Int): you set a Min and Max, and turning the dial writes that whole number straight to it, snapping between steps. That directness is the point — it commits an exact integer (great for a discrete selector), where a Radial Puppet gives you a continuous 0–1 Float.

The flow for any control is the same: player activates the control → it sets the parameter → your layer's clip reacts. Only parameters you also declared in Expression Parameters sync to other players.

Built-in menu items

Every action menu also carries system items you don't author: a Utility sub-menu (Avatar Size dial, Emotes, Disable Gestures), plus Default, Reset Avatar, and Exit at the root. See Reset vs Default for what those two do.

Outfits & one-button looks

You often want one control to change a whole look — enable a hat and jacket, hide the shirt, set a face to expression 2. There are two clean ways to do it, and it matters which you pick because "is this outfit active?" gets ambiguous if you do it the wrong way.

Use one Int parameter (e.g. Outfit) and give each look a value: 0, 1, 2, … Your animator controller has one state per value that enables/disables the right objects — the "enable 3, disable 2" work lives in the controller, where it belongs. Then add a simple Toggle per look, each pointing at Outfit with its value.

Why this is the blessed pattern:

  • "Active" is exact. A look is on exactly when Outfit == its value — one source of truth, no half-on states.
  • Mutual exclusivity is free. An Int holds one value, so selecting one outfit automatically deselects the others (their toggles read off).
  • Cheap to sync. One Int (8 of the 512 budget) instead of many Bools.

This works today

Toggles pointing at a shared Int already behave this way — set Outfit=2, and the value-1 outfit's toggle reads off automatically. Clicking the active outfit's toggle sets Outfit=0 ("none").

One dial instead of one toggle per value — the Radial Int control

Once you have that Int, you can let players pick its value with a single Radial Int dial instead of a Toggle per look — they just turn to 0, 1, 2, … and it commits that whole number. It's the natural companion to the Int-selector pattern and scales to ranges (a palette 0–99) where a toggle-per-value would be absurd. If you'd rather not hand-build the animator states, the Avatar Toolkit has a no-code Radial Int Dial feature that mints the Int, the dial, and a state per value for you.

The Preset control: for non-exclusive combos

When a look isn't one-of-a-set — a "party mode" that layers on glasses and a hat and a particle you might also toggle independently — use a Preset control. It applies a matrix of (parameter → value) entries at once. A Preset runs in one of two modes:

  • Apply once (persists) — a one-shot. Click it and it sets every entry; there's no on/off state to track. Great for "apply this look" / "reset to my default fit." Undo via Default / Reset Avatar.
  • Toggling look — a reconcilable on/off. It reads as ON only when every entry is at its on-value, and turning it off clears each entry per its Off mode. If you manually change one of its parameters elsewhere, the Preset honestly reads off (it's no longer fully applied).

Each entry has an Off mode that decides what "off" does to that parameter (toggling presets only):

Off mode On off, the parameter is… Use it for
Set to zero (default) set to 0 a normal on/off object (a hat Bool)
Set to off value set to an explicit value a param whose neutral isn't 0 (a scale Float that rests at 1)
Leave untouched not written at all a param another control owns — so the Preset never stomps it

A Preset only drives parameters — declare them

Every entry's parameter must be a declared, synced Expression Parameter (by exact name) or it won't apply or replicate. A Preset control itself costs nothing extra against the 512 budget — it just drives parameters you already have (so it saves menu slots, not budget). A Bool entry can be set to 0 to disable a default-on mesh (the "disable others" half of an outfit).

Mutually-exclusive Presets (radio groups). Give several toggling Presets the same Radio group name and they become mutually exclusive — selecting one deselects the others (each cleared by its own off-mode). This is the multi-parameter alternative to the Int selector: use it when each look sets several params, not just one Int. Give each member a distinguishing entry (e.g. a shared Int at a different value) so exactly one ever reads as selected.

Capture current values. Pose your avatar the way a look should be — in Play mode with the Avatar Animation Preview driving it — then click Capture current values on the Preset to fill every entry from the avatar's current state, instead of typing each value by hand.

Which do I use?

  • Outfits you switch between, each just an Int state → the Int selector. Exact active-state, mutual exclusivity, cheapest.
  • Outfits you switch between where each sets several params → toggling Presets in a Radio group.
  • Combos you layer on/off, or a "set my whole look" button → a Preset (toggling or apply-once).
  • A single object/expression → a plain Toggle.

Everything stays in sync automatically

Whatever changes a parameter — a Preset, a standalone Toggle, OSC, another player's synced state, or a controller state-behaviour — every control bound to that parameter re-reflects its on/off live while the menu is open. You never have to keep two controls in sync by hand.

Masks: you usually don't need one

A mask is "the list of things a layer is allowed to touch" — humanoid body parts (hands, head, legs) and/or specific transforms (an ear bone, a hat object). An override layer only writes what its mask allows, which is what keeps it from blanking the body.

Social Scape resolves the mask for a layer in this order, and you rarely provide one:

  1. A Mask you set on the descriptor layer — wins if present. Use it only to force a specific scope.
  2. The controller's own internal layer masks — e.g. SS_Gestures has masked left/right-hand layers; Social Scape reads them and derives the avatar-level scope automatically. Author gesture masks once, in the controller.
  3. The objects your clips animate — for a maskless toggle/FX layer, Social Scape scans the controller's clips and scopes the override to exactly the objects they touch (the toggled GameObject, the blendshape mesh, the material). This is why toggles "just work" now with no mask.

All three are derived live in the preview and baked into your avatar at upload, so the in-game result matches what you see in the editor.

The rule of thumb

Poses a body part → put it on Gesture/Action (masking is handled). Flips an object / drives a blendshape / swaps a material → put it on Expressions. Add a mask by hand only to force a scope the auto-derivation gets wrong.

Test it before you upload

You don't have to upload to see how your animations look. Add the SsAvatarPreview component (Social Scape ▸ Avatar Animation Preview) to your avatar and press Play — Social Scape merges and drives your layers exactly as the game will: locomotion, gestures, toggles, and expressions, masks and all.

  1. Add Avatar Animation Preview to the avatar root (next to its AvatarDescriptor).
  2. Enter Play mode.
  3. Use the Avatar Test Studio inspector that appears on the component — three tabs:
    • Gestures — pick a hand gesture per hand (Neutral · Fist · Open · Point · Victory · Rock&Roll · Gun · Thumbs) and drag its weight, like testing in-game hand signs.
    • Menu — your avatar's real expression menu, rendered as a ring. Click toggles, open sub-menus, drag radial/joystick dials (including the Radial Int dial) — every control behaves exactly as the in-game radial will.
    • Simulate — drive the platform's locomotion and state parameters: movement (stick + speed + presets like Walk / Run / Jump), poses (Stand / Crouch / Prone), Seated, Flying, emotes, VR/desktop identity, and Voice/viseme levels — so blend trees and state transitions can be tested, not just toggles.
    • Parameters — a live, searchable table of every parameter; edit a value inline and watch your logic fire. Expression parameters are marked with a ●.
    • Reset restores the avatar's load-time state; Rebuild re-merges the layers after you edit a controller.

Using the Avatar Toolkit? A compile bar appears automatically

If your avatar has Avatar Toolkit features, a bar appears above the tabs — Compile features runs the real build against your live preview avatar so generated toggles and dials show up in the Menu tab too, exactly as they will in-world. See Avatar Toolkit ▸ Testing before you upload.

Because the preview uses the same engine as the in-game runtime, what you see is what players see. Masks (authored and auto-derived) are resolved live here, so you can confirm a gesture only moves the hands — and a toggle turns off correctly — before uploading.

Great for thumbnails

Pose your avatar with the preview, then capture your upload screenshot in that pose. Avatar Animation Preview is a test-only component — Social Scape automatically strips it from the uploaded avatar, so it never ships and never affects other players.

Reset vs Default

The action menu has two restore items, and they're not the same:

  • Default — a soft reset. Restores every parameter to the value the avatar loaded with (your Expression Parameter defaults), on the live avatar. Fast, no rebuild. This is the everyday "put my toggles back."
  • Reset Avatar — a hard reset. Rebuilds the whole animation graph from scratch, then restores the defaults. Use it if something looks stuck after an avatar swap.

Examples

Object toggle (a hat on/off)

  1. Build the toggle controller. One layer named Hat. Two states, Off and On, each playing a tiny clip that animates only the hat GameObject's Is Active property (Off → inactive, On → active). Transition Off ↔ On on a Bool parameter HatToggle.
  2. Leave the controller's Base Layer empty — no body clips. (See gotchas.)
  3. Assign the controller to the avatar's Expressions / FX layer — it overrides just the hat, scoped automatically.
  4. Add HatToggle (Bool) to Expression Parameters. If the hat is enabled by default, set its Default to On (1).
  5. Add a Toggle control to the Expression Menu pointing at HatToggle (Value 1).

Result: the player opens the menu, toggles the hat on/off, and everyone sees it — while walking normally.

Hand gesture (a fist)

  1. Put the gesture clips in a controller assigned to the Gesture layer.
  2. Give its layers a hand mask (left and/or right) inside the controller. Social Scape derives the scope, so it overrides the hands and nothing else.
  3. Drive it from GestureLeft / GestureRight (set by the player's hand input).

Face expression (blendshapes)

Put a smile/blink on the Expressions / FX layer. A clip driving the face mesh's blendshape weights overrides just that mesh's blendshapes (auto-scoped), leaving the body to locomotion.

A multi-outfit selector (an Int)

  1. Author one Expressions controller layer with a state per outfit, each animating the relevant objects, gated on an Int parameter Outfit (0,1,2,…).
  2. Add Outfit (Int) to Expression Parameters; set its Default to the outfit you want on load.
  3. In the menu, add a Sub-menu "Outfits" containing one Toggle/Button per outfit that sets Outfit to that index (Value 0,1,2,…). (A four-way discrete selector is a job for four Buttons or a Preset, not a Four-Axis Puppet — puppets drive continuous 0–1 Floats, not a discrete Int.)

Controlling a layer's weight (emotes)

An Action emote (a dance, a sit-down) should take over the full body while it plays, then hand control back to locomotion. You do that with a Playable Layer Control state behaviour: add it to the emote's animator state (Add Behaviour on the state).

Field What it does
Layer Which merged layer to control (usually Action).
Goal Weight The weight to blend to when the state is entered (1 = full takeover).
Blend Duration Seconds to blend (0 = instant).
Blend On Exit / Exit Weight Blend back to Exit Weight (usually 0) when the emote ends.

So: enter the emote state → Action blends up to 1 (full body) → leave the state → Action blends back to 0 and locomotion returns. This is the clean way to make a full-body emote override everything and release without a T-pose.

Building controllers correctly (gotchas)

A toggle/FX controller must touch ONLY its target

The #1 cause of "my avatar T-poses when I toggle something" is an FX/toggle controller whose Base Layer plays a body clip (or whose states animate body bones). Because the toggle layer's scope is derived from what its clips animate, a stray body curve pulls the body into that layer's override and fights locomotion. Build toggle controllers with an empty Base Layer and clips that animate only the toggle's object / blendshape / material.

  • Name parameters identically across the animator controller, Expression Parameters, and the menu — an exact string match (case-sensitive) is what wires them together.
  • Set each Expression Parameter's Default to the toggle's starting state — the avatar loads at the Expression default, which overrides the controller's default. (The inspector warns on mismatches.)
  • Hand poses → Gesture. Object/blendshape/material toggles → Expressions. That placement selects override-vs-additive and the mask for you.
  • Declare every player-facing parameter in Expression Parameters and keep it Synced, or other players won't see it change.
  • Keep it lean. Extra layers and high-bone controllers add cost — see the performance tier.

Component class names (for scripts and search): SSExpressionParameters, SSExpressionsMenu, SSAvatarParameterDriver, SSPlayableLayerControl.