@viji-dev/core 0.6.2 → 0.7.4

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.
@@ -1,9 +1,9 @@
1
- // Viji Artist API - Global Type Definitions
2
- // All types are placed inside declare global {} because this file uses export {}
3
- // for top-level await support, which makes it a module (where top-level declarations
4
- // would otherwise be module-scoped, not global).
5
-
6
- declare global {
1
+ // Viji Artist API - Global Type Definitions
2
+ // All types are placed inside declare global {} because this file uses export {}
3
+ // for top-level await support, which makes it a module (where top-level declarations
4
+ // would otherwise be module-scoped, not global).
5
+
6
+ declare global {
7
7
  /**
8
8
  * Real-time audio analysis for the main audio stream (`viji.audio`).
9
9
  * Provides volume, frequency bands, beat detection (with triggers, events, BPM),
@@ -118,7 +118,7 @@ declare global {
118
118
  */
119
119
  getWaveform: () => Float32Array;
120
120
  }
121
-
121
+
122
122
  /**
123
123
  * Lightweight audio analysis for additional or device audio streams. A strict
124
124
  * subset of `AudioAPI` that exposes volume, frequency bands, spectral features,
@@ -178,7 +178,7 @@ declare global {
178
178
  */
179
179
  getWaveform: () => Float32Array;
180
180
  }
181
-
181
+
182
182
  /**
183
183
  * Configuration object passed to `viji.button()`. The host renders a clickable
184
184
  * momentary button. The resulting `value` is `true` for one frame after press,
@@ -194,7 +194,7 @@ declare global {
194
194
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
195
195
  category?: ParameterCategory;
196
196
  }
197
-
197
+
198
198
  /**
199
199
  * Reactive object returned by `viji.button()`. `value` is a momentary flag:
200
200
  * `true` for exactly one frame after the user clicks, then auto-resets to
@@ -212,7 +212,7 @@ declare global {
212
212
  /** Visibility category that controls when the parameter is shown. */
213
213
  category: ParameterCategory;
214
214
  }
215
-
215
+
216
216
  /**
217
217
  * Options accepted by `VijiCore.captureFrame()` for snapshotting the current
218
218
  * scene. Controls output format, encoding, and target resolution / aspect.
@@ -230,7 +230,7 @@ declare global {
230
230
  */
231
231
  resolution?: number | Resolution;
232
232
  }
233
-
233
+
234
234
  /**
235
235
  * Configuration object passed to `viji.color()`. Defines the color picker's
236
236
  * label and UI grouping/visibility.
@@ -245,7 +245,7 @@ declare global {
245
245
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
246
246
  category?: ParameterCategory;
247
247
  }
248
-
248
+
249
249
  /**
250
250
  * Accepted input forms for color parameters. Internally normalized to canonical
251
251
  * lowercase 6-digit hex `#rrggbb`. See `viji.color()` for full documentation.
@@ -270,7 +270,7 @@ declare global {
270
270
  /** Brightness in 0..100. */
271
271
  b: number;
272
272
  };
273
-
273
+
274
274
  /**
275
275
  * Reactive object returned by `viji.color()`. `value` is always a canonical
276
276
  * lowercase 6-digit hex string; `rgb` and `hsb` are derived accessors that are
@@ -306,7 +306,7 @@ declare global {
306
306
  /** Visibility category that controls when the parameter is shown. */
307
307
  category: ParameterCategory;
308
308
  }
309
-
309
+
310
310
  /**
311
311
  * Configuration object passed to `viji.coordinate()`. The host renders a 2D
312
312
  * draggable pad; both `x` and `y` are clamped to `-1..1`.
@@ -323,7 +323,7 @@ declare global {
323
323
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
324
324
  category?: ParameterCategory;
325
325
  }
326
-
326
+
327
327
  /**
328
328
  * Reactive object returned by `viji.coordinate()`. `value.x` and `value.y` are
329
329
  * always in `-1..1`; the host UI renders a draggable 2D pad.
@@ -342,7 +342,7 @@ declare global {
342
342
  /** Visibility category that controls when the parameter is shown. */
343
343
  category: ParameterCategory;
