export type AvatarAnimationContext = Readonly<{
frame: number;
fps: number;
focused: boolean;
hasMultipleCharacters: boolean;
}>;
export type AvatarAnimation = (context: AvatarAnimationContext) => number;
const gentleBobAmplitude = ({focused}: AvatarAnimationContext) =>
focused ? 10 : 3.5;
export const idleAvatarAnimations = {
none: () => 0,
gentleBob: (context) =>
Math.sin((context.frame / context.fps) * Math.PI * 2) *
gentleBobAmplitude(context),
} satisfies Record<string, AvatarAnimation>;
export type IdleAvatarAnimationType = keyof typeof idleAvatarAnimations;
export const speakingAvatarAnimations = {
none: () => 0,
gentleBob: idleAvatarAnimations.gentleBob,
quickHop: ({frame, fps}) => {
const cycleFrames = Math.max(1, Math.round(fps * 0.25));
const progress = (frame % cycleFrames) / cycleFrames;
return -Math.sin(progress * Math.PI) * 7;
},
} satisfies Record<string, AvatarAnimation>;
export type SpeakingAvatarAnimationType = keyof typeof speakingAvatarAnimations;