@voqalize/avatar 0.3.0 → 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 (132) hide show
  1. package/LICENSE-CC-BY-4.0 +396 -0
  2. package/README.md +65 -17
  3. package/assets/README.md +48 -0
  4. package/assets/tanya.glb +0 -0
  5. package/assets/tara.glb +0 -0
  6. package/assets/tushar.glb +0 -0
  7. package/client/AvatarClient.ts +337 -42
  8. package/client/arjun.ts +2 -0
  9. package/client/createAvatar.ts +38 -0
  10. package/client/index.ts +6 -1
  11. package/client/internal.ts +31 -3
  12. package/client/ishita.ts +2 -0
  13. package/client/kabir.ts +2 -0
  14. package/client/meera.ts +2 -0
  15. package/client/naina.ts +2 -0
  16. package/client/playout.ts +95 -0
  17. package/client/supports.ts +28 -0
  18. package/client/three/assets.ts +24 -0
  19. package/client/three/budgets.ts +27 -0
  20. package/client/three/holds.ts +64 -0
  21. package/client/three/internal.ts +54 -0
  22. package/client/three/motion-limits.json +116 -0
  23. package/client/three/sequences.ts +281 -0
  24. package/client/three/tanya.ts +69 -0
  25. package/client/three/tara-rig.ts +1562 -0
  26. package/client/three/tara.ts +76 -0
  27. package/client/three/tushar.ts +65 -0
  28. package/client/types.ts +109 -20
  29. package/client/vikram.ts +2 -0
  30. package/dist/AvatarClient.d.ts +89 -11
  31. package/dist/AvatarClient.d.ts.map +1 -1
  32. package/dist/AvatarClient.js +297 -35
  33. package/dist/AvatarClient.js.map +1 -1
  34. package/dist/arjun.d.ts +1 -0
  35. package/dist/arjun.d.ts.map +1 -1
  36. package/dist/arjun.js +2 -0
  37. package/dist/arjun.js.map +1 -1
  38. package/dist/createAvatar.d.ts +36 -0
  39. package/dist/createAvatar.d.ts.map +1 -1
  40. package/dist/createAvatar.js.map +1 -1
  41. package/dist/index.d.ts +6 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +5 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/internal.d.ts +9 -5
  46. package/dist/internal.d.ts.map +1 -1
  47. package/dist/internal.js +13 -5
  48. package/dist/internal.js.map +1 -1
  49. package/dist/ishita.d.ts +1 -0
  50. package/dist/ishita.d.ts.map +1 -1
  51. package/dist/ishita.js +2 -0
  52. package/dist/ishita.js.map +1 -1
  53. package/dist/kabir.d.ts +1 -0
  54. package/dist/kabir.d.ts.map +1 -1
  55. package/dist/kabir.js +2 -0
  56. package/dist/kabir.js.map +1 -1
  57. package/dist/meera.d.ts +1 -0
  58. package/dist/meera.d.ts.map +1 -1
  59. package/dist/meera.js +2 -0
  60. package/dist/meera.js.map +1 -1
  61. package/dist/naina.d.ts +1 -0
  62. package/dist/naina.d.ts.map +1 -1
  63. package/dist/naina.js +2 -0
  64. package/dist/naina.js.map +1 -1
  65. package/dist/playout.d.ts +42 -0
  66. package/dist/playout.d.ts.map +1 -0
  67. package/dist/playout.js +85 -0
  68. package/dist/playout.js.map +1 -0
  69. package/dist/supports.d.ts +20 -0
  70. package/dist/supports.d.ts.map +1 -0
  71. package/dist/supports.js +26 -0
  72. package/dist/supports.js.map +1 -0
  73. package/dist/three/assets.d.ts +24 -0
  74. package/dist/three/assets.d.ts.map +1 -0
  75. package/dist/three/assets.js +22 -0
  76. package/dist/three/assets.js.map +1 -0
  77. package/dist/three/budgets.d.ts +17 -0
  78. package/dist/three/budgets.d.ts.map +1 -0
  79. package/dist/three/budgets.js +21 -0
  80. package/dist/three/budgets.js.map +1 -0
  81. package/dist/three/holds.d.ts +33 -0
  82. package/dist/three/holds.d.ts.map +1 -0
  83. package/dist/three/holds.js +56 -0
  84. package/dist/three/holds.js.map +1 -0
  85. package/dist/three/internal.d.ts +52 -0
  86. package/dist/three/internal.d.ts.map +1 -0
  87. package/dist/three/internal.js +49 -0
  88. package/dist/three/internal.js.map +1 -0
  89. package/dist/three/motion-limits.json +116 -0
  90. package/dist/three/sequences.d.ts +71 -0
  91. package/dist/three/sequences.d.ts.map +1 -0
  92. package/dist/three/sequences.js +262 -0
  93. package/dist/three/sequences.js.map +1 -0
  94. package/dist/three/tanya.d.ts +35 -0
  95. package/dist/three/tanya.d.ts.map +1 -0
  96. package/dist/three/tanya.js +51 -0
  97. package/dist/three/tanya.js.map +1 -0
  98. package/dist/three/tara-rig.d.ts +403 -0
  99. package/dist/three/tara-rig.d.ts.map +1 -0
  100. package/dist/three/tara-rig.js +1500 -0
  101. package/dist/three/tara-rig.js.map +1 -0
  102. package/dist/three/tara.d.ts +35 -0
  103. package/dist/three/tara.d.ts.map +1 -0
  104. package/dist/three/tara.js +58 -0
  105. package/dist/three/tara.js.map +1 -0
  106. package/dist/three/tushar.d.ts +31 -0
  107. package/dist/three/tushar.d.ts.map +1 -0
  108. package/dist/three/tushar.js +47 -0
  109. package/dist/three/tushar.js.map +1 -0
  110. package/dist/types.d.ts +65 -10
  111. package/dist/types.d.ts.map +1 -1
  112. package/dist/types.js +58 -11
  113. package/dist/types.js.map +1 -1
  114. package/dist/vikram.d.ts +1 -0
  115. package/dist/vikram.d.ts.map +1 -1
  116. package/dist/vikram.js +2 -0
  117. package/dist/vikram.js.map +1 -1
  118. package/package.json +34 -6
  119. package/src/avatar.d.ts +59 -2
  120. package/src/avatar.js +743 -94
  121. package/src/behavior.d.ts +28 -12
  122. package/src/behavior.js +54 -42
  123. package/src/canvas/create-rig.js +1 -1
  124. package/src/canvas/src/live.js +2 -2
  125. package/src/conformance.js +1 -1
  126. package/src/emotions.js +4 -2
  127. package/src/gaze.js +334 -73
  128. package/src/head.js +134 -0
  129. package/src/idle.js +210 -19
  130. package/src/interjections.js +70 -26
  131. package/src/prosody.js +647 -0
  132. package/src/visemes.js +85 -15