344
344
  }
345
-
345
+
346
346
  /**
347
347
  * Two-component coordinate, both axes in `-1..1` (centered on the canvas).
348
348
  * Returned and consumed by `viji.coordinate()`.
@@ -353,20 +353,20 @@ declare global {
353
353
  /** Vertical axis in `-1..1` (bottom to top, `0` = canvas center). */
354
354
  y: number;
355
355
  };
356
-
356
+
357
357
  /**
358
358
  * Identifier for an individual computer-vision feature pipeline. Returned by
359
359
  * `VideoAPI.cv.getActiveFeatures()` and used internally to track state.
360
360
  */
361
361
  type CVFeature = 'faceDetection' | 'faceMesh' | 'handTracking' | 'poseDetection' | 'bodySegmentation' | 'emotionDetection';
362
-
362
+
363
363
  /**
364
364
  * Computer-vision processing rate relative to the scene's render rate.
365
365
  * `'full'` runs CV every frame, `'half'` every other frame, `'quarter'` every
366
366
  * fourth, `'eighth'` every eighth. Lower rates reduce CPU/GPU cost.
367
367
  */
368
368
  type CVFrameRateMode = 'full' | 'half' | 'quarter' | 'eighth';
369
-
369
+
370
370
  /**
371
371
  * Device motion data sourced from the platform's `DeviceMotionEvent`. All
372
372
  * accelerations are in metres per second squared (m/s²); rotation rates are
@@ -405,7 +405,7 @@ declare global {
405
405
  /** Interval between sensor updates in milliseconds. */
406
406
  interval: number;
407
407
  }
408
-
408
+
409
409
  /**
410
410
  * DeviceOrientationEvent data
411
411
  * Matches native DeviceOrientationEvent structure
@@ -420,7 +420,7 @@ declare global {
420
420
  /** True if using magnetometer (compass) for absolute orientation */
421
421
  absolute: boolean;
422
422
  }
423
-
423
+
424
424
  /**
425
425
  * Sensor snapshot for the device running the scene. Both fields read `null`
426
426
  * when the corresponding sensor is unavailable or has not delivered a sample yet.
@@ -431,7 +431,7 @@ declare global {
431
431
  /** Orientation data (alpha/beta/gamma in degrees). `null` if no orientation sensor is available. */
432
432
  orientation: DeviceOrientationData | null;
433
433
  }
434
-
434
+
435
435
  /**
436
436
  * External device state (includes id and name)
437
437
  */
@@ -445,7 +445,7 @@ declare global {
445
445
  /** Device audio stream (null if not available) */
446
446
  audio: AudioStreamAPI | null;
447
447
  }
448
-
448
+
449
449
  /**
450
450
  * 52 ARKit-compatible face blendshape coefficients (each in 0..1) derived from
451
451
  * MediaPipe FaceLandmarker. All fields are `0` unless emotion detection is
@@ -557,17 +557,27 @@ declare global {
557
557
  /** Coefficient (0..1) for the tongue protruding. */
558
558
  tongueOut: number;
559
559
  }
560
-
560
+
561
561
  /**
562
- * One detected face produced by the video CV pipeline. Always carries `id`,
563
- * `bounds`, `center`, and `confidence`; the rest is populated only when the
564
- * matching CV feature is enabled (face mesh for `landmarks` and `headPose`,
565
- * emotion detection for `expressions` and `blendshapes`).
562
+ * One detected face produced by the video CV pipeline. Each field is
563
+ * populated only by its source model; fields whose source model is not
564
+ * enabled read as `null` so the absence is loud rather than silent. Enable
565
+ * the relevant `enable*` verb on `viji.video.cv` to populate each group:
566
+ *
567
+ * - `bounds`, `center`, `confidence`: populated by `enableFaceDetection(true)`.
568
+ * - `landmarks`: populated by `enableFaceMesh(true)` (empty array otherwise).
569
+ * - `headPose`: populated by `enableFaceMesh(true)`.
570
+ * - `blendshapes`, `expressions`: populated by `enableEmotionDetection(true)`.
571
+ *
572
+ * If a field is `null`, call the corresponding `enable*` verb on
573
+ * `viji.video.cv` to populate it. Each active CV feature consumes its own
574
+ * WebGL context for MediaPipe; enabling many at once on lower-end hardware
575
+ * can hit context limits.
566
576
  */
