@voqalize/avatar 0.4.0 → 0.4.2

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 (77) hide show
  1. package/README.md +46 -28
  2. package/assets/README.md +4 -3
  3. package/assets/tanya.glb +0 -0
  4. package/assets/tara.glb +0 -0
  5. package/assets/tess.glb +0 -0
  6. package/assets/tushar.glb +0 -0
  7. package/client/internal.ts +20 -0
  8. package/client/three/assets.ts +16 -16
  9. package/client/three/holds.ts +7 -2
  10. package/client/three/internal.ts +5 -5
  11. package/client/three/motion-limits.json +5 -1
  12. package/client/three/sequences.ts +5 -5
  13. package/client/three/tanya-asset.ts +6 -0
  14. package/client/three/tanya.ts +2 -2
  15. package/client/three/tara-asset.ts +25 -0
  16. package/client/three/tara-rig.ts +37 -14
  17. package/client/three/tara.ts +2 -1
  18. package/client/three/tess-asset.ts +6 -0
  19. package/client/three/tess.ts +72 -0
  20. package/client/three/tushar-asset.ts +6 -0
  21. package/client/three/tushar.ts +2 -2
  22. package/dist/internal.d.ts +2 -2
  23. package/dist/internal.d.ts.map +1 -1
  24. package/dist/internal.js +12 -1
  25. package/dist/internal.js.map +1 -1
  26. package/dist/three/assets.d.ts +1 -16
  27. package/dist/three/assets.d.ts.map +1 -1
  28. package/dist/three/assets.js +15 -16
  29. package/dist/three/assets.js.map +1 -1
  30. package/dist/three/holds.d.ts +7 -2
  31. package/dist/three/holds.d.ts.map +1 -1
  32. package/dist/three/holds.js +7 -2
  33. package/dist/three/holds.js.map +1 -1
  34. package/dist/three/internal.d.ts +5 -5
  35. package/dist/three/internal.js +5 -5
  36. package/dist/three/motion-limits.json +5 -1
  37. package/dist/three/sequences.d.ts +5 -5
  38. package/dist/three/sequences.js +5 -5
  39. package/dist/three/tanya-asset.d.ts +7 -0
  40. package/dist/three/tanya-asset.d.ts.map +1 -0
  41. package/dist/three/tanya-asset.js +7 -0
  42. package/dist/three/tanya-asset.js.map +1 -0
  43. package/dist/three/tanya.js +2 -2
  44. package/dist/three/tanya.js.map +1 -1
  45. package/dist/three/tara-asset.d.ts +26 -0
  46. package/dist/three/tara-asset.d.ts.map +1 -0
  47. package/dist/three/tara-asset.js +26 -0
  48. package/dist/three/tara-asset.js.map +1 -0
  49. package/dist/three/tara-rig.d.ts +29 -9
  50. package/dist/three/tara-rig.d.ts.map +1 -1
  51. package/dist/three/tara-rig.js +14 -9
  52. package/dist/three/tara-rig.js.map +1 -1
  53. package/dist/three/tara.d.ts.map +1 -1
  54. package/dist/three/tara.js +2 -1
  55. package/dist/three/tara.js.map +1 -1
  56. package/dist/three/tess-asset.d.ts +7 -0
  57. package/dist/three/tess-asset.d.ts.map +1 -0
  58. package/dist/three/tess-asset.js +7 -0
  59. package/dist/three/tess-asset.js.map +1 -0
  60. package/dist/three/tess.d.ts +38 -0
  61. package/dist/three/tess.d.ts.map +1 -0
  62. package/dist/three/tess.js +54 -0
  63. package/dist/three/tess.js.map +1 -0
  64. package/dist/three/tushar-asset.d.ts +7 -0
  65. package/dist/three/tushar-asset.d.ts.map +1 -0
  66. package/dist/three/tushar-asset.js +7 -0
  67. package/dist/three/tushar-asset.js.map +1 -0
  68. package/dist/three/tushar.js +2 -2
  69. package/dist/three/tushar.js.map +1 -1
  70. package/package.json +5 -1
  71. package/src/avatar.d.ts +65 -2
  72. package/src/avatar.js +68 -63
  73. package/src/faces.js +1 -1
  74. package/src/gaze.js +19 -44
  75. package/src/idle.js +60 -26
  76. package/src/prosody.js +104 -14
  77. package/src/rig.d.ts +7 -0
@@ -11,7 +11,7 @@
11
11
 
12
12
  import type { PipecatClient } from "@pipecat-ai/client-js";
13
13
  import { AvatarClient, createSvgAvatar } from "../internal.js";
14
- import { ASSETS } from "./assets.js";
14
+ import { TUSHAR_GLB } from "./tushar-asset.js";
15
15
  import { BLENDER_ACTIONS, BLENDER_SEQUENCES, BLENDER_SUPPORTS } from "./sequences.js";
16
16
  import { headHold } from "./holds.js";
17
17
  import { createTaraRig, TARA_TUNING } from "./tara-rig.js";