package/src/behavior.d.ts CHANGED
@@ -1,26 +1,42 @@
1
- import type { AvatarApi, AvatarActionId, AvatarStateName } from "./avatar.js";
1
+ import type { AvatarStateName } from "./avatar.js";
2
2
 
3
- /** The whole behaviour vocabulary — the nine. Not an alias list; see behavior.js. */
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
+ */
4
20
  export type BehaviorStateId =
5
- | "IDLE" | "LISTENING" | "STRAINING" | "THINKING" | "WORKING"
21
+ | "IDLE" | "LISTENING" | "CANT_HEAR" | "THINKING" | "WORKING"
6
22
  | "MUTED" | "SPEAKING" | "DEGRADED" | "OFFLINE";
7
23
 
8
- export type BehaviorActionId =
9
- | "ack.receive" | "ack.nod"
10
- | "turn.interrupted"
11
- | "gesture.greet" | "gesture.farewell" | "gesture.approve" | "gesture.wait";
24
+ export type BehaviorActionId = "ack" | "turn.interrupted";
12
25
 
13
- export const BEHAVIOR_STATES: Readonly<Record<BehaviorStateId, { renderState: AvatarStateName }>>;
14
26
  export const BEHAVIOR_STATE_IDS: readonly BehaviorStateId[];
15
- export const BEHAVIOR_ACTIONS: Readonly<Record<BehaviorActionId, { renderAction: AvatarActionId }>>;
27
+ export const BEHAVIOR_ACTIONS: Readonly<Record<BehaviorActionId, { renderAction: string }>>;
16
28
  export const BEHAVIOR_ACTION_IDS: readonly BehaviorActionId[];
17
- export const WIRE_ACTION_TO_BEHAVIOR: Readonly<Record<AvatarActionId, BehaviorActionId>>;
29
+ export const WIRE_ACTION_TO_BEHAVIOR: Readonly<Record<string, BehaviorActionId>>;
18
30
 