567
577
  interface FaceData {
568
578
  /** Index-based face identifier (`0`, `1`, …). Stable within a single frame, may change between frames. */
569
579
  id: number;
570
- /** Bounding box, all components normalized to 0..1 of the source frame. */
580
+ /** Bounding box, all components normalized to 0..1 of the source frame. `null` unless face detection is enabled. */
571
581
  bounds: {
572
582
  /** Left edge of the face bounding box, normalized 0..1. */
573
583
  x: number;
@@ -577,16 +587,16 @@ declare global {
577
587
  width: number;
578
588
  /** Height of the face bounding box, normalized 0..1. */
579
589
  height: number;
580
- };
581
- /** Center of the bounding box, normalized 0..1. */
590
+ } | null;
591
+ /** Center of the bounding box, normalized 0..1. `null` unless face detection is enabled. */
582
592
  center: {
583
593
  /** Horizontal center of the face, normalized 0..1. */
584
594
  x: number;
585
595
  /** Vertical center of the face, normalized 0..1. */
586
596
  y: number;
587
- };
588
- /** Detection confidence in 0..1. */
589
- confidence: number;
597
+ } | null;
598
+ /** Detection confidence in 0..1. `null` unless face detection is enabled. */
599
+ confidence: number | null;
590
600
  /** 468 normalized face mesh landmarks when face mesh is enabled; empty array otherwise. */
591
601
  landmarks: {
592
602
  /** Horizontal landmark coordinate, normalized 0..1. */
@@ -596,7 +606,16 @@ declare global {
596
606
  /** Optional depth (relative). */
597
607
  z?: number;
598
608
  }[];
599
- /** Seven expression confidence scores in 0..1. All zero unless emotion detection is enabled. */
609
+ /** Estimated head rotation in degrees. `null` unless face mesh is enabled. */
610
+ headPose: {
611
+ /** Up/down rotation in degrees (-90..90). Positive = head looking up, negative = looking down. */
612
+ pitch: number;
613
+ /** Left/right rotation in degrees (-90..90). Positive = head turned to the subject's right, negative = to the left. */
614
+ yaw: number;
615
+ /** Tilt rotation in degrees (-180..180). Positive = head tilted to the subject's right (right ear toward right shoulder). */
616
+ roll: number;
617
+ } | null;
618
+ /** Seven expression confidence scores in 0..1. `null` unless emotion detection is enabled. */
600
619
  expressions: {
601
620
  /** Confidence (0..1) of a neutral expression. */
602
621
  neutral: number;
@@ -612,27 +631,18 @@ declare global {
612
631
  disgusted: number;
613
632
  /** Confidence (0..1) of a fearful expression. */
614
633
  fearful: number;
615
- };
616
- /** Estimated head rotation. All zero unless face mesh is enabled. */
617
- headPose: {
618
- /** Up/down rotation in degrees (-90..90). Positive = head looking up, negative = looking down. */
619
- pitch: number;
620
- /** Left/right rotation in degrees (-90..90). Positive = head turned to the subject's right, negative = to the left. */
621
- yaw: number;
622
- /** Tilt rotation in degrees (-180..180). Positive = head tilted to the subject's right (right ear toward right shoulder). */
623
- roll: number;
624
- };
625
- /** 52 ARKit-compatible facial muscle coefficients. All zero unless emotion detection is enabled. */
626
- blendshapes: FaceBlendshapes;
634
+ } | null;
635
+ /** 52 ARKit-compatible facial muscle coefficients. `null` unless emotion detection is enabled. */
636
+ blendshapes: FaceBlendshapes | null;
627
637
  }
628
-
638
+
629
639
  /**
630
640
  * Render-loop pacing mode. `'full'` runs `render()` on every animation frame
631
641
  * (~60 FPS). `'half'` runs every second frame (~30 FPS), saving CPU/GPU on
632
642
  * lower-cost scenes.
633
643
  */
