@voqalize/avatar 0.4.4 → 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.
Files changed (180) hide show
  1. package/README.md +65 -154
  2. package/dist/index.d.ts +37 -26
  3. package/dist/index.js +48 -25
  4. package/dist/pin.d.ts +2 -0
  5. package/dist/pin.js +3 -0
  6. package/dist/react.d.ts +21 -6
  7. package/dist/react.js +24 -6
  8. package/package.json +8 -67
  9. package/LICENSE-APACHE-2.0 +0 -201
  10. package/LICENSE-CC-BY-4.0 +0 -396
  11. package/assets/README.md +0 -60
  12. package/assets/tanvi.glb +0 -0
  13. package/assets/tanya.glb +0 -0
  14. package/assets/tara.glb +0 -0
  15. package/assets/tess.glb +0 -0
  16. package/assets/tushar.glb +0 -0
  17. package/client/Avatar.tsx +0 -57
  18. package/client/AvatarClient.ts +0 -818
  19. package/client/createAvatar.ts +0 -177
  20. package/client/index.ts +0 -44
  21. package/client/internal.ts +0 -132
  22. package/client/playout.ts +0 -95
  23. package/client/react.ts +0 -13
  24. package/client/supports.ts +0 -27
  25. package/client/three/assets.ts +0 -26
  26. package/client/three/budgets.ts +0 -27
  27. package/client/three/character-rig.ts +0 -1746
  28. package/client/three/createCharacter.ts +0 -109
  29. package/client/three/holds.ts +0 -69
  30. package/client/three/internal.ts +0 -55
  31. package/client/three/motion-limits.json +0 -124
  32. package/client/three/sequences.ts +0 -281
  33. package/client/three/tanvi-asset.ts +0 -6
  34. package/client/three/tanvi.ts +0 -30
  35. package/client/three/tanya-asset.ts +0 -6
  36. package/client/three/tanya.ts +0 -29
  37. package/client/three/tara-asset.ts +0 -25
  38. package/client/three/tara.ts +0 -36
  39. package/client/three/tess-asset.ts +0 -6
  40. package/client/three/tess.ts +0 -30
  41. package/client/three/tushar-asset.ts +0 -6
  42. package/client/three/tushar.ts +0 -28
  43. package/client/types.ts +0 -235
  44. package/client/useAvatar.ts +0 -74
  45. package/dist/Avatar.d.ts +0 -44
  46. package/dist/Avatar.d.ts.map +0 -1
  47. package/dist/Avatar.js +0 -7
  48. package/dist/Avatar.js.map +0 -1
  49. package/dist/AvatarClient.d.ts +0 -274
  50. package/dist/AvatarClient.d.ts.map +0 -1
  51. package/dist/AvatarClient.js +0 -712
  52. package/dist/AvatarClient.js.map +0 -1
  53. package/dist/createAvatar.d.ts +0 -135
  54. package/dist/createAvatar.d.ts.map +0 -1
  55. package/dist/createAvatar.js +0 -75
  56. package/dist/createAvatar.js.map +0 -1
  57. package/dist/index.d.ts.map +0 -1
  58. package/dist/index.js.map +0 -1
  59. package/dist/internal.d.ts +0 -34
  60. package/dist/internal.d.ts.map +0 -1
  61. package/dist/internal.js +0 -56
  62. package/dist/internal.js.map +0 -1
  63. package/dist/playout.d.ts +0 -42
  64. package/dist/playout.d.ts.map +0 -1
  65. package/dist/playout.js +0 -85
  66. package/dist/playout.js.map +0 -1
  67. package/dist/react.d.ts.map +0 -1
  68. package/dist/react.js.map +0 -1
  69. package/dist/supports.d.ts +0 -19
  70. package/dist/supports.d.ts.map +0 -1
  71. package/dist/supports.js +0 -25
  72. package/dist/supports.js.map +0 -1
  73. package/dist/three/assets.d.ts +0 -10
  74. package/dist/three/assets.d.ts.map +0 -1
  75. package/dist/three/assets.js +0 -23
  76. package/dist/three/assets.js.map +0 -1
  77. package/dist/three/budgets.d.ts +0 -17
  78. package/dist/three/budgets.d.ts.map +0 -1
  79. package/dist/three/budgets.js +0 -21
  80. package/dist/three/budgets.js.map +0 -1
  81. package/dist/three/character-rig.d.ts +0 -366
  82. package/dist/three/character-rig.d.ts.map +0 -1
  83. package/dist/three/character-rig.js +0 -1644
  84. package/dist/three/character-rig.js.map +0 -1
  85. package/dist/three/createCharacter.d.ts +0 -60
  86. package/dist/three/createCharacter.d.ts.map +0 -1
  87. package/dist/three/createCharacter.js +0 -84
  88. package/dist/three/createCharacter.js.map +0 -1
  89. package/dist/three/holds.d.ts +0 -38
  90. package/dist/three/holds.d.ts.map +0 -1
  91. package/dist/three/holds.js +0 -61
  92. package/dist/three/holds.js.map +0 -1
  93. package/dist/three/internal.d.ts +0 -52
  94. package/dist/three/internal.d.ts.map +0 -1
  95. package/dist/three/internal.js +0 -49
  96. package/dist/three/internal.js.map +0 -1
  97. package/dist/three/motion-limits.json +0 -124
  98. package/dist/three/sequences.d.ts +0 -71
  99. package/dist/three/sequences.d.ts.map +0 -1
  100. package/dist/three/sequences.js +0 -262
  101. package/dist/three/sequences.js.map +0 -1
  102. package/dist/three/tanvi-asset.d.ts +0 -7
  103. package/dist/three/tanvi-asset.d.ts.map +0 -1
  104. package/dist/three/tanvi-asset.js +0 -7
  105. package/dist/three/tanvi-asset.js.map +0 -1
  106. package/dist/three/tanvi.d.ts +0 -24
  107. package/dist/three/tanvi.d.ts.map +0 -1
  108. package/dist/three/tanvi.js +0 -24
  109. package/dist/three/tanvi.js.map +0 -1
  110. package/dist/three/tanya-asset.d.ts +0 -7
  111. package/dist/three/tanya-asset.d.ts.map +0 -1
  112. package/dist/three/tanya-asset.js +0 -7
  113. package/dist/three/tanya-asset.js.map +0 -1
  114. package/dist/three/tanya.d.ts +0 -23
  115. package/dist/three/tanya.d.ts.map +0 -1
  116. package/dist/three/tanya.js +0 -23
  117. package/dist/three/tanya.js.map +0 -1
  118. package/dist/three/tara-asset.d.ts +0 -26
  119. package/dist/three/tara-asset.d.ts.map +0 -1
  120. package/dist/three/tara-asset.js +0 -26
  121. package/dist/three/tara-asset.js.map +0 -1
  122. package/dist/three/tara.d.ts +0 -30
  123. package/dist/three/tara.d.ts.map +0 -1
  124. package/dist/three/tara.js +0 -30
  125. package/dist/three/tara.js.map +0 -1
  126. package/dist/three/tess-asset.d.ts +0 -7
  127. package/dist/three/tess-asset.d.ts.map +0 -1
  128. package/dist/three/tess-asset.js +0 -7
  129. package/dist/three/tess-asset.js.map +0 -1
  130. package/dist/three/tess.d.ts +0 -24
  131. package/dist/three/tess.d.ts.map +0 -1
  132. package/dist/three/tess.js +0 -24
  133. package/dist/three/tess.js.map +0 -1
  134. package/dist/three/tushar-asset.d.ts +0 -7
  135. package/dist/three/tushar-asset.d.ts.map +0 -1
  136. package/dist/three/tushar-asset.js +0 -7
  137. package/dist/three/tushar-asset.js.map +0 -1
  138. package/dist/three/tushar.d.ts +0 -22
  139. package/dist/three/tushar.d.ts.map +0 -1
  140. package/dist/three/tushar.js +0 -22
  141. package/dist/three/tushar.js.map +0 -1
  142. package/dist/types.d.ts +0 -149
  143. package/dist/types.d.ts.map +0 -1
  144. package/dist/types.js +0 -140
  145. package/dist/types.js.map +0 -1
  146. package/dist/useAvatar.d.ts +0 -39
  147. package/dist/useAvatar.d.ts.map +0 -1
  148. package/dist/useAvatar.js +0 -35
  149. package/dist/useAvatar.js.map +0 -1
  150. package/src/avatar.d.ts +0 -436
  151. package/src/avatar.js +0 -1378
  152. package/src/behavior.d.ts +0 -42
  153. package/src/behavior.js +0 -114
  154. package/src/camera.js +0 -29
  155. package/src/clips.js +0 -155
  156. package/src/conformance.js +0 -119
  157. package/src/emotions.js +0 -58
  158. package/src/face-core.js +0 -180
  159. package/src/face-myna.d.ts +0 -7
  160. package/src/face-myna.js +0 -786
  161. package/src/face-peep-control-plane.js +0 -167
  162. package/src/face-peep.d.ts +0 -7
  163. package/src/face-peep.js +0 -925
  164. package/src/face-wren.d.ts +0 -7
  165. package/src/face-wren.js +0 -468
  166. package/src/faces.d.ts +0 -12
  167. package/src/faces.js +0 -53
  168. package/src/gaze.js +0 -504
  169. package/src/hand.js +0 -709
  170. package/src/head.js +0 -134
  171. package/src/idle.js +0 -668
  172. package/src/interjections.js +0 -747
  173. package/src/line-art.js +0 -137
  174. package/src/params.js +0 -180
  175. package/src/perform.js +0 -107
  176. package/src/prosody.js +0 -741
  177. package/src/rig.d.ts +0 -31
  178. package/src/rig.js +0 -32
  179. package/src/speech-timing.js +0 -51
  180. package/src/visemes.js +0 -410