19
31
  export class BehaviorController {
20
- constructor(avatar: AvatarApi);
32
+ constructor(avatar: BehaviorAvatar);
21
33
  state: BehaviorStateId | null;
22
34
  setState(id: BehaviorStateId, options?: { force?: boolean }): this;
23
35
  action(id: BehaviorActionId): this;
24
- wireAction(id: AvatarActionId): 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;
25
41
  destroy(): void;
26
42
  }
package/src/behavior.js CHANGED
@@ -5,8 +5,9 @@
5
5
  * It is not a seam any avatar implementation sees: an implementation receives
6
6
  * a PipecatClient and decides for itself what a state means.
7
7
  *
8
- * The vocabulary is the nine core states. It used to be seventeen — the core
9
- * set plus every SVG render state (TYPING, CANT_HEAR, SEARCHING_SCREEN, …)
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, …)
10
11
  * passed straight through, so that tooling could drive one of *those* from
11
12
  * here. That made the mixer's private state list look like part of the
12
13
  * behaviour vocabulary. Tooling that wants a render state calls
@@ -14,52 +15,52 @@
14
15
  */
15
16
 
16
17
  /**
17
- * Durable states the behavior layer resolves, and the render state each one
18
- * asks the bundled SVG mixer for. The right-hand column belongs to a renderer:
19
- * `WORKING` may draw as anything, and this is where a divergence lands rather
20
- * than leaks. `STRAINING` is the first row to actually use that — the two
21
- * columns were written out for years while the mapping stayed 1:1.
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.
22
31
  */
23
- export const BEHAVIOR_STATES = Object.freeze({
24
- IDLE: { renderState: 'IDLE' },
25
- LISTENING: { renderState: 'LISTENING' },
26
- // What the server calls straining, this renderer draws as CANT_HEAR: the
27
- // behavior name claims only that the avatar is trying harder to hear, while
28
- // the pose is one particular drawing of that. A renderer with no such pose
29
- // may legitimately point this at LISTENING.
30
- STRAINING: { renderState: 'CANT_HEAR' },
31
- THINKING: { renderState: 'THINKING' },
32
- WORKING: { renderState: 'WORKING' },
33
- MUTED: { renderState: 'MUTED' },
34
- SPEAKING: { renderState: 'SPEAKING' },
35
- DEGRADED: { renderState: 'DEGRADED' },
36
- OFFLINE: { renderState: 'OFFLINE' },
37
- });
32
+ export const BEHAVIOR_STATE_IDS = Object.freeze([
33
+ 'IDLE', 'LISTENING', 'CANT_HEAR', 'THINKING', 'WORKING',
34
+ 'MUTED', 'SPEAKING', 'DEGRADED', 'OFFLINE',
35
+ ]);
38
36
 
39
- export const BEHAVIOR_STATE_IDS = Object.freeze(Object.keys(BEHAVIOR_STATES));
37
+ const STATE_IDS = new Set(BEHAVIOR_STATE_IDS);
40
38
 
41
- /** Library action IDs. These are broader and more readable than the wire. */
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
+ */
42
53
  export const BEHAVIOR_ACTIONS = Object.freeze({
43
- 'ack.receive': { renderAction: 'ACK_RECEIVE' },
44
- 'ack.nod': { renderAction: 'ACK_NOD' },
54
+ ack: { renderAction: 'ACKNOWLEDGE' },
45
55
  'turn.interrupted': { renderAction: 'RESPONSE_INTERRUPTED' },
46
- 'gesture.greet': { renderAction: 'GESTURE_GREET' },
47
- 'gesture.farewell': { renderAction: 'GESTURE_GOODBYE' },
48
- 'gesture.approve': { renderAction: 'GESTURE_APPROVE' },
49
- 'gesture.wait': { renderAction: 'GESTURE_WAIT' },
50
56
  });
51
57
 
52
58
  export const BEHAVIOR_ACTION_IDS = Object.freeze(Object.keys(BEHAVIOR_ACTIONS));
53
59
 