634
644
  type FrameRateMode = 'full' | 'half';
635
-
645
+
636
646
  /**
637
647
  * One named frequency band reported by the audio analyzer
638
648
  * (e.g., `{ name: 'low', min: 20, max: 120 }`). Frequencies are in Hertz.
@@ -645,7 +655,7 @@ declare global {
645
655
  /** Upper edge of the band in Hertz (exclusive). */
646
656
  max: number;
647
657
  }
648
-
658
+
649
659
  /**
650
660
  * One detected hand produced by the video CV pipeline. Carries 21 landmarks,
651
661
  * a palm shortcut, and ML-classified gesture confidence scores. Up to two
@@ -708,7 +718,7 @@ declare global {
708
718
  iLoveYou: number;
709
719
  };
710
720
  }
711
-
721
+
712
722
  /**
713
723
  * Configuration object passed to `viji.image()`. The host renders an upload
714
724
  * field; the user-supplied image becomes available as an `ImageBitmap` (Native)
@@ -724,7 +734,7 @@ declare global {
724
734
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
725
735
  category?: ParameterCategory;
726
736
  }
727
-
737
+
728
738
  /**
729
739
  * Reactive object returned by `viji.image()`. `value` is `null` until the user
730
740
  * uploads an image; in P5 scenes a `.p5` accessor is also added at runtime so
@@ -744,7 +754,7 @@ declare global {
744
754
  /** Visibility category that controls when the parameter is shown. */
745
755
  category: ParameterCategory;
746
756
  }
747
-
757
+
748
758
  /**
749
759
  * Keyboard input state for the current frame: per-key press / hold / release
750
760
  * queries (case-insensitive), the live set of held keys, modifier flags, and
@@ -785,7 +795,7 @@ declare global {
785
795
  */
786
796
  textureData: Uint8Array;
787
797
  }
788
-
798
+
789
799
  /**
790
800
  * Mouse input state for the current frame: position, individual button states,
791
801
  * per-frame movement, scroll wheel, and frame-based press / release events.
@@ -825,7 +835,7 @@ declare global {
825
835
  /** `true` if the mouse moved this frame (any non-zero `deltaX` / `deltaY`). Resets each frame. */
826
836
  wasMoved: boolean;
827
837
  }
828
-
838
+
829
839
  /**
830
840
  * Configuration object passed to `viji.number()`. Defines the bounds, step,
831
841
  * label, and UI grouping/visibility for a precise numeric input.
@@ -846,7 +856,7 @@ declare global {
846
856
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
847
857
  category?: ParameterCategory;
848
858
  }
849
-
859
+
850
860
  /**
851
861
  * Reactive object returned by `viji.number()`. Like `SliderParameter` but the
852
862
  * host UI renders a precise numeric input rather than a draggable slider.
@@ -869,14 +879,14 @@ declare global {
869
879
  /** Visibility category that controls when the parameter is shown. */
870
880
  category: ParameterCategory;
871
881
  }
872
-
882
+
873
883
  /**
874
884
  * Visibility category for a parameter. The host hides parameters whose category
875
885
  * is not currently available (e.g., `'audio'` controls hide when no audio
876
886
  * source is connected). `'general'` parameters are always visible.
877
887
  */
878
888
  type ParameterCategory = 'audio' | 'video' | 'interaction' | 'general';
879
-
889
+
880
890
  /**
881
891
  * Unified pointer that abstracts mouse and primary touch into a single input
882
892
  * stream. Recommended for click / drag / position-tracking interactions that
@@ -905,7 +915,7 @@ declare global {
905
915
  /** Currently active input source: `'touch'` when at least one touch is active, otherwise `'mouse'` (or `'none'` when the cursor is off-canvas). */
906
916
  type: 'mouse' | 'touch' | 'none';
907
917
  }
908
-
918
+
909
919
  /**
910
920
  * Detected body pose from MediaPipe BlazePose. `landmarks` contains 33 points;
911
921
  * the `face` / `torso` / `leftArm` / `rightArm` / `leftLeg` / `rightLeg` arrays
@@ -968,7 +978,7 @@ declare global {
968
978
  y: number;
969
979
  }[];
970
980
  }
971
-
981
+
972
982
  /**
973
983
  * Discrete pixel dimensions for a canvas, frame capture, or render target.
974
984
  */
