@voqalize/avatar 0.2.2 → 0.4.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 (265) hide show
  1. package/LICENSE +21 -661
  2. package/LICENSE-CC-BY-4.0 +396 -0
  3. package/README.md +191 -662
  4. package/assets/README.md +48 -0
  5. package/assets/tanya.glb +0 -0
  6. package/assets/tara.glb +0 -0
  7. package/assets/tushar.glb +0 -0
  8. package/client/Avatar.tsx +57 -0
  9. package/client/AvatarClient.ts +818 -0
  10. package/client/arjun.ts +26 -0
  11. package/client/createAvatar.ts +177 -0
  12. package/client/createCanvasAvatar.ts +72 -0
  13. package/client/index.ts +44 -0
  14. package/client/internal.ts +108 -0
  15. package/client/interviewer-female.ts +4 -0
  16. package/client/interviewer-male.ts +4 -0
  17. package/client/ishita.ts +26 -0
  18. package/client/kabir.ts +26 -0
  19. package/client/meera.ts +26 -0
  20. package/client/naina.ts +26 -0
  21. package/client/playout.ts +95 -0
  22. package/client/professional-female-a.ts +4 -0
  23. package/client/professional-female-b.ts +4 -0
  24. package/client/professional-male-a.ts +4 -0
  25. package/client/professional-male-b.ts +4 -0
  26. package/client/react.ts +13 -0
  27. package/client/supports.ts +28 -0
  28. package/client/three/assets.ts +24 -0
  29. package/client/three/budgets.ts +27 -0
  30. package/client/three/holds.ts +64 -0
  31. package/client/three/internal.ts +54 -0
  32. package/client/three/motion-limits.json +116 -0
  33. package/client/three/sequences.ts +281 -0
  34. package/client/three/tanya.ts +69 -0
  35. package/client/three/tara-rig.ts +1562 -0
  36. package/client/three/tara.ts +76 -0
  37. package/client/three/tushar.ts +65 -0
  38. package/client/types.ts +235 -0
  39. package/client/useAvatar.ts +74 -0
  40. package/client/vikram.ts +26 -0
  41. package/dist/Avatar.d.ts +44 -0
  42. package/dist/Avatar.d.ts.map +1 -0
  43. package/{client/dist → dist}/Avatar.js +2 -2
  44. package/dist/Avatar.js.map +1 -0
  45. package/dist/AvatarClient.d.ts +274 -0
  46. package/dist/AvatarClient.d.ts.map +1 -0
  47. package/dist/AvatarClient.js +712 -0
  48. package/dist/AvatarClient.js.map +1 -0
  49. package/dist/arjun.d.ts +7 -0
  50. package/dist/arjun.d.ts.map +1 -0
  51. package/dist/arjun.js +20 -0
  52. package/dist/arjun.js.map +1 -0
  53. package/dist/createAvatar.d.ts +135 -0
  54. package/dist/createAvatar.d.ts.map +1 -0
  55. package/dist/createAvatar.js +75 -0
  56. package/dist/createAvatar.js.map +1 -0
  57. package/dist/createCanvasAvatar.d.ts +22 -0
  58. package/dist/createCanvasAvatar.d.ts.map +1 -0
  59. package/dist/createCanvasAvatar.js +47 -0
  60. package/dist/createCanvasAvatar.js.map +1 -0
  61. package/dist/index.d.ts +34 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +33 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/internal.d.ts +34 -0
  66. package/dist/internal.d.ts.map +1 -0
  67. package/dist/internal.js +41 -0
  68. package/dist/internal.js.map +1 -0
  69. package/dist/interviewer-female.d.ts +4 -0
  70. package/dist/interviewer-female.d.ts.map +1 -0
  71. package/dist/interviewer-female.js +3 -0
  72. package/dist/interviewer-female.js.map +1 -0
  73. package/dist/interviewer-male.d.ts +4 -0
  74. package/dist/interviewer-male.d.ts.map +1 -0
  75. package/dist/interviewer-male.js +3 -0
  76. package/dist/interviewer-male.js.map +1 -0
  77. package/dist/ishita.d.ts +7 -0
  78. package/dist/ishita.d.ts.map +1 -0
  79. package/dist/ishita.js +20 -0
  80. package/dist/ishita.js.map +1 -0
  81. package/dist/kabir.d.ts +7 -0
  82. package/dist/kabir.d.ts.map +1 -0
  83. package/dist/kabir.js +20 -0
  84. package/dist/kabir.js.map +1 -0
  85. package/dist/meera.d.ts +7 -0
  86. package/dist/meera.d.ts.map +1 -0
  87. package/dist/meera.js +20 -0
  88. package/dist/meera.js.map +1 -0
  89. package/dist/naina.d.ts +7 -0
  90. package/dist/naina.d.ts.map +1 -0
  91. package/dist/naina.js +20 -0
  92. package/dist/naina.js.map +1 -0
  93. package/dist/playout.d.ts +42 -0
  94. package/dist/playout.d.ts.map +1 -0
  95. package/dist/playout.js +85 -0
  96. package/dist/playout.js.map +1 -0
  97. package/dist/professional-female-a.d.ts +4 -0
  98. package/dist/professional-female-a.d.ts.map +1 -0
  99. package/dist/professional-female-a.js +3 -0
  100. package/dist/professional-female-a.js.map +1 -0
  101. package/dist/professional-female-b.d.ts +4 -0
  102. package/dist/professional-female-b.d.ts.map +1 -0
  103. package/dist/professional-female-b.js +3 -0
  104. package/dist/professional-female-b.js.map +1 -0
  105. package/dist/professional-male-a.d.ts +4 -0
  106. package/dist/professional-male-a.d.ts.map +1 -0
  107. package/dist/professional-male-a.js +3 -0
  108. package/dist/professional-male-a.js.map +1 -0
  109. package/dist/professional-male-b.d.ts +4 -0
  110. package/dist/professional-male-b.d.ts.map +1 -0
  111. package/dist/professional-male-b.js +3 -0
  112. package/dist/professional-male-b.js.map +1 -0
  113. package/dist/react.d.ts +13 -0
  114. package/dist/react.d.ts.map +1 -0
  115. package/dist/react.js +13 -0
  116. package/dist/react.js.map +1 -0
  117. package/dist/supports.d.ts +20 -0
  118. package/dist/supports.d.ts.map +1 -0
  119. package/dist/supports.js +26 -0
  120. package/dist/supports.js.map +1 -0
  121. package/dist/three/assets.d.ts +24 -0
  122. package/dist/three/assets.d.ts.map +1 -0
  123. package/dist/three/assets.js +22 -0
  124. package/dist/three/assets.js.map +1 -0
  125. package/dist/three/budgets.d.ts +17 -0
  126. package/dist/three/budgets.d.ts.map +1 -0
  127. package/dist/three/budgets.js +21 -0
  128. package/dist/three/budgets.js.map +1 -0
  129. package/dist/three/holds.d.ts +33 -0
  130. package/dist/three/holds.d.ts.map +1 -0
  131. package/dist/three/holds.js +56 -0
  132. package/dist/three/holds.js.map +1 -0
  133. package/dist/three/internal.d.ts +52 -0
  134. package/dist/three/internal.d.ts.map +1 -0
  135. package/dist/three/internal.js +49 -0
  136. package/dist/three/internal.js.map +1 -0
  137. package/dist/three/motion-limits.json +116 -0
  138. package/dist/three/sequences.d.ts +71 -0
  139. package/dist/three/sequences.d.ts.map +1 -0
  140. package/dist/three/sequences.js +262 -0
  141. package/dist/three/sequences.js.map +1 -0
  142. package/dist/three/tanya.d.ts +35 -0
  143. package/dist/three/tanya.d.ts.map +1 -0
  144. package/dist/three/tanya.js +51 -0
  145. package/dist/three/tanya.js.map +1 -0
  146. package/dist/three/tara-rig.d.ts +403 -0
  147. package/dist/three/tara-rig.d.ts.map +1 -0
  148. package/dist/three/tara-rig.js +1500 -0
  149. package/dist/three/tara-rig.js.map +1 -0
  150. package/dist/three/tara.d.ts +35 -0
  151. package/dist/three/tara.d.ts.map +1 -0
  152. package/dist/three/tara.js +58 -0
  153. package/dist/three/tara.js.map +1 -0
  154. package/dist/three/tushar.d.ts +31 -0
  155. package/dist/three/tushar.d.ts.map +1 -0
  156. package/dist/three/tushar.js +47 -0
  157. package/dist/three/tushar.js.map +1 -0
  158. package/dist/types.d.ts +149 -0
  159. package/dist/types.d.ts.map +1 -0
  160. package/dist/types.js +140 -0
  161. package/dist/types.js.map +1 -0
  162. package/dist/useAvatar.d.ts +39 -0
  163. package/dist/useAvatar.d.ts.map +1 -0
  164. package/dist/useAvatar.js +35 -0
  165. package/dist/useAvatar.js.map +1 -0
  166. package/dist/vikram.d.ts +7 -0
  167. package/dist/vikram.d.ts.map +1 -0
  168. package/dist/vikram.js +20 -0
  169. package/dist/vikram.js.map +1 -0
  170. package/package.json +110 -25
  171. package/src/avatar.d.ts +218 -120
  172. package/src/avatar.js +950 -213
  173. package/src/behavior.d.ts +42 -0
  174. package/src/behavior.js +114 -0
  175. package/src/camera.js +29 -0
  176. package/src/canvas/author/parts/eye.mjs +722 -0
  177. package/src/canvas/author/parts/hand.mjs +1156 -0
  178. package/src/canvas/author/parts/mouth.mjs +741 -0
  179. package/src/canvas/author/parts/nose.mjs +100 -0
  180. package/src/canvas/author/parts/skin-detail.mjs +67 -0
  181. package/src/canvas/author/path.mjs +283 -0
  182. package/src/canvas/author/rig.mjs +405 -0
  183. package/src/canvas/avatars/round/face.d.mts +3 -0
  184. package/src/canvas/avatars/round/face.mjs +1307 -0
  185. package/src/canvas/create-rig.d.ts +15 -0
  186. package/src/canvas/create-rig.js +100 -0
  187. package/src/canvas/data/img/professional-female-a-hair-back.webp +0 -0
  188. package/src/canvas/data/img/professional-female-a-hair-front.webp +0 -0
  189. package/src/canvas/data/img/professional-female-a-top-body.webp +0 -0
  190. package/src/canvas/data/img/professional-female-b-hair-back.webp +0 -0
  191. package/src/canvas/data/img/professional-female-b-hair-front.webp +0 -0
  192. package/src/canvas/data/img/professional-female-b-top-body.webp +0 -0
  193. package/src/canvas/data/img/professional-male-a-hair-back.webp +0 -0
  194. package/src/canvas/data/img/professional-male-a-hair-front.webp +0 -0
  195. package/src/canvas/data/img/professional-male-a-top-body.webp +0 -0
  196. package/src/canvas/data/img/professional-male-b-hair-back.webp +0 -0
  197. package/src/canvas/data/img/professional-male-b-hair-front.webp +0 -0
  198. package/src/canvas/data/img/professional-male-b-top-body.webp +0 -0
  199. package/src/canvas/data/img/round-m3-hair-back.webp +0 -0
  200. package/src/canvas/data/img/round-m3-hair-front.webp +0 -0
  201. package/src/canvas/data/img/round-m3-top-body.webp +0 -0
  202. package/src/canvas/data/img/round-w1-hair-back.webp +0 -0
  203. package/src/canvas/data/img/round-w1-hair-front.webp +0 -0
  204. package/src/canvas/data/img/round-w1-top-body.webp +0 -0
  205. package/src/canvas/data/interviewer-female.rig.json +1 -0
  206. package/src/canvas/data/interviewer-male.rig.json +1 -0
  207. package/src/canvas/data/professional-female-a.rig.json +1 -0
  208. package/src/canvas/data/professional-female-b.rig.json +1 -0
  209. package/src/canvas/data/professional-male-a.rig.json +1 -0
  210. package/src/canvas/data/professional-male-b.rig.json +1 -0
  211. package/src/canvas/src/live.js +508 -0
  212. package/src/canvas/src/render2d.js +218 -0
  213. package/src/canvas/src/rig.js +297 -0
  214. package/src/canvas/src/vocab.js +96 -0
  215. package/src/clips.js +18 -7
  216. package/src/conformance.js +119 -0
  217. package/src/emotions.js +8 -5
  218. package/src/face-core.js +27 -1
  219. package/src/face-myna.d.ts +7 -0
  220. package/src/face-myna.js +192 -131
  221. package/src/face-peep-control-plane.js +167 -0
  222. package/src/face-peep.d.ts +7 -0
  223. package/src/face-peep.js +274 -116
  224. package/src/face-wren.d.ts +7 -0
  225. package/src/face-wren.js +17 -19
  226. package/src/faces.d.ts +12 -0
  227. package/src/faces.js +53 -0
  228. package/src/gaze.js +434 -58
  229. package/src/hand.js +120 -91
  230. package/src/head.js +134 -0
  231. package/src/idle.js +227 -119
  232. package/src/interjections.js +200 -31
  233. package/src/params.js +8 -4
  234. package/src/perform.js +7 -9
  235. package/src/prosody.js +647 -0
  236. package/src/rig.d.ts +24 -0
  237. package/src/rig.js +32 -0
  238. package/src/speech-timing.js +23 -0
  239. package/src/visemes.js +110 -24
  240. package/client/dist/Avatar.d.ts +0 -27
  241. package/client/dist/Avatar.d.ts.map +0 -1
  242. package/client/dist/Avatar.js.map +0 -1
  243. package/client/dist/AvatarClient.d.ts +0 -139
  244. package/client/dist/AvatarClient.d.ts.map +0 -1
  245. package/client/dist/AvatarClient.js +0 -223
  246. package/client/dist/AvatarClient.js.map +0 -1
  247. package/client/dist/index.d.ts +0 -22
  248. package/client/dist/index.d.ts.map +0 -1
  249. package/client/dist/index.js +0 -22
  250. package/client/dist/index.js.map +0 -1
  251. package/client/dist/types.d.ts +0 -86
  252. package/client/dist/types.d.ts.map +0 -1
  253. package/client/dist/types.js +0 -31
  254. package/client/dist/types.js.map +0 -1
  255. package/client/dist/useAvatar.d.ts +0 -42
  256. package/client/dist/useAvatar.d.ts.map +0 -1
  257. package/client/dist/useAvatar.js +0 -51
  258. package/client/dist/useAvatar.js.map +0 -1
  259. package/client/src/Avatar.tsx +0 -33
  260. package/client/src/AvatarClient.ts +0 -270
  261. package/client/src/index.ts +0 -22
  262. package/client/src/types.ts +0 -104
  263. package/client/src/useAvatar.ts +0 -85
  264. package/docs/contract-avatar.md +0 -371
  265. package/docs/contract-protocol.md +0 -461
