We are implementing a new feature named Social Emotes, which will allow multiple players to interact with this new kind of emote.
These new emotes will have a new property named outcomes, which lets authors
define the animation to execute and specify whether it loops or not.
loop and additionalProperties (sound/geometry) while
keeping richer data off-chain. Social Emotes require multiple animation outcomes for better
player interaction, but we want to avoid bloating the on-chain metadata.
To ensure consistency across exported GLB clips, we suggest the use of the following pattern:
<Action>_(Start | Start_Prop | Avatar | Prop | AvatarOther)
HighFive,
WaveHello).
_, followed by one of the
predefined roles (Start, Start_Prop, Avatar,
Prop, AvatarOther).
Start_Prop, AvatarOther).
Examples:
HighFive_StartHighFive_Start_PropHighFive_AvatarHighFive_PropHighFive_AvatarOtherRationale
export type ArmatureId = "Armature" | "Armature_Prop" | "Armature_Other"
export type EmoteClip = {
animation: string // GLB clip name "HighFive_Avatar" (suggested, not enforced)
loop: boolean
}
export type StartAnimation = {
Armature: EmoteClip
Armature_Prop?: EmoteClip
}
export type OutcomeGroup = {
title: string
// Any subset of armatures; validated at runtime to ensure at least one
clips: Partial<Record<ArmatureId, EmoteClip>>
}
export type EmoteDataADR287 = {
category: EmoteCategory
representations: EmoteRepresentationADR74[]
tags: string[]
loop: boolean
startAnimation: StartAnimation
randomizeOutcomes: boolean
outcomes: OutcomeGroup[]
}
EmoteCategory and EmoteRepresentationADR74 to avoid
churn.
loop for compatibility, but
emote SHOULD prefer the selected outcome’s loop if present.
Example (two-armature outcomes):
const emoteWithADR287Data = {
// ...,
emoteDataADR287: {
// ...,
startAnimation: {
Armature: {
animation: "HighFive_Start",
loop: true
}
},
randomizeOutcomes: false,
outcomes: [
{
title: "High Five",
clips: {
Armature: {
animation: "HighFive_Avatar",
loop: false
},
Armature_Other: {
animation: "HighFive_AvatarOther",
loop: false
}
}
}
]
}
}
outcomes.length === 1 always that outcome.outcomes.length > 1 and no
randomizeOutcomes: true deterministic selection policy
(implementation-defined).
outcomes.length > 1 and
randomizeOutcomes: true choose randomly among all outcomes.
ADR-74 extended the metadata to include loop and then
additionalProperties (sound/geometry) by appending fields on the right to avoid
breaking older parsers. We follow the same pattern:
ADR-74 (current)
${version}:${type}:${name}:${description}:${category}:${bodyShapeTypes}:${loop}:${additionalProperties}
ADR-287 (append-only)
${version}:${type}:${name}:${description}:${category}:${bodyShapeTypes}:${loop}:${additionalProperties}:${outcomeType}
Where:
outcomeType ∈ { so | mo | ro }outcomeType is stored on-chain. The full
outcomes[] list remains off-chain in the emote JSON.
outcomeType from the schema
outcomes.length === 1: sooutcomes.length > 1 && randomizeOutcomes === true:
ro
mooutcomes is empty (legacy), treat as so using the default
animation.
export type Emote = EmoteADR74 | EmoteADR287
export type EmoteADR287 = BaseItem &
(StandardProps | ThirdPartyProps) & { emoteDataADR287: EmoteDataADR287 }
(Union pattern follows ADR-74’s versioned types approach.)
armature MUST be a non-empty string.armature MUST be unique across all clips (no
duplicates).
animation MUST be a non-empty string.loop MUST be a boolean.outcomeType MUST be consistent with the off-chain
outcomes[] derivation.
additionalProperties continues to accept
s | g | sg (sound/geometry) as per ADR-74.
additionalProperties MUST continue to function.
Random outcomes (off-chain)
const emoteWithADR287Data = {
// ...,
emoteDataADR287: {
// ...,
startAnimation: {
Armature: {
animation: "Hug_Start",
loop: true
}
},
randomizeOutcomes: true,
outcomes: [
{
title: "Hug Short",
clips: {
Armature: {
animation: "HugShort_Avatar",
loop: false
},
Armature_Other: {
animation: "HugShort_AvatarOther",
loop: false
}
}
},
{
title: "Hug Long",
clips: {
Armature: {
animation: "HugLong_Avatar",
loop: false
},
Armature_Other: {
animation: "HugLong_AvatarOther",
loop: false
}
}
}
]
}
}
// outcomeType => "ro"
Resulting on-chain metadata (example)
${version}:${type}:${name}:${description}:${category}:${bodyShapeTypes}:${loop}:${additionalProperties}:ro
Note: The actual list of
outcomes[]is not encoded on-chain.