@@ -978,7 +988,7 @@ declare global {
978
988
  /** Height in pixels (positive integer). */
979
989
  height: number;
980
990
  };
981
-
991
+
982
992
  /**
983
993
  * Per-pixel person/background segmentation mask. Mask dimensions reflect the
984
994
  * ML model's output and may differ from the source video frame.
@@ -991,7 +1001,7 @@ declare global {
991
1001
  /** Mask height in pixels. */
992
1002
  height: number;
993
1003
  }
994
-
1004
+
995
1005
  /**
996
1006
  * Configuration object passed to `viji.select()`. The host renders the choices
997
1007
  * as a dropdown or segmented control.
@@ -1008,7 +1018,7 @@ declare global {
1008
1018
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
1009
1019
  category?: ParameterCategory;
1010
1020
  }
1011
-
1021
+
1012
1022
  /**
1013
1023
  * Reactive object returned by `viji.select()`. `value` matches the element type
1014
1024
  * of the configured `options` array (`string` or `number`).
@@ -1027,7 +1037,7 @@ declare global {
1027
1037
  /** Visibility category that controls when the parameter is shown. */
1028
1038
  category: ParameterCategory;
1029
1039
  }
1030
-
1040
+
1031
1041
  /**
1032
1042
  * Configuration object passed to `viji.slider()`. Defines the slider's bounds,
1033
1043
  * increment, label, and UI grouping/visibility.
@@ -1048,7 +1058,7 @@ declare global {
1048
1058
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
1049
1059
  category?: ParameterCategory;
1050
1060
  }
1051
-
1061
+
1052
1062
  /**
1053
1063
  * Reactive object returned by `viji.slider()`. `value` updates in real-time as
1054
1064
  * the user drags the control; the remaining fields mirror the configuration.
@@ -1071,7 +1081,7 @@ declare global {
1071
1081
  /** Visibility category that controls when the parameter is shown. */
1072
1082
  category: ParameterCategory;
1073
1083
  }
1074
-
1084
+
1075
1085
  /**
1076
1086
  * Configuration object passed to `viji.text()`. The host renders a single-line
1077
1087
  * text input field.
@@ -1088,7 +1098,7 @@ declare global {
1088
1098
  /** Maximum character count enforced by the host UI. Default: `1000`. */
1089
1099
  maxLength?: number;
1090
1100
  }
1091
-
1101
+
1092
1102
  /**
1093
1103
  * Reactive object returned by `viji.text()`. `value` updates as the user types
1094
1104
  * and is enforced to be no longer than `maxLength` by the host UI.
@@ -1107,7 +1117,7 @@ declare global {
1107
1117
  /** Visibility category that controls when the parameter is shown. */
1108
1118
  category: ParameterCategory;
1109
1119
  }
1110
-
1120
+
1111
1121
  /**
1112
1122
  * Configuration object passed to `viji.toggle()`. Defines the on/off switch's
1113
1123
  * label and UI grouping/visibility.
@@ -1122,7 +1132,7 @@ declare global {
1122
1132
  /** Visibility category. The control hides when its capability is unavailable. Default: `'general'`. */
1123
1133
  category?: ParameterCategory;
1124
1134
  }
1125
-
1135
+
1126
1136
  /**
1127
1137
  * Reactive object returned by `viji.toggle()`. `value` flips between `true`
1128
1138
  * and `false` as the user interacts with the switch.
@@ -1139,7 +1149,7 @@ declare global {
1139
1149
  /** Visibility category that controls when the parameter is shown. */
1140
1150
  category: ParameterCategory;
1141
1151
  }
1142
-
1152
+
1143
1153
  /**
1144
1154
  * Multi-touch input state. Lists all active touches, plus per-frame
1145
1155
  * `started` / `moved` / `ended` arrays for frame-based gesture handling.
@@ -1161,7 +1171,7 @@ declare global {
1161
1171
  /** Convenience shortcut to `points[0]`, or `null` when no touches are active. */