@@ -0,0 +1,42 @@
1
+ import type { AvatarStateName } from "./avatar.js";
2
+
3
+ /** The two renderer calls behavior resolution actually needs. */
4
+ export interface BehaviorAvatar {
5
+ setState(name: AvatarStateName): unknown;
6
+ /**
7
+ * One motion by name. The vocabulary is open — two core intents plus
8
+ * whatever this avatar publishes — so a name it does not have must be a
9
+ * no-op rather than a throw.
10
+ */
11
+ action(id: string): unknown;
12
+ }
13
+
14
+ /**
15
+ * The whole behaviour vocabulary — the nine. Not an alias list; see behavior.js.
16
+ *
17
+ * Every one of these is also an `AvatarStateName`, which is why there is no
18
+ * mapping table any more.
19
+ */
20
+ export type BehaviorStateId =
21
+ | "IDLE" | "LISTENING" | "CANT_HEAR" | "THINKING" | "WORKING"
22
+ | "MUTED" | "SPEAKING" | "DEGRADED" | "OFFLINE";
23
+
24
+ export type BehaviorActionId = "ack" | "turn.interrupted";
25
+
26
+ export const BEHAVIOR_STATE_IDS: readonly BehaviorStateId[];
27
+ export const BEHAVIOR_ACTIONS: Readonly<Record<BehaviorActionId, { renderAction: string }>>;
28
+ export const BEHAVIOR_ACTION_IDS: readonly BehaviorActionId[];
29
+ export const WIRE_ACTION_TO_BEHAVIOR: Readonly<Record<string, BehaviorActionId>>;
30
+
31
+ export class BehaviorController {
32
+ constructor(avatar: BehaviorAvatar);
33
+ state: BehaviorStateId | null;
34
+ setState(id: BehaviorStateId, options?: { force?: boolean }): this;
35
+ action(id: BehaviorActionId): this;
36
+ /**
37
+ * One id off the wire: a core intent through the catalog, anything else
38
+ * straight to the renderer. Never throws — the vocabulary is open.
39
+ */
40
+ wireAction(id: string): this;
41
+ destroy(): void;
42
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Behavior catalog and controller.
3
+ *
4
+ * This is the layer between factual/wire inputs and the bundled SVG renderer.
5
+ * It is not a seam any avatar implementation sees: an implementation receives
6
+ * a PipecatClient and decides for itself what a state means.
7
+ *
8
+ * The vocabulary is the nine core states and two core actions. The states used
9
+ * to be seventeen — the core
10
+ * set plus every SVG render state (TYPING, SEARCHING_SCREEN, WANTS_IN, …)
11
+ * passed straight through, so that tooling could drive one of *those* from
12
+ * here. That made the mixer's private state list look like part of the
13
+ * behaviour vocabulary. Tooling that wants a render state calls
14
+ * `avatar.setState` on the mixer, which is whose state it is.
15
+ */
16
+
17
+ /**
18
+ * The durable states this layer resolves. Each one *is* the render state it
19
+ * asks the bundled SVG mixer for, so there is nothing here to map.
20
+ *
21
+ * There used to be a second column — a per-state `renderState`, so a renderer
22
+ * could draw `WORKING` as anything it liked and the divergence would land here
23
+ * rather than leak. In nine rows it had exactly one non-identity entry,
24
+ * `STRAINING -> CANT_HEAR`, and the wire redesign deleted that entry by
25
+ * renaming the state: a state names the bot's *situation*, never how it looks,
26
+ * and "straining" was a situation named after one drawing of it. A table whose
27
+ * every row reads `X: X` documents nothing and invites a renderer to plug into
28
+ * it, so it is a list now. A renderer that genuinely wants to draw one of these
29
+ * as something else calls `avatar.setState` with the render state it wants,
30
+ * which is whose state that is.
31
+ */
32
+ export const BEHAVIOR_STATE_IDS = Object.freeze([
33
+ 'IDLE', 'LISTENING', 'CANT_HEAR', 'THINKING', 'WORKING',
34
+ 'MUTED', 'SPEAKING', 'DEGRADED', 'OFFLINE',
35
+ ]);
36
+
37
+ const STATE_IDS = new Set(BEHAVIOR_STATE_IDS);
38
+
39
+ /**
40
+ * Library action IDs: the two intents every renderer owes a server, in this
41
+ * layer's readable spelling.
42
+ *
43
+ * It used to be seven, with a `sequence` command alongside for a renderer's own
44
+ * motions. The seven were not the wrong *names* so much as the wrong idea: four
45
+ * of them were `GESTURE_*` — a greet, a goodbye, a wave — which are things a
46
+ * particular body does and not intents a server can hold every face to, and two
47
+ * were the receipt and the nod, which are two *shapes* of acknowledging. So the
48
+ * server now says only that an acknowledgement is due, the avatar picks which
49
+ * one it makes, and the gestures live in whichever catalogue can draw them,
50
+ * reachable by the same open `action` id a sequence used to need its own
51
+ * command for.
52
+ */
53
+ export const BEHAVIOR_ACTIONS = Object.freeze({
54
+ ack: { renderAction: 'ACKNOWLEDGE' },
55
+ 'turn.interrupted': { renderAction: 'RESPONSE_INTERRUPTED' },
56
+ });
57
+
58
+ export const BEHAVIOR_ACTION_IDS = Object.freeze(Object.keys(BEHAVIOR_ACTIONS));
59
+
60
+ /** The two core wire names map into the behavior catalog here and nowhere else. */
61
+ export const WIRE_ACTION_TO_BEHAVIOR = Object.freeze({
62
+ ACKNOWLEDGE: 'ack',
63
+ RESPONSE_INTERRUPTED: 'turn.interrupted',
64
+ });
65
+
66
+ /**
67
+ * Turns behavior intent into the legacy SVG mixer's calls. A future renderer
68
+ * receives the same intent through its own adapter; it does not need to know
69
+ * the legacy state/action names used below.
70
+ */
71
+ export class BehaviorController {
72
+ constructor(avatar) {
73
+ if (!avatar || typeof avatar.setState !== 'function' || typeof avatar.action !== 'function') {
74
+ throw new TypeError('BehaviorController requires an avatar implementation');
75
+ }
76
+ this.avatar = avatar;
77
+ this.state = null;
78
+ }
79
+
80
+ setState(id, { force = false } = {}) {
81
+ if (!STATE_IDS.has(id)) throw new Error(`unknown behavior state: ${id}`);
82
+ if (!force && this.state === id) return this;
83
+ this.state = id;
84
+ this.avatar.setState(id);
85
+ return this;
86
+ }
87
+
88
+ destroy() {}
89
+
90
+ action(id) {
91
+ const def = BEHAVIOR_ACTIONS[id];
92
+ if (!def) throw new Error(`unknown behavior action: ${id}`);
93
+ this.avatar.action(def.renderAction);
94
+ return this;
95
+ }
96
+
97
+ /**
98
+ * One id off the wire. A core one goes through the behavior catalog; any
99
+ * other goes straight to the renderer, and that asymmetry is the point.
100
+ * `BEHAVIOR_ACTIONS` exists so a portable intent has a readable name
101
+ * independent of how any one face renders it. An avatar's own id has no such
102
+ * independence — it *is* the rendering, named by the renderer that owns it —
103
+ * so giving it a behavior alias would claim a portability it does not have.
104
+ *
105
+ * Neither path throws. The wire's action vocabulary is open and an id this
106
+ * face cannot draw is the expected case, not somebody's broken build.
107
+ */
108
+ wireAction(id) {
109
+ const behaviorId = WIRE_ACTION_TO_BEHAVIOR[id];
110
+ if (behaviorId) return this.action(behaviorId);
111
+ this.avatar.action(id);
112
+ return this;
113
+ }
114
+ }
package/src/camera.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The close webcam crop shared by every avatar renderer.
3
+ *
4
+ * The head gets most of the tile because lip sync and listening expressions
5
+ * are the information the avatar is there to carry. The remaining lower band
6
+ * leaves enough neck and shoulder for posture to read without turning the
7
+ * call tile into a half-body portrait.
8
+ */
9
+ export const CALL_CAMERA = Object.freeze({
10
+ aspect: 4 / 3,
11
+ headroom: 0.06,
12
+ head: 0.70,
13
+ body: 0.24,
14
+ });
15
+
16
+ /** Derive a camera rectangle from three landmarks in the drawing's own units. */
17
+ export function viewBoxForHead({ centerX, crownY, chinY }) {
18
+ if (![centerX, crownY, chinY].every(Number.isFinite) || chinY <= crownY) {
19
+ throw new TypeError('viewBoxForHead: expected a finite centre and crown above chin');
20
+ }
21
+ const h = (chinY - crownY) / CALL_CAMERA.head;
22
+ const w = h * CALL_CAMERA.aspect;
23
+ return {
24
+ x: centerX - w / 2,
25
+ y: crownY - h * CALL_CAMERA.headroom,
26
+ w,
27
+ h,
28
+ };
29
+ }