@spatius/avatarkit 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,13 @@
1
+ import { CharacterMeta } from '../types';
2
+ export declare class Avatar {
3
+ readonly id: string;
4
+ private characterMeta;
5
+ private resources;
6
+ /** Local-only field: tracks whether cached model is "standard" or "compressed". */
7
+ _cachedModelType: string;
8
+ /**
9
+ * Get character metadata
10
+ * @returns Character metadata, including all configuration information (version, resource URLs, camera config, etc.)
11
+ */
12
+ getCharacterMeta(): CharacterMeta;
13
+ }
@@ -0,0 +1,140 @@
1
+ import { Avatar } from './Avatar';
2
+ import { ConnectionState, AvatarError, DrivingServiceMode, ConversationState, PostProcessingConfig } from '../types';
3
+ import { FrameRateInfo } from '../performance/FrameRateMonitor';
4
+ export declare class AvatarController {
5
+ private networkLayer?;
6
+ private readonly playbackMode;
7
+ private isStartingPlayback;
8
+ private currentConversationId;
9
+ private reqEnd;
10
+ onConnectionState: ((state: ConnectionState) => void) | null;
11
+ onConversationState: ((state: ConversationState) => void) | null;
12
+ onError: ((error: AvatarError) => void) | null;
13
+ private eventListeners;
14
+ private readonly frameRateMonitor;
15
+ /** Frame rate monitoring callback. Fires with aggregated metrics from a 2-second sliding window. */
16
+ get onFrameRateInfo(): ((info: FrameRateInfo) => void) | null;
17
+ set onFrameRateInfo(value: ((info: FrameRateInfo) => void) | null);
18
+ /** Whether frame rate monitoring is enabled. Default is false (zero overhead when disabled). */
19
+ get frameRateMonitorEnabled(): boolean;
20
+ set frameRateMonitorEnabled(value: boolean);
21
+ private renderCallback?;
22
+ private characterHandle;
23
+ private characterId;
24
+ private postProcessingConfig;
25
+ private playbackLoopId;
26
+ private playbackLoopGeneration;
27
+ private lastRenderedFrameIndex;
28
+ private keyframesOffset;
29
+ private readonly MAX_KEYFRAMES;
30
+ private readonly KEYFRAMES_CLEANUP_THRESHOLD;
31
+ private lastSyncLogTime;
32
+ private lastOutOfBoundsState;
33
+ private isFallbackMode;
34
+ private frameStarvationEvents;
35
+ private isFrameStarved;
36
+ private playbackStuckCheckState;
37
+ private readonly MAX_AUDIO_TIME_ZERO_COUNT;
38
+ private readonly MAX_AUDIO_TIME_STUCK_COUNT;
39
+ private readonly AUDIO_TIME_STUCK_THRESHOLD;
40
+ private hostModeMetrics;
41
+ private readonly audioBytesPerSecond;
42
+ constructor(avatar: Avatar, options?: {
43
+ playbackMode?: DrivingServiceMode;
44
+ });
45
+ private handleVisibilityChange;
46
+ private shouldReportPlaybackStats;
47
+ private _getDeviceScoreProps;
48
+ /**
49
+ * Get current conversation ID
50
+ * Returns the current conversation ID for the active audio session
51
+ * @returns Current conversation ID, or null if no active session
52
+ */
53
+ getCurrentConversationId(): string | null;
54
+ /**
55
+ * Initialize audio context (must be called in user gesture context)
56
+ *
57
+ * This method must be called before any audio operations (send, yieldAudioData, etc.)
58
+ * to ensure AudioContext is created and initialized in a user gesture context.
59
+ *
60
+ * @example
61
+ * // In user click handler
62
+ * button.addEventListener('click', async () => {
63
+ * await avatarView.controller.initializeAudioContext()
64
+ * // Now you can safely use send() or yieldAudioData()
65
+ * })
66
+ */
67
+ initializeAudioContext(): Promise<void>;
68
+ /**
69
+ * Start service (SDK mode only)
70
+ */
71
+ start(): Promise<void>;
72
+ /**
73
+ * Send audio to server (SDK mode only)
74
+ * Also cache to data layer for playback
75
+ * @returns conversationId - Conversation ID for this audio session
76
+ */
77
+ send(audioData: ArrayBuffer, end?: boolean): string | null;
78
+ /**
79
+ * Close service (SDK mode only)
80
+ */
81
+ close(): void;
82
+ /**
83
+ * Send audio data (host mode)
84
+ * Stream additional audio data after playback()
85
+ * @returns conversationId - Conversation ID for this audio session
86
+ */
87
+ yieldAudioData(data: Uint8Array, isLast?: boolean): string | null;
88
+ /**
89
+ * Send animation keyframes (host mode or SDK mode)
90
+ * Stream additional animation data after playback()
91
+ *
92
+ * Public API: accepts binary data array (protobuf encoded Message array)
93
+ * @param keyframesDataArray - Animation keyframes binary data array (each element is a protobuf encoded Message) or empty array to trigger audio-only mode
94
+ * @param conversationId - Conversation ID (required). If conversationId doesn't match current conversationId, keyframes will be discarded.
95
+ * Use getCurrentConversationId() to get the current conversationId.
96
+ * @returns `true` if the server has sent all animation data for this conversation (end signal received), `false` otherwise.
97
+ */
98
+ yieldFramesData(keyframesDataArray: (Uint8Array | ArrayBuffer)[], conversationId: string): boolean;
99
+ /**
100
+ * Pause playback (can be resumed later)
101
+ * Pause audio playback and stop render loop, but preserve all state (keyframes, audio buffers, etc.)
102
+ */
103
+ pause(): void;
104
+ /**
105
+ * Resume playback (from paused state)
106
+ * Resume audio playback and restart render loop
107
+ * Animation will continue from paused frame (because animation time base comes from audio, will auto-sync)
108
+ */
109
+ resume(): Promise<void>;
110
+ /**
111
+ * Interrupt current playback
112
+ */
113
+ interrupt(): void;
114
+ /**
115
+ * Clear all data and resources
116
+ */
117
+ clear(): void;
118
+ /**
119
+ * Get point cloud count of the current avatar
120
+ * @returns Point cloud count, or null if avatar is not loaded
121
+ */
122
+ getPointCount(): number | null;
123
+ /**
124
+ * Set post-processing configuration
125
+ * These parameters will be applied in real-time to animation parameters returned by the server
126
+ * @param config Post-processing configuration, or null to clear
127
+ */
128
+ setPostProcessingConfig(config: PostProcessingConfig | null): void;
129
+ /**
130
+ * Set audio playback volume
131
+ * Note: This only controls the avatar audio player volume, not the system volume
132
+ * @param volume Volume value, range from 0.0 to 1.0 (0.0 = mute, 1.0 = max volume)
133
+ */
134
+ setVolume(volume: number): void;
135
+ /**
136
+ * Get current audio playback volume
137
+ * @returns Current volume value (0.0 - 1.0)
138
+ */
139
+ getVolume(): number;
140
+ }
@@ -0,0 +1,46 @@
1
+ import { LoadProgressInfo } from '../types';
2
+ import { Avatar } from './Avatar';
3
+ export declare class AvatarManager {
4
+ private static _instance;
5
+ private avatarDownloader;
6
+ private avatarCache;
7
+ /** 下载队列:FIFO 顺序 */
8
+ private downloadQueue;
9
+ /** 当前正在执行的任务 */
10
+ private currentTask;
11
+ /** 任务索引:快速查找 id 对应的任务 */
12
+ private taskIndex;
13
+ /**
14
+ * Access via global singleton
15
+ */
16
+ static get shared(): AvatarManager;
17
+ /**
18
+ * Load avatar
19
+ * @param id Avatar ID
20
+ * @param onProgress Progress callback
21
+ * @param useCompressedModel Use compressed model resource (~30% of original size, with minor quality degradation). Defaults to false.
22
+ * @returns Promise<Avatar>
23
+ */
24
+ load(id: string, onProgress?: (progress: LoadProgressInfo) => void, useCompressedModel?: boolean): Promise<Avatar>;
25
+ /**
26
+ * Cancel a pending or running download task
27
+ * @param id Avatar ID to cancel
28
+ * @returns true if task was found and cancelled
29
+ */
30
+ cancelLoad(id: string): boolean;
31
+ /**
32
+ * Get cached avatar
33
+ * @param id Avatar ID
34
+ * @returns Avatar instance, or undefined if not in cache
35
+ */
36
+ retrieve(id: string): Avatar | undefined;
37
+ /**
38
+ * Clear cached avatar for specified ID
39
+ * @param id Avatar ID
40
+ */
41
+ clear(id: string): void;
42
+ /**
43
+ * Clear all avatar cache and cancel all tasks
44
+ */
45
+ clearAll(): void;
46
+ }
@@ -0,0 +1,57 @@
1
+ import { Configuration } from '../types';
2
+ export declare class AvatarSDK {
3
+ private static _initializationState;
4
+ private static _initializingPromise;
5
+ private static _configuration;
6
+ private static readonly _version;
7
+ private static _avatarCore;
8
+ private static _cachedDeviceScore;
9
+ private static _rendererBackend;
10
+ /**
11
+ * Initialize SDK
12
+ * @param appId Application ID to be included in both HTTP Headers and WebSocket Headers
13
+ * @param configuration Configuration parameters
14
+ */
15
+ static initialize(appId: string, configuration: Configuration): Promise<void>;
16
+ private static _initializeInternal;
17
+ /**
18
+ * Set sessionToken
19
+ * Developer Client -> Developer Server -> AvatarKit Server -> return sessionToken (max 1 hour validity)
20
+ * Include in WebSocket Headers for avatar WebSocket service authentication
21
+ */
22
+ static setSessionToken(token: string): void;
23
+ /**
24
+ * Set userId
25
+ * Optional interface for developers, SDK includes this in telemetry logs
26
+ */
27
+ static setUserId(userId: string): void;
28
+ static get isInitialized(): boolean;
29
+ static get appId(): string | null;
30
+ static get configuration(): Configuration | null;
31
+ static get sessionToken(): string | null;
32
+ static get userId(): string | null;
33
+ static get version(): string;
34
+ /**
35
+ * Cleanup resources
36
+ */
37
+ static cleanup(): void;
38
+ /**
39
+ * Device performance score (CPU + GPU).
40
+ * CPU: matrix multiply throughput in Web Worker (warmup 1s + measure 1s).
41
+ * GPU: WebGPU compute shader throughput (warmup 1s + measure 1s).
42
+ * Returns 0 for GPU if WebGPU is not available.
43
+ */
44
+ static deviceScore(): Promise<{
45
+ cpuScore: number;
46
+ gpuScore: number;
47
+ }>;
48
+ private static readonly CPU_SCORE_THRESHOLD;
49
+ private static readonly GPU_SCORE_THRESHOLD;
50
+ /**
51
+ * Check if the current device can run the avatar.
52
+ * Runs a ~2s benchmark, reports device info and scores to PostHog.
53
+ * Currently always returns true — thresholds will be calibrated from production data.
54
+ */
55
+ static isDeviceSupported(): Promise<boolean>;
56
+ private static _getGPURenderer;
57
+ }
@@ -0,0 +1,170 @@
1
+ import { CameraConfig } from '../types';
2
+ import { Avatar } from './Avatar';
3
+ import { AvatarController } from './AvatarController';
4
+ export declare class AvatarView {
5
+ private readonly avatarController;
6
+ private readonly avatar;
7
+ onFirstRendering?: () => void;
8
+ private canvas;
9
+ private renderSystem;
10
+ private isInitialized;
11
+ private cameraConfig;
12
+ private renderingState;
13
+ private currentKeyframes;
14
+ private lastRenderedFrameIndex;
15
+ private lastRealtimeProtoFrame;
16
+ private idleAnimationLoopId;
17
+ private realtimeAnimationLoopId;
18
+ private resizeObserver;
19
+ private onWindowResize;
20
+ private frameCount;
21
+ private lastFpsUpdate;
22
+ private currentFPS;
23
+ private transitionKeyframes;
24
+ private transitionStartTime;
25
+ private readonly startTransitionDurationMs;
26
+ private readonly endTransitionDurationMs;
27
+ private cachedIdleFirstFrame;
28
+ private idleCurrentFrameIndex;
29
+ private currentPlayingFrame;
30
+ private characterHandle;
31
+ private characterId;
32
+ private isPureRenderingMode;
33
+ private _renderingEnabled;
34
+ private avatarActiveTimer;
35
+ private readonly AVATAR_ACTIVE_INTERVAL;
36
+ /**
37
+ * Constructor
38
+ * Creates a unified AvatarController, internally composes network layer based on configuration
39
+ * @param avatar - Avatar instance
40
+ * @param container - Canvas container element (required)
41
+ */
42
+ constructor(avatar: Avatar, container: HTMLElement);
43
+ /**
44
+ * Get controller (public interface)
45
+ */
46
+ get controller(): AvatarController;
47
+ /**
48
+ * Cleanup view resources
49
+ * Closes avatarController and cleans up all related resources
50
+ */
51
+ dispose(): void;
52
+ /**
53
+ * 获取相机配置
54
+ */
55
+ getCameraConfig(): CameraConfig | null;
56
+ /**
57
+ * 更新相机配置
58
+ */
59
+ updateCameraConfig(cameraConfig: CameraConfig): void;
60
+ /**
61
+ * Render a single animation frame from raw protobuf data.
62
+ *
63
+ * Decodes the protobuf Message internally and renders the first keyframe.
64
+ * This is the preferred method for RTC consumers that receive raw animation bytes.
65
+ *
66
+ * @param data - Raw protobuf bytes (a single Message containing ServerResponseAnimation)
67
+ */
68
+ renderFromProtobuf(data: ArrayBuffer | Uint8Array): Promise<void>;
69
+ /**
70
+ * Play a transition from idle to the target frame in the protobuf data,
71
+ * then resolve when the transition is complete.
72
+ *
73
+ * The transition frames are generated and played internally at 25fps.
74
+ * The caller should wait for the returned Promise before pushing streaming frames.
75
+ *
76
+ * @param data - Raw protobuf bytes containing the target frame
77
+ * @param frameCount - Number of transition frames to generate
78
+ * @returns Promise that resolves when the transition playback finishes
79
+ */
80
+ playTransitionFromProtobuf(data: ArrayBuffer | Uint8Array, frameCount: number): Promise<void>;
81
+ /**
82
+ * Play a transition from current animation back to idle,
83
+ * then start the idle animation loop.
84
+ *
85
+ * Generates reverse transition frames from idle→lastFrame, reverses them,
86
+ * plays at 25fps, then starts idle.
87
+ *
88
+ * @param data - Raw protobuf bytes containing the last animation frame
89
+ * @param frameCount - Number of transition frames to generate
90
+ * @returns Promise that resolves when idle animation starts
91
+ */
92
+ playTransitionToIdleFromProtobuf(data: ArrayBuffer | Uint8Array, frameCount: number): Promise<void>;
93
+ /**
94
+ * Start idle animation (stop pure rendering mode, resume idle loop).
95
+ */
96
+ startIdle(): void;
97
+ /**
98
+ * Generate transition frames from protobuf data.
99
+ *
100
+ * Decodes the protobuf, extracts the target keyframe, and generates
101
+ * transition frames from the current idle position to the target.
102
+ * The caller is responsible for playing the returned frames at the desired cadence.
103
+ *
104
+ * @param data - Raw protobuf bytes containing the target frame
105
+ * @param frameCount - Number of transition frames to generate
106
+ * @param options - Additional options
107
+ * @param options.useLinear - Use linear interpolation (default: true)
108
+ * @returns Array of opaque keyframe data for sequential playback
109
+ */
110
+ generateTransitionFromProtobuf(data: ArrayBuffer | Uint8Array, frameCount: number, options?: {
111
+ useLinear?: boolean;
112
+ }): Promise<unknown[]>;
113
+ /**
114
+ * Cancel any in-progress frame sequence playback.
115
+ * Called by renderFromProtobuf when streaming frames arrive during transition.
116
+ */
117
+ cancelFrameSequence(): void;
118
+ /**
119
+ * Pause rendering loop
120
+ *
121
+ * When called:
122
+ * - Rendering loop stops (no GPU/canvas updates)
123
+ * - Audio playback continues normally
124
+ * - Animation state machine continues running
125
+ *
126
+ * Use `resumeRendering()` to resume rendering.
127
+ *
128
+ * @example
129
+ * // Stop rendering to save GPU resources (audio continues)
130
+ * avatarView.pauseRendering()
131
+ */
132
+ pauseRendering(): void;
133
+ /**
134
+ * Resume rendering loop
135
+ *
136
+ * When called:
137
+ * - Rendering loop resumes from current state
138
+ * - If in Idle state, immediately renders current frame to restore display
139
+ *
140
+ * @example
141
+ * // Resume rendering
142
+ * avatarView.resumeRendering()
143
+ */
144
+ resumeRendering(): void;
145
+ /**
146
+ * Check if rendering is currently enabled
147
+ * @returns true if rendering is enabled, false if paused
148
+ */
149
+ isRenderingEnabled(): boolean;
150
+ /**
151
+ * Get or set avatar transform in canvas
152
+ *
153
+ * @example
154
+ * // Get current transform
155
+ * const current = avatarView.avatarTransform
156
+ *
157
+ * // Set transform
158
+ * avatarView.avatarTransform = { x: 0.5, y: 0, scale: 2.0 }
159
+ */
160
+ get avatarTransform(): {
161
+ x: number;
162
+ y: number;
163
+ scale: number;
164
+ };
165
+ set avatarTransform(value: {
166
+ x: number;
167
+ y: number;
168
+ scale: number;
169
+ });
170
+ }