1162
1172
  primary: TouchPoint | null;
1163
1173
  }
1164
-
1174
+
1165
1175
  /**
1166
1176
  * One active touch contact reported by `TouchAPI`. Position, pressure, radius,
1167
1177
  * rotation, per-frame movement, velocity, and lifecycle flags. Coordinates are
@@ -1210,9 +1220,9 @@ declare global {
1210
1220
  /** `true` for exactly one frame when this touch ends, then auto-resets. */
1211
1221
  isEnding: boolean;
1212
1222
  }
1213
-
1214
- const VERSION = "0.6.0";
1215
-
1223
+
1224
+ const VERSION = "0.7.4";
1225
+
1216
1226
  /**
1217
1227
  * Real-time video API: drawable frame, dimensions, and the source-side
1218
1228
  * properties of the connected stream. All computer-vision data and controls
@@ -1254,7 +1264,7 @@ declare global {
1254
1264
  */
1255
1265
  cv: VideoCVAPI;
1256
1266
  }
1257
-
1267
+
1258
1268
  /**
1259
1269
  * Computer-vision surface attached to a `VideoAPI`. Combines paired CV data
1260
1270
  * outputs (the analysed frame and its detection / segmentation results) with
@@ -1320,35 +1330,73 @@ declare global {
1320
1330
  /** Body segmentation mask, or `null` when segmentation is off. */
1321
1331
  segmentation: SegmentationData | null;
1322
1332
  /**
1323
- * Toggle face detection (bounding box, center, confidence, id).
1333
+ * Toggle face detection. Populates `face.bounds`, `face.center`,
1334
+ * `face.confidence` on each detected face. Independent of face mesh and
1335
+ * emotion detection.
1336
+ *
1337
+ * Safe to call from module scope as well as from inside `render()`. The
1338
+ * verb is idempotent and reference-counted; per-frame calls inside
1339
+ * `render()` (the canonical toggle-gated pattern) are cheap.
1340
+ *
1324
1341
  * @returns Resolves once the underlying pipeline has finished switching.
1325
1342
  */
1326
1343
  enableFaceDetection(enabled: boolean): Promise<void>;
1327
1344
  /**
1328
- * Toggle 468-point face mesh + head pose (`pitch`, `yaw`, `roll`). Implies
1329
- * face detection.
1345
+ * Toggle 468-point face mesh and computed head pose (`pitch`, `yaw`,
1346
+ * `roll`). Populates `face.landmarks` and `face.headPose` on the face
1347
+ * entry. Does NOT populate `face.bounds` / `face.center` / `face.confidence`
1348
+ * — those are produced only by face detection. Enable both if you need
1349
+ * both, or compute bounds from `face.landmarks` min/max yourself.
1350
+ *
1351
+ * Safe to call from module scope as well as from inside `render()`. The
1352
+ * verb is idempotent and reference-counted.
1353
+ *
1330
1354
  * @returns Resolves once the pipeline has finished switching.
1331
1355
  */
1332
1356
  enableFaceMesh(enabled: boolean): Promise<void>;
1333
1357
  /**
1334
1358
  * Toggle 7 expressions + 52 ARKit blendshape coefficients on each face.
1335
- * Implies face mesh.
1359
+ * Populates `face.blendshapes` and `face.expressions`. Internally requires
1360
+ * the face landmarker model (loads it alongside `enableFaceMesh` if both
1361
+ * are enabled; loads it on its own otherwise so `face.landmarks` and
1362
+ * `face.headPose` are also populated). Does NOT populate
1363
+ * `face.bounds` / `face.center` / `face.confidence` — enable face
1364
+ * detection for those.
1365
+ *
1366
+ * Safe to call from module scope as well as from inside `render()`. The
1367
+ * verb is idempotent and reference-counted.
1368
+ *
1336
1369
  * @returns Resolves once the pipeline has finished switching.
1337
1370
  */
1338
1371
  enableEmotionDetection(enabled: boolean): Promise<void>;
