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_Start
HighFive_Start_Prop
HighFive_Avatar
HighFive_Prop
HighFive_AvatarOther
Rationale
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
: so
outcomes.length > 1 && randomizeOutcomes === true
:
ro
mo
outcomes
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.