@@ -41,7 +41,7 @@ export function createAvatar(options: AvatarOptions): AvatarInstance {
41
41
  if (!mount) throw new TypeError("createAvatar: `mount` is required");
42
42
  if (!client) throw new TypeError("createAvatar: `client` is required");
43
43
 
44
- const rigOptions: TaraRigOptions = { onReady, url: ASSETS.tushar };
44
+ const rigOptions: TaraRigOptions = { onReady, url: TUSHAR_GLB };
45
45
  // TARA-SPECIFIC, kept on purpose: `TARA_TUNING` (her mouth and motion gains)
46
46
  // is the first thing to question if he reads wrong in a call.
47
47
  const widget = createSvgAvatar({
@@ -21,8 +21,8 @@
21
21
  * renderer uses internally. It is *not* the seam to implement — see
22
22
  * docs/design-avatar-interface.md.
23
23
  */
24
- export { createAvatar as createSvgAvatar, REST, CHANNELS, RANGE, STATES, STATE_NAMES, ACTIONS, ACTION_IDS, GAZE_NAMES, GAZE_TARGETS, EMOTION_NAMES, VisemeTrack, VISEME_LETTERS, VISEME_SHAPES, SILENT, LEAD_MS, SHOULDER_TILT, shapeFor, normalizeCues, textToCues, ARPABET_TO_VISEME, AZURE_VISEME_TO_LETTER, } from "../src/avatar.js";
25
- export type { AvatarApi, AvatarStateName, AvatarActionId, AvatarGazeName, AvatarEmotionName, AvatarAction, AvatarMeta, VisemeLetter, Cue, PoseChannel, PoseOverrides, CreateAvatarOptions as CreateSvgAvatarOptions, } from "../src/avatar.js";
24
+ export { createAvatar as createSvgAvatar, REST, CHANNELS, RANGE, STATES, STATE_NAMES, ACTIONS, ACTION_IDS, GAZE_NAMES, GAZE_TARGETS, EMOTION_NAMES, VisemeTrack, VISEME_LETTERS, VISEME_SHAPES, SILENT, LEAD_MS, SHOULDER_TILT, INTERNAL_CLIPS, ClipPlayer, TAU, clamp, approach, makeParams, emotionPose, avatarFrame, createSvgRig, shapeFor, normalizeCues, textToCues, ARPABET_TO_VISEME, AZURE_VISEME_TO_LETTER, } from "../src/avatar.js";
25
+ export type { Clip, ClipSample, AvatarApi, AvatarStateName, AvatarActionId, AvatarGazeName, AvatarEmotionName, AvatarAction, AvatarMeta, VisemeLetter, Cue, PoseChannel, PoseOverrides, CreateAvatarOptions as CreateSvgAvatarOptions, } from "../src/avatar.js";
26
26
  export type { AvatarFrame, AvatarRig, AvatarRigFactory, HandFrame, RigPose, } from "../src/rig.js";
27
27
  export { BEHAVIOR_STATE_IDS, BEHAVIOR_ACTIONS, BEHAVIOR_ACTION_IDS, WIRE_ACTION_TO_BEHAVIOR, } from "../src/behavior.js";
28
28
  export type { BehaviorStateId, BehaviorActionId } from "../src/behavior.js";
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../client/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAIL,YAAY,IAAI,eAAe,EAG/B,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAO,EACP,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EAEb,WAAW,EACX,cAAc,EACd,aAAa,EACb,MAAM,EACN,OAAO,EAEP,aAAa,EACb,QAAQ,EACR,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,SAAS,EACT,eAAe,EACf,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,GAAG,EACH,WAAW,EACX,aAAa,EACb,mBAAmB,IAAI,sBAAsB,GAC9C,MAAM,kBAAkB,CAAC;AAK1B,YAAY,EACV,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,OAAO,GACR,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAM9D,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EACV,aAAa,EACb,SAAS,EACT,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../client/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAIL,YAAY,IAAI,eAAe,EAG/B,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAO,EACP,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa,EAEb,WAAW,EACX,cAAc,EACd,aAAa,EACb,MAAM,EACN,OAAO,EAEP,aAAa,EAMb,cAAc,EACd,UAAU,EACV,GAAG,EACH,KAAK,EACL,QAAQ,EAKR,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EACV,IAAI,EACJ,UAAU,EACV,SAAS,EACT,eAAe,EACf,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,GAAG,EACH,WAAW,EACX,aAAa,EACb,mBAAmB,IAAI,sBAAsB,GAC9C,MAAM,kBAAkB,CAAC;AAK1B,YAAY,EACV,WAAW,EACX,SAAS,EACT,gBAAgB,EAChB,SAAS,EACT,OAAO,GACR,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE5E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAM9D,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EACV,aAAa,EACb,SAAS,EACT,aAAa,EACb,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,mBAAmB,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/internal.js CHANGED
@@ -32,7 +32,18 @@ REST, CHANNELS, RANGE, STATES, STATE_NAMES, ACTIONS, ACTION_IDS, GAZE_NAMES, GAZ
32
32
  // The viseme clock and its tables.
33
33
  VisemeTrack, VISEME_LETTERS, VISEME_SHAPES, SILENT, LEAD_MS,
34
34
  // The body's share of a held tilt, for a page driving a rig by hand.
35
- SHOULDER_TILT, shapeFor, normalizeCues, textToCues, ARPABET_TO_VISEME, AZURE_VISEME_TO_LETTER, } from "../src/avatar.js";
35
+ SHOULDER_TILT,
36
+ // Stepping a gesture by hand: the catalogue, the player, and the smoothing
37
+ // law the mixer puts between a clip's keyframes and the face. A filmstrip
38
+ // instrument needs all three, and the one thing it must not do is fork the
39
+ // smoothing — that is the part a keyframe is authored against
40
+ // (docs/internal-mixer.md § Smoothing).
41
+ INTERNAL_CLIPS, ClipPlayer, TAU, clamp, approach,
42
+ // Posing a face directly, with no mixer, no clock and no client above it —
43
+ // what a pose sheet or a rig-conformance page does. `makeParams` fills the
44
+ // rests around a handful of overrides, `emotionPose` names a familiar set of
45
+ // them, `avatarFrame` wraps the result and `createSvgRig(face)` draws it.
46
+ makeParams, emotionPose, avatarFrame, createSvgRig, shapeFor, normalizeCues, textToCues, ARPABET_TO_VISEME, AZURE_VISEME_TO_LETTER, } from "../src/avatar.js";
36
47
  export { BEHAVIOR_STATE_IDS, BEHAVIOR_ACTIONS, BEHAVIOR_ACTION_IDS, WIRE_ACTION_TO_BEHAVIOR, } from "../src/behavior.js";
37
48
  export { isAvatarMessage, CORE_ACTION_IDS } from "./types.js";
38
49
  // A renderer may reuse the one lifecycle/precedence ladder without gaining a
@@ -1 +1 @@
1
- {"version":3,"file":"internal.js","sourceRoot":"","sources":["../client/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO;AACL,2EAA2E;AAC3E,0DAA0D;AAC1D,8DAA8D;AAC9D,YAAY,IAAI,eAAe;AAC/B,8EAA8E;AAC9E,oBAAoB;AACpB,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAO,EACP,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa;AACb,mCAAmC;AACnC,WAAW,EACX,cAAc,EACd,aAAa,EACb,MAAM,EACN,OAAO;AACP,qEAAqE;AACrE,aAAa,EACb,QAAQ,EACR,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AA4B1B,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAO9D,6EAA6E;AAC7E,kCAAkC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"internal.js","sourceRoot":"","sources":["../client/internal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO;AACL,2EAA2E;AAC3E,0DAA0D;AAC1D,8DAA8D;AAC9D,YAAY,IAAI,eAAe;AAC/B,8EAA8E;AAC9E,oBAAoB;AACpB,IAAI,EACJ,QAAQ,EACR,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAO,EACP,UAAU,EACV,UAAU,EACV,YAAY,EACZ,aAAa;AACb,mCAAmC;AACnC,WAAW,EACX,cAAc,EACd,aAAa,EACb,MAAM,EACN,OAAO;AACP,qEAAqE;AACrE,aAAa;AACb,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,8DAA8D;AAC9D,wCAAwC;AACxC,cAAc,EACd,UAAU,EACV,GAAG,EACH,KAAK,EACL,QAAQ;AACR,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,0EAA0E;AAC1E,UAAU,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,kBAAkB,CAAC;AA8B1B,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAO9D,6EAA6E;AAC7E,kCAAkC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,23 +1,8 @@
1
- /**
2
- * Where the three compiled characters are, resolved against this module's own
3
- * location.
4
- *
5
- * `new URL(…, import.meta.url)` and not a bundler's asset import: these files
6
- * are fetched at runtime by a consumer's app, and the spelling has to survive
7
- * Vite, webpack, Rollup, esbuild, a plain `tsc` output and a browser loading
8
- * the module directly. The query-suffix form this used to carry is Vite syntax —
9
- * every other toolchain, including the compiler that now builds this directory,
10
- * passes it through verbatim and produces an import of a file that is not there.
11
- *
12
- * The depth is the same from the source tree and from the compiled one
13
- * (`client/three/` and `dist/three/` are siblings), so one literal is correct in
14
- * both — and literal is load-bearing, because a bundler can only follow this
15
- * pattern when it can read the path without running anything.
16
- */
17
1
  export declare const ASSETS: Readonly<{
18
2
  tara: string;
19
3
  tushar: string;
20
4
  tanya: string;
5
+ tess: string;
21
6
  }>;
22
7
  /** Every compiled character, in build order. */
23
8
  export type CharacterName = keyof typeof ASSETS;
@@ -1 +1 @@
1
- {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../client/three/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,MAAM;;;;EAIjB,CAAC;AAEH,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,MAAM,CAAC"}
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../../client/three/assets.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,MAAM;;;;;EAKjB,CAAC;AAEH,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,MAAM,CAAC"}
@@ -1,22 +1,21 @@
1
1
  /**
2
- * Where the three compiled characters are, resolved against this module's own
3
- * location.
2
+ * Every compiled character as one table — for a page that enumerates them.
4
3
  *
5
- * `new URL(…, import.meta.url)` and not a bundler's asset import: these files
6
- * are fetched at runtime by a consumer's app, and the spelling has to survive
7
- * Vite, webpack, Rollup, esbuild, a plain `tsc` output and a browser loading
8
- * the module directly. The query-suffix form this used to carry is Vite syntax —
9
- * every other toolchain, including the compiler that now builds this directory,
10
- * passes it through verbatim and produces an import of a file that is not there.
11
- *
12
- * The depth is the same from the source tree and from the compiled one
13
- * (`client/three/` and `dist/three/` are siblings), so one literal is correct in
14
- * both — and literal is load-bearing, because a bundler can only follow this
15
- * pattern when it can read the path without running anything.
4
+ * Each URL lives in its own module (`tara-asset.ts` and its siblings) and this
5
+ * file only gathers them, because a bundler emits assets per module: anything
6
+ * importing *this* ships every GLB, which is right for a rig instrument that
7
+ * switches between characters and wrong for a consumer who mounted one.
8
+ * So the character modules never import this — they import their own — and the
9
+ * only reader is `/internal/three`, a separate entry point that ships nothing.
16
10
  */
11
+ import { TANYA_GLB } from "./tanya-asset.js";
12
+ import { TESS_GLB } from "./tess-asset.js";
13
+ import { TARA_GLB } from "./tara-asset.js";
14
+ import { TUSHAR_GLB } from "./tushar-asset.js";
17
15
  export const ASSETS = Object.freeze({
18
- tara: new URL("../../assets/tara.glb", import.meta.url).href,
19
- tushar: new URL("../../assets/tushar.glb", import.meta.url).href,
20
- tanya: new URL("../../assets/tanya.glb", import.meta.url).href,
16
+ tara: TARA_GLB,
17
+ tushar: TUSHAR_GLB,
18
+ tanya: TANYA_GLB,
19
+ tess: TESS_GLB,
21
20
  });
22
21
  //# sourceMappingURL=assets.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../client/three/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,IAAI,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI;IAC5D,MAAM,EAAE,IAAI,GAAG,CAAC,yBAAyB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI;IAChE,KAAK,EAAE,IAAI,GAAG,CAAC,wBAAwB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI;CAC/D,CAAC,CAAC"}
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../client/three/assets.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,QAAQ;IACd,MAAM,EAAE,UAAU;IAClB,KAAK,EAAE,SAAS;IAChB,IAAI,EAAE,QAAQ;CACf,CAAC,CAAC"}
@@ -3,12 +3,16 @@
3
3
  * pose units per axis, ready to hand to `createSvgAvatar` as `headHold`.
4
4
  *
5
5
  * The angles themselves are in `motion-limits.json` and are only
6
- * there. They were read off the three faces by the person who owns how they
6
+ * there. They were read off the faces by the person who owns how they
7
7
  * look, one axis at a time, at the surface a call actually shows — which is
8
8
  * the only way this kind of number can be got, and the reason nothing here
9
9
  * recomputes, adjusts or second-guesses one. This file is the unit change and
10
10
  * nothing else.
11
11
  *
12
+ * An axis the file never carried is the same case, and a character built but
13
+ * not yet driven by the owner has none of them: the file says so instead of
14
+ * carrying a number nobody read, and the fallback below is what holds her.
15
+ *
12
16
  * An axis the file marks for re-measurement is not enforced: it was read off an
13
17
  * asset that has since been fixed, and holding the driver to a defect that no
14
18
  * longer exists is worse than the defect. It falls back to the tightest angle
@@ -27,7 +31,8 @@ declare const AXIS: {
27
31
  readonly headRoll: "roll";
28
32
  };
29
33
  export type HeadHold = Partial<Record<keyof typeof AXIS, number>>;
30
- /** `name` as the limits file spells it: tara, tushar, tanya. */
34
+ /** `name` as the limits file spells it — a character with no live axis there
35
+ * is held to the tightest budget any other is still vouched for. */
31
36
  export declare function headHold(name: keyof typeof LIMITS.characters): HeadHold;
32
37
  export {};
33
38
  //# sourceMappingURL=holds.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"holds.d.ts","sourceRoot":"","sources":["../../client/three/holds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,MAAM,MAAM,sBAAsB,CAAuB;AAUhE,QAAA,MAAM,IAAI;;;;CAAoE,CAAC;AAE/E,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAmBlE,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,MAAM,CAAC,UAAU,GAAG,QAAQ,CAQvE"}
1
+ {"version":3,"file":"holds.d.ts","sourceRoot":"","sources":["../../client/three/holds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,MAAM,MAAM,sBAAsB,CAAuB;AAUhE,QAAA,MAAM,IAAI;;;;CAAoE,CAAC;AAE/E,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,OAAO,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAmBlE;qEACqE;AACrE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,MAAM,CAAC,UAAU,GAAG,QAAQ,CAQvE"}
@@ -3,12 +3,16 @@
3
3
  * pose units per axis, ready to hand to `createSvgAvatar` as `headHold`.
4
4
  *
5
5
  * The angles themselves are in `motion-limits.json` and are only
6
- * there. They were read off the three faces by the person who owns how they
6
+ * there. They were read off the faces by the person who owns how they
7
7
  * look, one axis at a time, at the surface a call actually shows — which is
8
8
  * the only way this kind of number can be got, and the reason nothing here
9
9
  * recomputes, adjusts or second-guesses one. This file is the unit change and
10
10
  * nothing else.
11
11
  *
12
+ * An axis the file never carried is the same case, and a character built but
13
+ * not yet driven by the owner has none of them: the file says so instead of
14
+ * carrying a number nobody read, and the fallback below is what holds her.
15
+ *
12
16
  * An axis the file marks for re-measurement is not enforced: it was read off an
13
17
  * asset that has since been fixed, and holding the driver to a defect that no
14
18
  * longer exists is worse than the defect. It falls back to the tightest angle
@@ -42,7 +46,8 @@ function safest(axis) {
42
46
  .filter((deg) => deg !== undefined);
43
47
  return angles.length ? Math.min(...angles) : undefined;
44
48
  }
45
- /** `name` as the limits file spells it: tara, tushar, tanya. */
49
+ /** `name` as the limits file spells it — a character with no live axis there
50
+ * is held to the tightest budget any other is still vouched for. */
46
51
  export function headHold(name) {
47
52
  const out = {};
48
53
  for (const channel of Object.keys(AXIS)) {
@@ -1 +1 @@
1
- {"version":3,"file":"holds.js","sourceRoot":"","sources":["../../client/three/holds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,MAAM,MAAM,sBAAsB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,4EAA4E;AAC5E,MAAM,QAAQ,GAAG;IACf,OAAO,EAAE,QAAQ,CAAC,GAAG,GAAG,UAAU;IAClC,SAAS,EAAE,QAAQ,CAAC,KAAK,GAAG,UAAU;IACtC,QAAQ,EAAE,QAAQ,CAAC,IAAI,GAAG,UAAU;CAC5B,CAAC;AAEX,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAW,CAAC;AAM/E,+EAA+E;AAC/E,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,IAAY,EAAwB,EAAE;IAChE,MAAM,QAAQ,GAAI,MAAM,CAAC,UAAsD,CAAC,IAAI,CAAC,CAAC;IACtF,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAyB,CAAC;IACvD,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC,CAAC;AAEF,0EAA0E;AAC1E,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;SAC1C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;SACrC,MAAM,CAAC,CAAC,GAAG,EAAiB,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,IAAoC;IAC3D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAA0B,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,GAAG,KAAK,SAAS;YAAE,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"holds.js","sourceRoot":"","sources":["../../client/three/holds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,MAAM,MAAM,sBAAsB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,4EAA4E;AAC5E,MAAM,QAAQ,GAAG;IACf,OAAO,EAAE,QAAQ,CAAC,GAAG,GAAG,UAAU;IAClC,SAAS,EAAE,QAAQ,CAAC,KAAK,GAAG,UAAU;IACtC,QAAQ,EAAE,QAAQ,CAAC,IAAI,GAAG,UAAU;CAC5B,CAAC;AAEX,MAAM,IAAI,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAW,CAAC;AAM/E,+EAA+E;AAC/E,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,IAAY,EAAwB,EAAE;IAChE,MAAM,QAAQ,GAAI,MAAM,CAAC,UAAsD,CAAC,IAAI,CAAC,CAAC;IACtF,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC,IAAI,CAAyB,CAAC;IACvD,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC,CAAC;AAEF,0EAA0E;AAC1E,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC;SAC1C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC;SACrC,MAAM,CAAC,CAAC,GAAG,EAAiB,EAAE,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC;AAED;qEACqE;AACrE,MAAM,UAAU,QAAQ,CAAC,IAAoC;IAC3D,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAA0B,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,GAAG,KAAK,SAAS;YAAE,GAAG,CAAC,OAAO,CAAC,GAAG,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * The 2.5-D rig, below the `createAvatar` modules — `/internal`, no semver promise.
3
3
  *
4
- * `tara.ts`, `tushar.ts` and `tanya.ts` are what a consumer gets: one
5
- * `createAvatar` each, the mixer behind it, nothing to configure. This is the
6
- * other door, for a page whose subject is the *rig* rather than the call — the
7
- * renderer with no mixer in front of it, the three compiled characters, and the
8
- * head envelope their channels are scaled by. Its readers are the rig instruments
4
+ * A character's own module — `tara.ts`, `tushar.ts`, `tanya.ts`, `tess.ts` — is
5
+ * what a consumer gets: one `createAvatar` each, the mixer behind it, nothing to
6
+ * configure. This is the other door, for a page whose subject is the *rig*
7
+ * rather than the call — the renderer with no mixer in front of it, every
8
+ * compiled character, and the head envelope their channels are scaled by. Its readers are the rig instruments
9
9
  * in the working tree, and nothing that ships.
10
10
  *
11
11
  * It exists because those instruments were reaching around the package instead,
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * The 2.5-D rig, below the `createAvatar` modules — `/internal`, no semver promise.
3
3
  *
4
- * `tara.ts`, `tushar.ts` and `tanya.ts` are what a consumer gets: one
5
- * `createAvatar` each, the mixer behind it, nothing to configure. This is the
6
- * other door, for a page whose subject is the *rig* rather than the call — the
7
- * renderer with no mixer in front of it, the three compiled characters, and the
8
- * head envelope their channels are scaled by. Its readers are the rig instruments
4
+ * A character's own module — `tara.ts`, `tushar.ts`, `tanya.ts`, `tess.ts` — is
5
+ * what a consumer gets: one `createAvatar` each, the mixer behind it, nothing to
6
+ * configure. This is the other door, for a page whose subject is the *rig*
7
+ * rather than the call — the renderer with no mixer in front of it, every
8
+ * compiled character, and the head envelope their channels are scaled by. Its readers are the rig instruments
9
9
  * in the working tree, and nothing that ships.
10
10
  *
11
11
  * It exists because those instruments were reaching around the package instead,
@@ -110,7 +110,11 @@
110
110
  "fixed": "2026-09-19"
111
111
  }
112
112
  ]
113
+ },
114
+ "tess": {
115
+ "unmeasured": "Nothing here has been read off her. She was built on 2026-09-20 and the owner has not driven her yet, so this entry carries no angle at all rather than a plausible one: `holds.ts` falls back to the tightest angle still live on each axis, which is the budget the faces nobody doubts are already held to. Drive her with the hold-and-move instrument and this becomes the same shape as the entries above.",
116
+ "defects": []
113
117
  }
114
118
  },
115
- "remeasure": "An axis carrying `remeasure` was measured on an asset that has since changed. It stays as written \u2014 a measurement is not edited by someone who did not make it \u2014 and a gate skips it and says so."
119
+ "remeasure": "An axis carrying `remeasure` was measured on an asset that has since changed. It stays as written — a measurement is not edited by someone who did not make it — and a gate skips it and says so."
116
120
  }
@@ -56,13 +56,13 @@ export declare const BLENDER_ACTIONS: Readonly<{
56
56
  * What every character in this package answers to — each one's `supports`, the
57
57
  * optional declaration a driving UI reads (`AvatarSupport`).
58
58
  *
59
- * Derived rather than written down, because the three parts already exist: the
60
- * two ids every avatar owes a server, the mixer's own clip catalogue these
61
- * characters inherit whole, and this file's own names. A hand-kept fourth copy
62
- * would only be able to disagree with them. `BLENDER_ACTIONS` adds nothing —
59
+ * Derived rather than written down, because every part of it already exists: the
60
+ * ids every avatar owes a server, the mixer's own clip catalogue these
61
+ * characters inherit whole, and this file's own names. A hand-kept copy beside
62
+ * them would only be able to disagree with them. `BLENDER_ACTIONS` adds nothing —
63
63
  * it re-shapes an id the mixer already has.
64
64
  *
65
- * It is one list for tara, tushar and tanya for the same reason the tables are:
65
+ * It is one list for every 2.5-D character for the same reason the tables are:
66
66
  * they are one rig driven through one set of channels. A character that stops
67
67
  * being able to do one of these owes its own `supports`, not an edit here.
68
68
  */
@@ -242,13 +242,13 @@ void BLENDER_ACTIONS;
242
242
  * What every character in this package answers to — each one's `supports`, the
243
243
  * optional declaration a driving UI reads (`AvatarSupport`).
244
244
  *
245
- * Derived rather than written down, because the three parts already exist: the
246
- * two ids every avatar owes a server, the mixer's own clip catalogue these
247
- * characters inherit whole, and this file's own names. A hand-kept fourth copy
248
- * would only be able to disagree with them. `BLENDER_ACTIONS` adds nothing —
245
+ * Derived rather than written down, because every part of it already exists: the
246
+ * ids every avatar owes a server, the mixer's own clip catalogue these
247
+ * characters inherit whole, and this file's own names. A hand-kept copy beside
248
+ * them would only be able to disagree with them. `BLENDER_ACTIONS` adds nothing —
249
249
  * it re-shapes an id the mixer already has.
250
250
  *
251
- * It is one list for tara, tushar and tanya for the same reason the tables are:
251
+ * It is one list for every 2.5-D character for the same reason the tables are:
252
252
  * they are one rig driven through one set of channels. A character that stops
253
253
  * being able to do one of these owes its own `supports`, not an edit here.
254
254
  */
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Where tanya's compiled GLB is. One character, one module — see
3
+ * [tara-asset.ts](./tara-asset.ts) for why that separation is load-bearing and
4
+ * why the `new URL` literal is spelled exactly this way.
5
+ */
6
+ export declare const TANYA_GLB: string;
7
+ //# sourceMappingURL=tanya-asset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tanya-asset.d.ts","sourceRoot":"","sources":["../../client/three/tanya-asset.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,SAAS,QAA0D,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Where tanya's compiled GLB is. One character, one module — see
3
+ * [tara-asset.ts](./tara-asset.ts) for why that separation is load-bearing and
4
+ * why the `new URL` literal is spelled exactly this way.
5
+ */
6
+ export const TANYA_GLB = new URL("../../assets/tanya.glb", import.meta.url).href;
7
+ //# sourceMappingURL=tanya-asset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tanya-asset.js","sourceRoot":"","sources":["../../client/three/tanya-asset.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,wBAAwB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC"}
@@ -13,7 +13,7 @@
13
13
  * nothing — the rig reads the window from the GLB.
14
14
  */
15
15
  import { AvatarClient, createSvgAvatar } from "../internal.js";
16
- import { ASSETS } from "./assets.js";
16
+ import { TANYA_GLB } from "./tanya-asset.js";
17
17
  import { BLENDER_ACTIONS, BLENDER_SEQUENCES, BLENDER_SUPPORTS } from "./sequences.js";
18
18
  import { headHold } from "./holds.js";
19
19
  import { createTaraRig, TARA_TUNING } from "./tara-rig.js";
@@ -26,7 +26,7 @@ export function createAvatar(options) {
26
26
  throw new TypeError("createAvatar: `mount` is required");
27
27
  if (!client)
28
28
  throw new TypeError("createAvatar: `client` is required");
29
- const rigOptions = { onReady, url: ASSETS.tanya };
29
+ const rigOptions = { onReady, url: TANYA_GLB };
30
30
  // TARA-SPECIFIC, kept on purpose: `TARA_TUNING` (her mouth and motion gains)
31
31
  // is the first thing to question if she reads wrong in a call.
32
32
  const widget = createSvgAvatar({
@@ -1 +1 @@
1
- {"version":3,"file":"tanya.js","sourceRoot":"","sources":["../../client/three/tanya.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAkB3D;kDACkD;AAClD,MAAM,CAAC,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAEzC,MAAM,UAAU,YAAY,CAAC,OAAsB;IACjD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IACrD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,oCAAoC,CAAC,CAAC;IAEvE,MAAM,UAAU,GAAmB,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IAClE,6EAA6E;IAC7E,+DAA+D;IAC/D,MAAM,MAAM,GAAG,eAAe,CAAC;QAC7B,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK;QAClD,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,WAAW;QACtE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK;KACtC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAErC,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,OAAO;QACL,OAAO;YACL,IAAI,SAAS;gBAAE,OAAO;YACtB,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM,EAAE,CAAC;YACT,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"tanya.js","sourceRoot":"","sources":["../../client/three/tanya.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACtF,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAkB3D;kDACkD;AAClD,MAAM,CAAC,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAEzC,MAAM,UAAU,YAAY,CAAC,OAAsB;IACjD,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IACrD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAC;IACrE,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,SAAS,CAAC,oCAAoC,CAAC,CAAC;IAEvE,MAAM,UAAU,GAAmB,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;IAC/D,6EAA6E;IAC7E,+DAA+D;IAC/D,MAAM,MAAM,GAAG,eAAe,CAAC;QAC7B,KAAK,EAAE,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE,KAAK;QAClD,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,WAAW;QACtE,QAAQ,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,GAAG,KAAK;KACtC,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAErC,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,OAAO;QACL,OAAO;YACL,IAAI,SAAS;gBAAE,OAAO;YACtB,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM,EAAE,CAAC;YACT,MAAM,CAAC,OAAO,EAAE,CAAC;YACjB,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Where tara's compiled GLB is — one character, one module, on purpose.
3
+ *
4
+ * `new URL(…, import.meta.url)` and not a bundler's asset import: these files
5
+ * are fetched at runtime by a consumer's app, and the spelling has to survive
6
+ * Vite, webpack, Rollup, esbuild, a plain `tsc` output and a browser loading the
7
+ * module directly. The query-suffix form this used to carry is Vite syntax —
8
+ * every other toolchain, including the compiler that now builds this directory,
9
+ * passes it through verbatim and produces an import of a file that is not there.
10
+ *
11
+ * The depth is the same from the source tree and from the compiled one
12
+ * (`client/three/` and `dist/three/` are siblings), so one literal is correct in
13
+ * both — and literal is load-bearing, because a bundler can only follow this
14
+ * pattern when it can read the path without running anything.
15
+ *
16
+ * **One module per character is the whole point, and it is a bundling fact, not
17
+ * tidiness.** A bundler that can follow `new URL` emits the file it names, and
18
+ * it decides what to emit per *module*: three literals in one object meant a
19
+ * consumer who imported one character shipped all three GLBs — ~1.3 MB of a
20
+ * character they never mount, in every build we checked. Splitting them is what
21
+ * makes `@voqalize/avatar/avatars/tara` cost tara. Nothing that a character
22
+ * module loads may reach for another character's URL, which is also why the rig
23
+ * takes its `url` from its caller instead of defaulting to tara's.
24
+ */
25
+ export declare const TARA_GLB: string;
26
+ //# sourceMappingURL=tara-asset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tara-asset.d.ts","sourceRoot":"","sources":["../../client/three/tara-asset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,QAAQ,QAAyD,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Where tara's compiled GLB is — one character, one module, on purpose.
3
+ *
4
+ * `new URL(…, import.meta.url)` and not a bundler's asset import: these files
5
+ * are fetched at runtime by a consumer's app, and the spelling has to survive
6
+ * Vite, webpack, Rollup, esbuild, a plain `tsc` output and a browser loading the
7
+ * module directly. The query-suffix form this used to carry is Vite syntax —
8
+ * every other toolchain, including the compiler that now builds this directory,
9
+ * passes it through verbatim and produces an import of a file that is not there.
10
+ *
11
+ * The depth is the same from the source tree and from the compiled one
12
+ * (`client/three/` and `dist/three/` are siblings), so one literal is correct in
13
+ * both — and literal is load-bearing, because a bundler can only follow this
14
+ * pattern when it can read the path without running anything.
15
+ *
16
+ * **One module per character is the whole point, and it is a bundling fact, not
17
+ * tidiness.** A bundler that can follow `new URL` emits the file it names, and
18
+ * it decides what to emit per *module*: three literals in one object meant a
19
+ * consumer who imported one character shipped all three GLBs — ~1.3 MB of a
20
+ * character they never mount, in every build we checked. Splitting them is what
21
+ * makes `@voqalize/avatar/avatars/tara` cost tara. Nothing that a character
22
+ * module loads may reach for another character's URL, which is also why the rig
23
+ * takes its `url` from its caller instead of defaulting to tara's.
24
+ */
25
+ export const TARA_GLB = new URL("../../assets/tara.glb", import.meta.url).href;
26
+ //# sourceMappingURL=tara-asset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tara-asset.js","sourceRoot":"","sources":["../../client/three/tara-asset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC"}
@@ -49,10 +49,11 @@ import type { AvatarRig } from "../internal.js";
49
49
  * shell, and a large turn is where that reads as a cardboard cutout rather than
50
50
  * a head. **It opened from 9° to 15° on 2026-09-18.** The cutout was measured
51
51
  * rather than assumed — a ladder rendered at 9/12/15/18/21/25/30 and read at
52
- * crop on all three characters is clean to 21° on tara and to 18° on tanya and
53
- * tushar. 9° was therefore set at half of where the artefact actually begins,
54
- * and the stiffness the owner reported on tanya's turns was that margin, not
55
- * her asset. 15° keeps 3° of headroom on the tightest of the three.
52
+ * crop on the characters that existed then is clean to 21° on tara and to 18°
53
+ * on tanya and tushar. 9° was therefore set at half of where the artefact
54
+ * actually begins, and the stiffness the owner reported on tanya's turns was
55
+ * that margin, not her asset. 15° keeps 3° of headroom under the 18° the
56
+ * tightest of them read.
56
57
  *
57
58
  * This is not for speech: Busso wants ±1.15° of yaw in neutral conversation and
58
59
  * always did. It is for a head that turns to *look* at something, which is what
@@ -334,14 +335,33 @@ export declare const TARA_TUNING: {
334
335
  export interface TaraRigOptions {
335
336
  /** Called once the GLB is in the scene, for a capture tool that must wait. */
336
337
  readonly onReady?: () => void;
337
- /** The character's GLB, when it is not tara. The second-character seam: every
338
- * build fact the rig reads (jaw-shadow tile, rim depth, morph names) travels
339
- * in the GLB's own extras, so a character built by the same scripts needs
340
- * nothing else. Tara's tuning is still applied, which is the experiment. */
341
- readonly url?: string;
338
+ /** The character's GLB. The second-character seam: every build fact the rig
339
+ * reads (jaw-shadow tile, rim depth, morph names) travels in the GLB's own
340
+ * extras, so a character built by the same scripts needs nothing else. Tara's
341
+ * tuning is still applied, which is the experiment.
342
+ *
343
+ * Required, and it used to default to tara's. A default meant this module
344
+ * imported one character's URL, and a bundler emits assets per module — so
345
+ * every consumer of *any* character shipped tara's GLB whether or not they
346
+ * mounted her. The caller knows which character it is building; this file
347
+ * must not ([tara-asset.ts](./tara-asset.ts)). */
348
+ readonly url: string;
342
349
  /** `false` leaves an asset's expression maps unread, for a capture tool
343
350
  * comparing the face with and without them. */
344
351
  readonly expression?: boolean;
352
+ /** Keeps the drawing buffer readable after the browser has composited it, so
353
+ * a caller can copy the canvas out at any moment rather than only from
354
+ * inside the frame that drew it.
355
+ *
356
+ * A pose sheet needs this and a consumer must not have it. WebGL contexts are
357
+ * a handful per page, so a sheet of thirty poses cannot be thirty canvases —
358
+ * it holds one pose still, copies the canvas into a tile, and moves on. Copy
359
+ * outside the drawing frame and the buffer has already been cleared, which is
360
+ * a blank tile and not an error. The cost is that the buffer cannot be
361
+ * discarded, which on some drivers means a second copy of every frame; the
362
+ * consumer renders thirty frames a second forever and pays nothing for a
363
+ * readback it never performs. */
364
+ readonly readback?: boolean;
345
365
  }
346
366
  /**
347
367
  * The hair's roll, which is the one thing in this rig that is not a function of
@@ -1 +1 @@
1
- {"version":3,"file":"tara-rig.d.ts","sourceRoot":"","sources":["../../client/three/tara-rig.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAe,SAAS,EAAW,MAAM,gBAAgB,CAAC;AAmBtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AAKH,eAAO,MAAM,UAAU,MAAM,CAAC;AAC9B,eAAO,MAAM,QAAQ;;;;CAAkC,CAAC;AAyIxD;;;;;;qDAMqD;AACrD,eAAO,MAAM,OAAO;;;CAA2G,CAAC;AAGhI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiJd,CAAC;AA8bX,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B;;;iFAG6E;IAC7E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;oDACgD;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAsID;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS;;;;CAAyC,CAAC;AAEhE;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE,IAAI,MAAM;;;CAInF,CAAC;AAkCF,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CA4Z9E"}
1
+ {"version":3,"file":"tara-rig.d.ts","sourceRoot":"","sources":["../../client/three/tara-rig.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAe,SAAS,EAAW,MAAM,gBAAgB,CAAC;AAkBtE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoEG;AAKH,eAAO,MAAM,UAAU,MAAM,CAAC;AAC9B,eAAO,MAAM,QAAQ;;;;CAAkC,CAAC;AAyIxD;;;;;;qDAMqD;AACrD,eAAO,MAAM,OAAO;;;CAA2G,CAAC;AAGhI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiJd,CAAC;AA8bX,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B;;;;;;;;;uDASmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB;oDACgD;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;;;;;sCAWkC;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAsID;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS;;;;CAAyC,CAAC;AAEhE;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,EAAE,MAAM,MAAM,EAAE,QAAQ,MAAM,EAAE,IAAI,MAAM;;;CAInF,CAAC;AAkCF,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAga9E"}
@@ -28,7 +28,6 @@
28
28
  import { REST } from "../internal.js";
29
29
  import * as THREE from "three";
30
30
  import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
31
- import { ASSETS } from "./assets.js";
32
31
  import { HARD_BUDGET, pixelRatioFor } from "./budgets.js";
33
32
  /**
34
33
  * The camera, from `scripts/render_frame.py`. It is orthographic and that is
@@ -65,10 +64,11 @@ const FRAME_CENTRE = (FRAME.top + FRAME.bottom) / 2;
65
64
  * shell, and a large turn is where that reads as a cardboard cutout rather than
66
65
  * a head. **It opened from 9° to 15° on 2026-09-18.** The cutout was measured
67
66
  * rather than assumed — a ladder rendered at 9/12/15/18/21/25/30 and read at
68
- * crop on all three characters is clean to 21° on tara and to 18° on tanya and
69
- * tushar. 9° was therefore set at half of where the artefact actually begins,
70
- * and the stiffness the owner reported on tanya's turns was that margin, not
71
- * her asset. 15° keeps 3° of headroom on the tightest of the three.
67
+ * crop on the characters that existed then is clean to 21° on tara and to 18°
68
+ * on tanya and tushar. 9° was therefore set at half of where the artefact
69
+ * actually begins, and the stiffness the owner reported on tanya's turns was
70
+ * that margin, not her asset. 15° keeps 3° of headroom under the 18° the
71
+ * tightest of them read.
72
72
  *
73
73
  * This is not for speech: Busso wants ±1.15° of yaw in neutral conversation and
74
74
  * always did. It is for a head that turns to *look* at something, which is what
@@ -873,9 +873,9 @@ const radians = (deg) => (deg * Math.PI) / 180;
873
873
  * Three.js throws out of the constructor rather than returning anything, and it
874
874
  * has already written its own line to the console by then; that line is kept
875
875
  * because it names the underlying reason, which this one does not. */
876
- function webglRenderer() {
876
+ function webglRenderer(readback = false) {
877
877
  try {
878
- return new THREE.WebGLRenderer({ antialias: true, alpha: true });
878
+ return new THREE.WebGLRenderer({ antialias: true, alpha: true, preserveDrawingBuffer: readback });
879
879
  }
880
880
  catch {
881
881
  return null;
@@ -1081,12 +1081,17 @@ const expressionWeights = (pose, side, expression, into) => {
1081
1081
  // `options` is `unknown` in the contract, and stays `unknown` here: the mixer
1082
1082
  // passes `rigOptions` through verbatim and has no way to know any rig's shape.
1083
1083
  export function createTaraRig(mount, options) {
1084
- const { onReady, url = ASSETS.tara, expression: readExpression = true } = (options ?? {});
1084
+ const { onReady, url, expression: readExpression = true, readback = false } = (options ?? {});
1085
+ // A missing `url` is a caller's defect, not a browser condition — the WebGL
1086
+ // path below degrades because a driver is nobody's fault, whereas this would
1087
+ // otherwise be a 404 on a path spelled `undefined`.
1088
+ if (!url)
1089
+ throw new TypeError("createTaraRig: `url` is required");
1085
1090
  const scene = new THREE.Scene();
1086
1091
  const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 100);
1087
1092
  camera.position.set(0, FRAME_CENTRE, 6);
1088
1093
  camera.lookAt(0, FRAME_CENTRE, 0);
1089
- const renderer = webglRenderer();
1094
+ const renderer = webglRenderer(readback);
1090
1095
  // No context, no face — and that has to be the whole of it. `createAvatar` is
1091
1096
  // synchronous and returns `{ destroy }`, so a consumer has nothing to catch:
1092
1097
  // anything thrown here lands in *their* window and takes the call page with