1339
1372
  /**
1340
- * Toggle 21-point hand landmarks + ML-classified gesture confidences for up
1341
- * to two hands.
1373
+ * Toggle 21-point hand landmarks and ML-classified gesture confidences
1374
+ * for up to two hands. Populates `viji.video.cv.hands[]` with `landmarks`,
1375
+ * `palm`, `bounds` (computed from landmarks), and `gestures`.
1376
+ *
1377
+ * Safe to call from module scope as well as from inside `render()`. The
1378
+ * verb is idempotent and reference-counted.
1379
+ *
1342
1380
  * @returns Resolves once the pipeline has finished switching.
1343
1381
  */
1344
1382
  enableHandTracking(enabled: boolean): Promise<void>;
1345
1383
  /**
1346
1384
  * Toggle 33-point BlazePose body landmarks (face / torso / arms / legs).
1385
+ * Populates `viji.video.cv.pose`.
1386
+ *
1387
+ * Safe to call from module scope as well as from inside `render()`. The
1388
+ * verb is idempotent and reference-counted.
1389
+ *
1347
1390
  * @returns Resolves once the pipeline has finished switching.
1348
1391
  */
1349
1392
  enablePoseDetection(enabled: boolean): Promise<void>;
1350
1393
  /**
1351
- * Toggle per-pixel person/background segmentation mask.
1394
+ * Toggle per-pixel person/background segmentation mask. Populates
1395
+ * `viji.video.cv.segmentation`.
1396
+ *
1397
+ * Safe to call from module scope as well as from inside `render()`. The
1398
+ * verb is idempotent and reference-counted.
1399
+ *
1352
1400
  * @returns Resolves once the pipeline has finished switching.
1353
1401
  */
1354
1402
  enableBodySegmentation(enabled: boolean): Promise<void>;
@@ -1357,7 +1405,7 @@ declare global {
1357
1405
  /** Returns `true` if the CV worker is actively processing frames. */
1358
1406
  isProcessing(): boolean;
1359
1407
  }
1360
-
1408
+
1361
1409
  /**
1362
1410
  * The Viji API surface available to artist code as the global `viji` object.
1363
1411
  * Groups canvas access, timing, connected media APIs, user interaction, device
@@ -1511,14 +1559,14 @@ declare global {
1511
1559
  */
1512
1560
  useContext(type: 'webgl2'): WebGL2RenderingContext;
1513
1561
  }
1514
-
1515
- // Runtime global - the main viji object
1516
- const viji: VijiAPI;
1517
-
1518
- // Function type aliases (artists define their own render/setup functions)
1519
- type Render = (viji: VijiAPI) => void;
1520
- type Setup = (viji: VijiAPI) => void;
1521
- }
1522
-
1523
- // Module marker (enables top-level await in artist code)
1524
- export {};
1562
+
1563
+ // Runtime global - the main viji object
1564
+ const viji: VijiAPI;
1565
+
1566
+ // Function type aliases (artists define their own render/setup functions)
1567
+ type Render = (viji: VijiAPI) => void;
1568
+ type Setup = (viji: VijiAPI) => void;
1569
+ }
1570
+
1571
+ // Module marker (enables top-level await in artist code)
1572
+ export {};
@@ -362,23 +362,27 @@
362
362
  */
363
363
 