package/src/avatar.d.ts DELETED
@@ -1,436 +0,0 @@
1
- /**
2
- * avatar.d.ts — hand-maintained types for the widget's public surface.
3
- *
4
- * The widget is dependency-free ES modules with no build step, so there is no
5
- * compiler to derive these from; this file is written by hand against
6
- * `docs/internal-mixer.md` (the mixer's driving API) and a
7
- * reading of `avatar.js`. It lives here rather than in a consumer because it
8
- * is only correct next to the code it describes — the previous copy lived in
9
- * a vendored tree two repos away and went stale the first time an enum grew.
10
- *
11
- * It is deliberately not a conversion of the widget to TypeScript. The
12
- * string-keyed enums (state / gaze / emotion / semantic action ids) are closed
13
- * literal unions, and there is no `| string` escape hatch on any setter: the
14
- * widget enforces the same enums at runtime — unknown state and action ids
15
- * throw, unknown emotion and gaze fall back silently — so a caller who wants
16
- * one of these has a name that is in the union or a bug. Keeping the two in
17
- * step is this file's whole job; a stale `.d.ts` must never claim to be
18
- * stricter than the code it describes, so widen the runtime first.
19
- */
20
-
21
- /** `STATE_NAMES` — see docs/internal-mixer.md § States. */
22
- export type AvatarStateName =
23
- | "IDLE"
24
- | "LISTENING"
25
- | "THINKING"
26
- | "SPEAKING"
27
- | "WORKING"
28
- | "REVIEWING_SCREEN"
29
- | "WAITING_FOR_USER"
30
- | "TYPING_CHAT"
31
- | "DISTRACTED"
32
- | "SEARCHING_SCREEN"
33
- | "CANT_HEAR"
34
- | "TAKING_FLOOR"
35
- | "WANTS_IN"
36
- | "YIELDED"
37
- | "DEGRADED"
38
- | "OFFLINE";
39
-
40
- /** `EMOTION_NAMES` — see docs/internal-mixer.md § Emotion. */
41
- export type AvatarEmotionName =
42
- | "neutral"
43
- | "warm"
44
- | "curious"
45
- | "concerned"
46
- | "encouraging"
47
- | "thoughtful";
48
-
49
- /** `GAZE_NAMES` — see docs/internal-mixer.md § Gaze. `"CUSTOM"` is the escape hatch (any name + a `custom` point works). */
50
- export type AvatarGazeName =
51
- | "USER"
52
- | "USER_EAR"
53
- | "SCREEN_CENTER"
54
- | "SCREEN_LEFT"
55
- | "SCREEN_RIGHT"
56
- | "SCREEN_TOP"
57
- | "SCREEN_BOTTOM"
58
- | "SCREEN_WORK"
59
- | "NOTES"
60
- | "AWAY_THINKING"
61
- | "AWAY_RIGHT"
62
- | "AWAY_DOWN"
63
- | "AWAY_SIDE"
64
- | "CUSTOM";
65
-
66
- /** The complete server-addressable action contract. Names are semantic, not
67
- * anatomical: a future rig may implement `ACK_NOD` without a literal nod. */
68
- export type AvatarActionId =
69
- | "ACK_RECEIVE"
70
- | "ACK_NOD"
71
- | "RESPONSE_INTERRUPTED"
72
- | "GESTURE_GREET"
73
- | "GESTURE_GOODBYE"
74
- | "GESTURE_APPROVE"
75
- | "GESTURE_WAIT";
76
-
77
- /** Rhubarb Lip Sync letter — see docs/internal-mixer.md § Speech. */
78
- export type VisemeLetter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "X";
79
-
80
- /** One viseme cue: `t` is a ms offset into the utterance, `i` is optional 0..1 loudness. */
81
- export interface Cue {
82
- t: number;
83
- v: VisemeLetter;
84
- /** 0..1. Absent means full. */
85
- i?: number;
86
- }
87
-
88
- /** A pose channel from `src/params.js` — see docs/internal-rig.md § The pose channels. */
89
- export type PoseChannel =
90
- | "mouthOpen" | "mouthWidth" | "mouthRound" | "mouthPress" | "mouthTuck"
91
- | "mouthCornerL" | "mouthCornerR" | "teethUpper" | "tongue" | "jaw"
92
- | "lidL" | "lidR" | "squintL" | "squintR" | "pupilX" | "pupilY"
93
- | "browRaiseL" | "browRaiseR" | "browAngleL" | "browAngleR"
94
- | "browInnerL" | "browInnerR"
95
- | "headYaw" | "headPitch" | "headRoll"
96
- | "breath" | "shoulderL" | "shoulderR" | "torsoLean" | "torsoTurn";
97
-
98
- /** A partial pose. Channels are clamped to `RANGE`; absent channels keep the mix. */
99
- export type PoseOverrides = Partial<Record<PoseChannel, number>>;
100
-
101
- /** Which hand the character gestures with: `1` the viewer's right (the
102
- * character's own left), `-1` the other. */
103
- export type HandSide = 1 | -1;
104
-
105
- /**
106
- * A motion scale. `1` is the amplitude the channel was authored at, `0` stills
107
- * it, and `2` is the documented ceiling — past that the smoothing constants
108
- * stop holding and clips overshoot their range clamp.
109
- *
110
- * TypeScript cannot say "0..2", so this is a named `number` and the check is at
111
- * the door: the public `createAvatar` throws `RangeError`. The live setters
112
- * below do not, because they exist for review sliders that already bound
113
- * themselves and should not throw mid-drag.
114
- */
115
- export type Gain = number;
116
-
117
- /** Normalized-screen-coordinate escape hatch for `setGaze('CUSTOM', custom)`. */
118
- export interface GazeCustom {
119
- x: number;
120
- y: number;
121
- }
122
-
123
- /**
124
- * A `perform()` timeline action — see docs/internal-mixer.md § Composing
125
- * behavior. `t` is a ms offset into the performance. Discriminated on `do`
126
- * because the verbs do not share their payload: only `action` is addressed by
127
- * `id`, and only `state` reads `keepGaze`.
128
- */
129
- export type AvatarAction =
130
- | { t: number; do: "state"; name: AvatarStateName; keepGaze?: boolean }
131
- | { t: number; do: "emotion"; name: AvatarEmotionName; i?: number }
132
- | { t: number; do: "gaze"; name: AvatarGazeName }
133
- | { t: number; do: "action"; id: AvatarActionId };
134
-
135
- export interface SetStateOptions {
136
- emotion?: AvatarEmotionName;
137
- /** 0..1 emotion strength. Default 1. */
138
- intensity?: number;
139
- gaze?: AvatarGazeName;
140
- keepGaze?: boolean;
141
- }
142
-
143
- export interface SpeakOptions {
144
- cues?: Cue[];
145
- audio?: HTMLMediaElement;
146
- clock?: () => number;
147
- }
148
-
149
- export interface PerformOptions {
150
- audio?: HTMLMediaElement;
151
- clock?: () => number;
152
- onAction?: (a: AvatarAction) => void;
153
- }
154
-
155
- export interface PerformHandle {
156
- /** Cancels the *future* of this performance only — see docs/internal-mixer.md. */
157
- stop: () => void;
158
- }
159
-
160
- export type AvatarEventName = "state" | "speakEnd" | "clipEnd" | "performEnd" | "gestureEnd";
161
-
162
- /** What a host needs to frame an avatar it has never seen: the drawing's own
163
- * window, and where the mouth is inside it. See CLAUDE.md § The two
164
- * abstractions that matter for why it carries nothing else. */
165
- export interface AvatarMeta {
166
- viewBox: { x: number; y: number; w: number; h: number };
167
- mouthCrop?: { x: number; y: number; w: number; h: number };
168
- }
169
-
170
- /** The object `createAvatar()` returns — the whole server-facing surface. */
171
- export interface AvatarApi {
172
- setState(name: AvatarStateName, o?: SetStateOptions): AvatarApi;
173
- setEmotion(name: AvatarEmotionName, intensity?: number): AvatarApi;
174
- setGaze(name: AvatarGazeName, custom?: GazeCustom): AvatarApi;
175
- speak(o?: SpeakOptions): AvatarApi;
176
- pushCues(cues: Cue[]): AvatarApi;
177
- stopSpeaking(): AvatarApi;
178
- /**
179
- * Self-completing action: states resolve underneath while motion lands.
180
- *
181
- * One core intent, one of `actions`, or one of `sequences` — the vocabulary
182
- * is open, so an id this face has no word for is a no-op rather than a
183
- * throw. `ACKNOWLEDGE` resolves to whichever of this body's acknowledgements
184
- * the moment calls for.
185
- */
186
- action(id: string): AvatarApi;
187
- setHandSide(dir: HandSide): AvatarApi;
188
- perform(actions: AvatarAction[], o?: PerformOptions): PerformHandle;
189
- setUserSpeaking(speaking: boolean | null): AvatarApi;
190
- /** Hold user gaze for a short interaction window; currently JS-level only. */
191
- attend(ms?: number): AvatarApi;
192
- setMouthGain(g: Gain): AvatarApi;
193
- readonly mouthGain: Gain;
194
- setGestureGain(g: Gain): AvatarApi;
195
- readonly gestureGain: Gain;
196
- setMotionGain(g: Gain): AvatarApi;
197
- readonly motionGain: Gain;
198
- blink(): AvatarApi;
199
- /** Advance one frame by hand — only meaningful under `{ manual: true }`. */
200
- step(dt: number): AvatarApi;
201
- /** Pin channels to fixed values, above the whole mix. `null` releases. */
202
- setOverrides(o: PoseOverrides | null): AvatarApi;
203
- on(event: "state", fn: (name: AvatarStateName) => void): AvatarApi;
204
- on(event: "speakEnd" | "performEnd", fn: () => void): AvatarApi;
205
- on(event: "clipEnd" | "gestureEnd", fn: (id: string) => void): AvatarApi;
206
- on(event: AvatarEventName, fn: (...args: unknown[]) => void): AvatarApi;
207
- readonly state: AvatarStateName;
208
- readonly emotion: AvatarEmotionName;
209
- readonly gaze: AvatarGazeName;
210
- readonly speaking: boolean;
211
- readonly performing: boolean;
212
- /** Internal clip id in flight. Not a contract — the clip catalog is private
213
- * to this renderer, which is why this is the one open string here. */
214
- readonly clip: string | null;
215
- /** Semantic hand action in flight, including for a non-SVG custom rig. */
216
- readonly gesturing: AvatarActionId | null;
217
- readonly params: Readonly<Record<PoseChannel, number>>;
218
- readonly userSpeaking: boolean;
219
- /** Legacy SVG inspection fields; null for a renderer-neutral AvatarRig. */
220
- readonly svg: SVGSVGElement | null;
221
- readonly meta: AvatarMeta | null;
222
- destroy(): void;
223
- }
224
-
225
- /** A face module's factory — `createFace(mount, theme)`. See
226
- * docs/authoring-a-face.md § Adding a new avatar. */
227
- export type FaceFactory = (
228
- mount: Element,
229
- theme?: FaceTheme,
230
- ) => {
231
- svg: SVGSVGElement;
232
- apply: (params: Readonly<Record<PoseChannel, number>>) => void;
233
- theme: FaceTheme;
234
- destroy: () => void;
235
- };
236
-
237
- /** A face's palette: CSS colour strings by role. Keys are the face's own
238
- * (`THEME` in its module) — a shared key set was tried and each drawing wanted
239
- * different roles. See CLAUDE.md on why `peep` has no second palette. */
240
- export type FaceTheme = Readonly<Record<string, string>>;
241
-
242
- /** One drawn face: what to build, and how to frame it. Import one from
243
- * `@voqalize/avatar/faces/<name>`; nothing resolves a face by string. */
244
- export interface Face {
245
- readonly create: FaceFactory;
246
- readonly meta: AvatarMeta;
247
- }
248
-
249
- export interface CreateAvatarOptions {
250
- /** Element, or CSS selector resolved via `document.querySelector`. */
251
- mount: string | Element;
252
- /** The face to wear. Required unless `rig` replaces the renderer outright. */
253
- face?: Face;
254
- /** Renderer-neutral rig factory. It replaces the SVG face implementation;
255
- * no SVG or metadata is required. See docs/internal-rig.md. */
256
- rig?: import("./rig.js").AvatarRigFactory;
257
- /** Passed to `rig` verbatim. Opaque here on purpose — it belongs to whoever
258
- * wrote the rig, and this file has no way to know its shape. */
259
- rigOptions?: unknown;
260
- theme?: FaceTheme;
261
- mouthGain?: Gain;
262
- gestureGain?: Gain;
263
- motionGain?: Gain;
264
- /** Speech-rhythm head motion (beats, phrase drift), 0..2. A per-rig
265
- * calibration: the same pose unit is a different angle on every rig. */
266
- prosodyHeadGain?: Gain;
267
- /** Speech-rhythm face motion (brows, lids, turn-edge warmth), 0..2. */
268
- prosodyFaceGain?: Gain;
269
- /** The share of a sustained head turn the trunk takes up, 0..1. Default
270
- * 0.45; a per-rig calibration, like `prosodyHeadGain`. */
271
- trunkFollow?: number;
272
- /**
273
- * How far this face may *hold* its head off centre, per axis, in pose units.
274
- * The layers that hold a pose — attitude, gaze, a phrase's pose, the idle
275
- * posture — are folded into this together; strokes, beats and clip deltas
276
- * are not, so a nod keeps its peak. An axis left out is unbudgeted.
277
- *
278
- * A per-rig fact and a measured one: what a face can hold before its
279
- * rendering gives it away is a property of that drawing or that photograph,
280
- * and the library has no way to guess it.
281
- */
282
- headHold?: Readonly<Partial<Record<"headYaw" | "headPitch" | "headRoll", number>>>;
283
- /** Disable only the bundled SVG hand renderer. A custom rig still receives
284
- * first-class `frame.hand` controls for every gesture action. Default true. */
285
- hand?: boolean;
286
- handSide?: HandSide;
287
- /** Withhold the rAF loop so a tool can drive frames itself via `step(dt)`. */
288
- manual?: boolean;
289
- /**
290
- * This avatar's own addressable motions, on top of the core intents. A
291
- * server names one with the wire's `action` command, same as a core one; a
292
- * face that does not have the name ignores it. Keyed by id, each value a clip
293
- * in the same shape `ACTIONS` uses. Cannot shadow one of this renderer's own
294
- * — `ACTIONS` wins.
295
- */
296
- sequences?: Readonly<Record<string, unknown>>;
297
- /**
298
- * This avatar's own shape for one of this renderer's actions, keyed by its
299
- * id. Same id, same intent, a rendering sized for this body — the shared
300
- * clips are authored for a line face. An id this renderer does not publish
301
- * throws: this can reshape the vocabulary, never add to it.
302
- */
303
- actions?: Readonly<Record<string, unknown>>;
304
- /**
305
- * This avatar's own rendering of a state, keyed by state name. Each value
306
- * replaces that state's fields whole (`pose`, `idle`, …); the rest are the
307
- * shared table's. A name that is not a state throws: this can re-render the
308
- * vocabulary, never add to it.
309
- */
310
- states?: Readonly<Partial<Record<AvatarStateName, Readonly<Record<string, unknown>>>>>;
311
- }
312
-
313
- export function createAvatar(opts: CreateAvatarOptions): AvatarApi;
314
-
315
- /** Per-state base pose + idle profile. Read-only in practice — the mixer owns it. */
316
- export const STATES: Readonly<Record<AvatarStateName, Readonly<Record<string, unknown>>>>;
317
- export const STATE_NAMES: readonly AvatarStateName[];
318
- export const GAZE_NAMES: readonly AvatarGazeName[];
319
- /** Where each named direction puts the eyes and the head: pupil offset
320
- * (`px`/`py`), the head's share of it (`hx`/`hy`) and any roll. Pose units, and
321
- * the head deliberately carries less than the whole (`src/gaze.js`). */
322
- export const GAZE_TARGETS: Readonly<Record<AvatarGazeName, {
323
- px: number; py: number; hx: number; hy: number; roll?: number;
324
- }>>;
325
- export const EMOTION_NAMES: readonly AvatarEmotionName[];
326
- export const ACTIONS: Readonly<Record<AvatarActionId, unknown>>;
327
- export const ACTION_IDS: readonly AvatarActionId[];
328
- /** Asserts the two framing rules against a face's own window. Throws on
329
- * violation — `pnpm test` runs it for every registered avatar. */
330
- export function checkHandFraming(meta: AvatarMeta): {
331
- ok: true;
332
- wristDrop: number;
333
- outboardLimit: number;
334
- worst: Record<string, number>;
335
- };
336
- /** The neutral pose: every channel's resting value (`src/params.js`). */
337
- export const REST: Readonly<Record<PoseChannel, number>>;
338
- /** Every channel name, in `REST` order. */
339
- export const CHANNELS: readonly PoseChannel[];
340
- /** Post-mix clamp per channel, `[min, max]`. */
341
- export const RANGE: Readonly<Record<PoseChannel, readonly [number, number]>>;
342
- /** Each channel's smoothing time constant, in seconds. */
343
- export const TAU: Readonly<Record<PoseChannel, number>>;
344
- export function clamp(v: number, lo?: number, hi?: number): number;
345
- /** One frame of exponential smoothing: where `cur` lands `dt` seconds into a
346
- * chase of `target` with time constant `tau`. */
347
- export function approach(cur: number, target: number, tau: number, dt: number): number;
348
- export const VISEME_LETTERS: readonly VisemeLetter[];
349
- export const VISEME_SHAPES: Readonly<Record<VisemeLetter, PoseOverrides>>;
350
- /** Cues lead the audio by this many ms — perceptual tolerance is asymmetric. */
351
- export const LEAD_MS: number;
352
- /** The shoulder line's share of a held `headRoll`, per pose unit. */
353
- export const SHOULDER_TILT: number;
354
- /** A full pose from a handful of overrides: every unnamed channel takes its
355
- * `REST` value. The only correct way to build a frame by hand. */
356
- export function makeParams(overrides?: PoseOverrides): Record<PoseChannel, number>;
357
- /** One named emotion's channel deltas at `intensity` (0..1). */
358
- export function emotionPose(name: AvatarEmotionName, intensity?: number): PoseOverrides;
359
- export { avatarFrame, createSvgRig } from "./rig.js";
360
- export const ARPABET_TO_VISEME: Readonly<Record<string, VisemeLetter>>;
361
- export const AZURE_VISEME_TO_LETTER: Readonly<Record<number, VisemeLetter>>;
362
-
363
- /** The silent/rest letter, `"X"`. */
364
- export const SILENT: VisemeLetter;
365
- /** Pose channels for one letter at `intensity` (0..1), ready to merge into a frame. */
366
- export function shapeFor(letter: VisemeLetter, intensity?: number): PoseOverrides;
367
- /** `jaw` per unit of `mouthOpen`, the ratio the mouth's interior is calibrated at. */
368
- export const JAW_OF_OPEN: number;
369
-
370
- /**
371
- * The mouth clock. Someone has to turn a cue array plus a clock into "which
372
- * letter is on screen right now"; every renderer needs exactly that and none
373
- * should write it twice, so it is a class to construct rather than a contract
374
- * to implement. `sample()` returns `null` when the track is done.
375
- */
376
- export class VisemeTrack {
377
- /** @param clock elapsed ms of the audio being played. */
378
- start(cues: Cue[], clock: () => number): void;
379
- /** Streaming top-up: append cues that arrive mid-utterance. */
380
- push(cues: Cue[]): void;
381
- stop(): void;
382
- sample(): { letter: VisemeLetter; intensity: number } | null;
383
- onEnd: (() => void) | null;
384
- readonly playing: boolean;
385
- }
386
-
387
- /**
388
- * One authored gesture: a short multi-channel timeline of additive deltas,
389
- * optionally with its own mouth track, gaze override and blink beats.
390
- */
391
- export interface Clip {
392
- readonly id: string;
393
- readonly label: string;
394
- readonly text: string;
395
- /** Milliseconds, end to end. */
396
- readonly duration: number;
397
- /** Per channel, `[u, delta]` keys against normalized clip time. */
398
- readonly keys: Readonly<Record<string, ReadonlyArray<readonly [number, number]>>>;
399
- readonly mouthCues?: readonly Cue[];
400
- readonly gaze?: AvatarGazeName;
401
- readonly blinkAt?: readonly number[];
402
- }
403
-
404
- /**
405
- * Every clip this renderer has, keyed by id — the authoring library, not the
406
- * wire's vocabulary. Most are reachable only from inside the mixer: `ACTIONS`
407
- * is what a server may name.
408
- */
409
- export const INTERNAL_CLIPS: Readonly<Record<string, Clip>>;
410
-
411
- /** What one tick of a clip contributes: additive channel deltas, the ramp
412
- * weight they already carry, and the mouth when the clip owns it. */
413
- export interface ClipSample {
414
- delta: Record<string, number> | null;
415
- weight: number;
416
- mouth: { letter: VisemeLetter; intensity: number } | null;
417
- ownsMouth: boolean;
418
- }
419
-
420
- /** Plays one clip forward in ticks you supply. Fixed-`dt` stepping is what
421
- * makes a rendered gesture reproducible. */
422
- export class ClipPlayer {
423
- constructor(hooks?: { onGaze?: (name: string | null) => void; onBlink?: () => void });
424
- play(clip: Clip, audio?: HTMLMediaElement | null, opts?: { queue?: boolean }): void;
425
- /** Advance by `dtMs` and report this frame's contribution. */
426
- update(dtMs: number): ClipSample;
427
- stop(immediate?: boolean): void;
428
- onEnd: ((clip: Clip | null) => void) | null;
429
- readonly playing: boolean;
430
- readonly id: string | null;
431
- }
432
-
433
- export function attachAudio(id: string, url: string): void;
434
- export function normalizeActions(actions: AvatarAction[]): AvatarAction[];
435
- export function normalizeCues(cues: Cue[]): Cue[];
436
- export function textToCues(text: string, opts?: { wpm?: number }): Cue[];