54
- /** Stable, promoted wire names map into the behavior catalog here and nowhere else. */
60
+ /** The two core wire names map into the behavior catalog here and nowhere else. */
55
61
  export const WIRE_ACTION_TO_BEHAVIOR = Object.freeze({
56
- ACK_RECEIVE: 'ack.receive',
57
- ACK_NOD: 'ack.nod',
62
+ ACKNOWLEDGE: 'ack',
58
63
  RESPONSE_INTERRUPTED: 'turn.interrupted',
59
- GESTURE_GREET: 'gesture.greet',
60
- GESTURE_GOODBYE: 'gesture.farewell',
61
- GESTURE_APPROVE: 'gesture.approve',
62
- GESTURE_WAIT: 'gesture.wait',
63
64
  });
64
65
 
65
66
  /**
@@ -77,11 +78,10 @@ export class BehaviorController {
77
78
  }
78
79
 
79
80
  setState(id, { force = false } = {}) {
80
- const def = BEHAVIOR_STATES[id];
81
- if (!def) throw new Error(`unknown behavior state: ${id}`);
81
+ if (!STATE_IDS.has(id)) throw new Error(`unknown behavior state: ${id}`);
82
82
  if (!force && this.state === id) return this;
83
83
  this.state = id;
84
- this.avatar.setState(def.renderState);
84
+ this.avatar.setState(id);
85
85
  return this;
86
86
  }
87
87
 
@@ -94,9 +94,21 @@ export class BehaviorController {
94
94
  return this;
95
95
  }
96
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
+ */
97
108
  wireAction(id) {
98
109
  const behaviorId = WIRE_ACTION_TO_BEHAVIOR[id];
99
- if (!behaviorId) throw new Error(`unknown wire action: ${id}`);
100
- return this.action(behaviorId);
110
+ if (behaviorId) return this.action(behaviorId);
111
+ this.avatar.action(id);
112
+ return this;
101
113
  }
102
114
  }
@@ -73,7 +73,7 @@ export function createCanvasRig({ url, images, face, label }) {
73
73
  renderer.resize(dpr);
74
74
  live = createLive(rig, faceModule);
75
75
  }).catch((error) => {
76
- if (!destroyed) console.error('[voqalize avatar] canvas rig failed to load', error);
76
+ if (!destroyed) console.error('[avatar] canvas rig failed to load', error);
77
77
  });
78
78
 
79
79
  return {
@@ -120,8 +120,8 @@ const num = (v, d) => (typeof v === 'number' && v === v ? v : d);
120
120
  // degree and a half about the sternum, which is exactly what you get when the
121
121
  // torso is one filled path. So the four are spent the way `poseTransforms`
122
122
  // spends them, with each avatar's own travels in `face.mjs BODY_LIVE`, and the
123
- // state that made this visible — STRAINING, which is voqalize's `CANT_HEAR`
124
- // and holds `torsoLean` at 0.70 — leans in instead of standing straight.
123
+ // state that made this visible — `CANT_HEAR`, which holds `torsoLean` at
124
+ // 0.70 — leans in instead of standing straight.
125
125
  //
126
126
  // AND THE TRUNK'S SHARE OF THE HEAD'S OWN CHANNELS, which is the rest of what
127
127
  // `poseTransforms` does and was left out the first time round. Three things,
@@ -13,7 +13,7 @@
13
13
  * drawing before anyone is asked to look at it.
14
14
  *
15
15
  * `advance` is the seam that lets one copy of the sweep serve both callers:
16
- * `apps/authoring/rig-check.html` lets real time pass and watches it happen, while
16
+ * a rig page in the working tree lets real time pass and watches it happen, while
17
17
  * `packages/avatar/test/conformance.test.ts` steps `{manual: true}` avatars by a fixed
18
18
  * dt and finishes in milliseconds. The sweep's own clock is handed to `speak`
19
19
  * so the mouth articulates under both — a stepped avatar reading
package/src/emotions.js CHANGED
@@ -14,8 +14,10 @@ export const EMOTIONS = {
14
14
  neutral: {},
15
15
  warm: {
16
16
  mouthCornerL: 0.48, mouthCornerR: 0.48,
17
- // A real smile squints. Without this it reads as a mask.
18
- squintL: 0.30, squintR: 0.30, lidL: 0.04, lidR: 0.04,
17
+ // A real smile squints. Without this it reads as a mask. From below —
18
+ // the cheek pushes the lower lid up (AU6) — with no upper lid dropped on
19
+ // top, which on a photographic eye read as sleepy rather than warm.
20
+ squintL: 0.30, squintR: 0.30,
19
21
  browRaiseL: 0.10, browRaiseR: 0.10,
20
22
  },
21
23
  curious: {