364
364
  /**
365
- * One detected face produced by the video CV pipeline. Always carries `id`, `bounds`, `center`, and `confidence`; the rest is populated only when the matching CV feature is enabled (face mesh for `landmarks` and `headPose`, emotion detection for `expressions` and `blendshapes`).
365
+ * One detected face produced by the video CV pipeline. Each field is populated only by its source model; fields whose source model is not enabled read as `null` so the absence is loud rather than silent. Enable the relevant `enable*` verb on `viji.video.cv` to populate each group: - `bounds`, `center`, `confidence`: populated by `enableFaceDetection(true)`. - `landmarks`: populated by `enableFaceMesh(true)` (empty array otherwise). - `headPose`: populated by `enableFaceMesh(true)`. - `blendshapes`, `expressions`: populated by `enableEmotionDetection(true)`. If a field is `null`, call the corresponding `enable*` verb on `viji.video.cv` to populate it. Each active CV feature consumes its own WebGL context for MediaPipe; enabling many at once on lower-end hardware can hit context limits.
366
366
  * @typedef {Object} FaceData
367
367
  * @property {number} id - Index-based face identifier (`0`, `1`, …). Stable within a single frame, may change between frames.
368
- * @property {Object} bounds - Bounding box, all components normalized to 0..1 of the source frame.
368
+ * @property {Object} bounds - Bounding box, all components normalized to 0..1 of the source frame. `null` unless face detection is enabled.
369
369
  * @property {number} bounds.x - Left edge of the face bounding box, normalized 0..1.
370
370
  * @property {number} bounds.y - Top edge of the face bounding box, normalized 0..1.
371
371
  * @property {number} bounds.width - Width of the face bounding box, normalized 0..1.
372
372
  * @property {number} bounds.height - Height of the face bounding box, normalized 0..1.
373
- * @property {Object} center - Center of the bounding box, normalized 0..1.
373
+ * @property {Object} center - Center of the bounding box, normalized 0..1. `null` unless face detection is enabled.
374
374
  * @property {number} center.x - Horizontal center of the face, normalized 0..1.
375
375
  * @property {number} center.y - Vertical center of the face, normalized 0..1.
376
- * @property {number} confidence - Detection confidence in 0..1.
376
+ * @property {number |null} confidence - Detection confidence in 0..1. `null` unless face detection is enabled.
377
377
  * @property {Object} landmarks - 468 normalized face mesh landmarks when face mesh is enabled; empty array otherwise.
378
378
  * @property {number} landmarks.x - Horizontal landmark coordinate, normalized 0..1.
379
379
  * @property {number} landmarks.y - Vertical landmark coordinate, normalized 0..1.
380
380
  * @property {number} landmarks.z - Optional depth (relative).
381
- * @property {Object} expressions - Seven expression confidence scores in 0..1. All zero unless emotion detection is enabled.
381
+ * @property {Object} headPose - Estimated head rotation in degrees. `null` unless face mesh is enabled.
382
+ * @property {number} headPose.pitch - Up/down rotation in degrees (-90..90). Positive = head looking up, negative = looking down.
383
+ * @property {number} headPose.yaw - Left/right rotation in degrees (-90..90). Positive = head turned to the subject's right, negative = to the left.
384
+ * @property {number} headPose.roll - Tilt rotation in degrees (-180..180). Positive = head tilted to the subject's right (right ear toward right shoulder).
385
+ * @property {Object} expressions - Seven expression confidence scores in 0..1. `null` unless emotion detection is enabled.
382
386
  * @property {number} expressions.neutral - Confidence (0..1) of a neutral expression.
383
387
  * @property {number} expressions.happy - Confidence (0..1) of a happy / smiling expression.
384
388
  * @property {number} expressions.sad - Confidence (0..1) of a sad expression.
@@ -386,11 +390,7 @@
386
390
  * @property {number} expressions.surprised - Confidence (0..1) of a surprised expression.
387
391
  * @property {number} expressions.disgusted - Confidence (0..1) of a disgusted expression.
388
392
  * @property {number} expressions.fearful - Confidence (0..1) of a fearful expression.
389
- * @property {Object} headPose - Estimated head rotation. All zero unless face mesh is enabled.
390
- * @property {number} headPose.pitch - Up/down rotation in degrees (-90..90). Positive = head looking up, negative = looking down.
391
- * @property {number} headPose.yaw - Left/right rotation in degrees (-90..90). Positive = head turned to the subject's right, negative = to the left.
392
- * @property {number} headPose.roll - Tilt rotation in degrees (-180..180). Positive = head tilted to the subject's right (right ear toward right shoulder).
393
- * @property {FaceBlendshapes} blendshapes - 52 ARKit-compatible facial muscle coefficients. All zero unless emotion detection is enabled.
393
+ * @property {FaceBlendshapes |null} blendshapes - 52 ARKit-compatible facial muscle coefficients. `null` unless emotion detection is enabled.
394
394
  */
395
395
 
396
396
  /**