@vincentt-xr/sdk 2.0.0-alpha.6 → 2.0.0-alpha.8

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 (287) hide show
  1. package/GROUNDING.md +1188 -14
  2. package/dist/{DebuggerUI-C_rbLdkh.js → DebuggerUI-C-ldiozL.js} +1 -1
  3. package/dist/{DebuggerUI-DYT_dk1E.cjs → DebuggerUI-CWX5EtQv.cjs} +1 -1
  4. package/dist/FaceMeshRenderer-BEEXZwS6.cjs +158 -0
  5. package/dist/FaceMeshRenderer-UuH5sTXG.js +1322 -0
  6. package/dist/FaceRetouch-C7h7x9kd.js +683 -0
  7. package/dist/FaceRetouch-DqftT26Y.cjs +134 -0
  8. package/dist/GLTFLoader-C5q63sKO.js +1633 -0
  9. package/dist/GLTFLoader-Cb_CRwmG.cjs +1 -0
  10. package/dist/SegmentationOverlay-DQuW3dsc.js +133 -0
  11. package/dist/SegmentationOverlay-aWVod8Kl.cjs +1 -0
  12. package/dist/assets/cache.d.ts +99 -0
  13. package/dist/assets/estimate.d.ts +11 -0
  14. package/dist/assets/index.d.ts +7 -0
  15. package/dist/assets/loaders.d.ts +28 -0
  16. package/dist/assets/react.d.ts +33 -0
  17. package/dist/calibration/calibration.d.ts +70 -0
  18. package/dist/calibration/index.d.ts +6 -0
  19. package/dist/calibration/storage.d.ts +13 -0
  20. package/dist/calibration/types.d.ts +53 -0
  21. package/dist/calibration/useXRCalibration.d.ts +46 -0
  22. package/dist/camera/CameraPermissionGate.d.ts +26 -0
  23. package/dist/camera/devices.d.ts +26 -0
  24. package/dist/camera/index.d.ts +8 -0
  25. package/dist/camera/permission.d.ts +86 -0
  26. package/dist/camera/useCameraPermission.d.ts +35 -0
  27. package/dist/capabilities/detect.d.ts +45 -0
  28. package/dist/capabilities/index.d.ts +5 -0
  29. package/dist/capabilities/types.d.ts +120 -0
  30. package/dist/capabilities/useXRCapabilities.d.ts +18 -0
  31. package/dist/compat/environment.d.ts +5 -0
  32. package/dist/compat/index.d.ts +4 -0
  33. package/dist/compat/run.d.ts +18 -0
  34. package/dist/compat/scenarios.d.ts +10 -0
  35. package/dist/compat/types.d.ts +101 -0
  36. package/dist/components/BodyTracker/index.d.ts +9 -0
  37. package/dist/components/FaceMesh/FaceMeshRenderer.d.ts +11 -3
  38. package/dist/components/FaceMesh/canonical/CanonicalFaceMeshRenderer.d.ts +41 -5
  39. package/dist/components/FaceMesh/canonical/parseCanonicalFaceModel.d.ts +12 -0
  40. package/dist/components/FaceMesh/canonical/shaders.d.ts +1 -1
  41. package/dist/components/FaceMesh/index.d.ts +2 -2
  42. package/dist/components/GestureListener/index.d.ts +11 -0
  43. package/dist/components/HandTracker/index.d.ts +10 -0
  44. package/dist/components/HolisticTracker/index.d.ts +69 -0
  45. package/dist/components/HolisticTracker/settings.d.ts +58 -0
  46. package/dist/components/HolisticTracker/signals.d.ts +49 -0
  47. package/dist/components/IrisEffect/IrisMaterial.d.ts +73 -0
  48. package/dist/components/IrisEffect/index.d.ts +51 -0
  49. package/dist/components/IrisEffect/settings.d.ts +117 -0
  50. package/dist/components/LipEffect/LipEffect.d.ts +7 -1
  51. package/dist/components/LipEffect/LipEffectMaterial.d.ts +43 -7
  52. package/dist/components/LipEffect/LipEffects.d.ts +9 -0
  53. package/dist/components/LipEffect/defaults.d.ts +27 -4
  54. package/dist/components/LipEffect/index.d.ts +11 -3
  55. package/dist/components/LipEffect/settings.d.ts +271 -0
  56. package/dist/components/LipEffect/types.d.ts +56 -9
  57. package/dist/components/LipEffect/useLipDesignTextures.d.ts +11 -0
  58. package/dist/components/LipEffect/uvGuide.d.ts +59 -0
  59. package/dist/components/PersonCutout/index.d.ts +50 -0
  60. package/dist/components/PersonCutout/personCutout.d.ts +16 -0
  61. package/dist/components/SceneObjectRenderer/index.d.ts +9 -2
  62. package/dist/components/ScreenImage/components/ScreenImageBody.d.ts +1 -1
  63. package/dist/components/ScreenImage/index.d.ts +3 -1
  64. package/dist/components/ScreenImage/materials/ScreenImageBlendMaterial.d.ts +1 -1
  65. package/dist/components/ScreenImage/materials/ScreenImageMaskMaterial.d.ts +11 -0
  66. package/dist/components/ScreenImage/source.d.ts +6 -0
  67. package/dist/components/ScreenImage/types.d.ts +18 -0
  68. package/dist/components/SegmentationOverlay/SegmentationOverlay.d.ts +37 -0
  69. package/dist/components/SegmentationOverlay/SegmentationOverlayMaterial.d.ts +31 -0
  70. package/dist/components/SegmentationOverlay/index.d.ts +9 -0
  71. package/dist/components/SegmentationOverlay/maskTexture.d.ts +23 -0
  72. package/dist/components/SegmentationOverlay/providers.d.ts +72 -0
  73. package/dist/components/SegmentationOverlay/settings.d.ts +65 -0
  74. package/dist/components/XRMediaSurface/index.d.ts +2 -1
  75. package/dist/components/XRProvider/index.d.ts +16 -2
  76. package/dist/components/XRViewport/hooks/useViewportExternalFaces.d.ts +1 -1
  77. package/dist/components/XRViewport/hooks/useViewportMediaSource.d.ts +1 -1
  78. package/dist/components/XRViewport/hooks/useViewportPipeline.d.ts +1 -1
  79. package/dist/components/index.d.ts +3 -0
  80. package/dist/constants/index.d.ts +4 -1
  81. package/dist/contexts-CO0sd4JQ.cjs +1 -0
  82. package/dist/{contexts-DvQI4PqI.js → contexts-FzlnnMYD.js} +252 -246
  83. package/dist/coordinates/feed.d.ts +52 -0
  84. package/dist/coordinates/index.d.ts +5 -0
  85. package/dist/coordinates/spaces.d.ts +49 -0
  86. package/dist/coordinates/viewport.d.ts +22 -0
  87. package/dist/coordinates/world.d.ts +15 -0
  88. package/dist/debug-ui/gesture-settings/index.d.ts +14 -0
  89. package/dist/debug-ui/holistic-tracker-settings/index.d.ts +16 -0
  90. package/dist/debug-ui/index.d.ts +14 -0
  91. package/dist/debug-ui/iris-effect-settings/index.d.ts +12 -0
  92. package/dist/debug-ui/lip-effect-settings/index.d.ts +15 -0
  93. package/dist/debug-ui/media-source.cjs +1 -1
  94. package/dist/debug-ui/media-source.js +1 -1
  95. package/dist/debug-ui/product-hotspots/index.d.ts +24 -0
  96. package/dist/debug-ui/segmentation-overlay-settings/index.d.ts +12 -0
  97. package/dist/debug-ui/xr-diagnostics/index.d.ts +25 -0
  98. package/dist/debug-ui.cjs +3 -3
  99. package/dist/debug-ui.js +2057 -1687
  100. package/dist/detector-K8aej4MB.js +59 -0
  101. package/dist/detector-ijypGtse.cjs +1 -0
  102. package/dist/entries/core.d.ts +21 -3
  103. package/dist/entries/face-effects.d.ts +12 -4
  104. package/dist/entries/low-level.d.ts +22 -3
  105. package/dist/entries/product.d.ts +11 -0
  106. package/dist/entries/tracking.d.ts +26 -1
  107. package/dist/face-effects.cjs +155 -1
  108. package/dist/face-effects.js +1134 -2196
  109. package/dist/gestures/definitions.d.ts +85 -0
  110. package/dist/gestures/detector.d.ts +87 -0
  111. package/dist/gestures/index.d.ts +8 -0
  112. package/dist/gestures/measures.d.ts +101 -0
  113. package/dist/gestures/settings.d.ts +49 -0
  114. package/dist/{getFaceInfo-B43Cwq4n.js → getFaceInfo-BkZfaQs-.js} +5 -5
  115. package/dist/{getFaceInfo-DEzcL_Ub.cjs → getFaceInfo-ahPxQFGu.cjs} +1 -1
  116. package/dist/hooks/selectors.d.ts +68 -1
  117. package/dist/hooks/useCalibrationFaceTracker.d.ts +1 -1
  118. package/dist/hooks/useFaceInfo.d.ts +13 -1
  119. package/dist/hooks/useFaceInfos.d.ts +1 -1
  120. package/dist/hooks/useGesture.d.ts +32 -0
  121. package/dist/hooks/useIrisTracking.d.ts +45 -0
  122. package/dist/hooks/useXRDiagnostics.d.ts +81 -0
  123. package/dist/hooks/useXRModel.d.ts +7 -0
  124. package/dist/hooks/useXRSession/facade.d.ts +44 -2
  125. package/dist/hooks/useXRSession/index.d.ts +2 -2
  126. package/dist/hooks/useXRSession/reason.d.ts +3 -1
  127. package/dist/hooks/useXRSession/registry.d.ts +14 -0
  128. package/dist/hooks/useXRSession/store.d.ts +52 -3
  129. package/dist/index--RgGzZ6X.cjs +47 -0
  130. package/dist/{index-uE3sKczj.js → index-CHVbH0hz.js} +4 -4
  131. package/dist/index-CeVkgsag.cjs +1 -0
  132. package/dist/index-CnGox6Xr.js +3054 -0
  133. package/dist/{index-B-s4YbWu.js → index-CsUdQ-pn.js} +113 -111
  134. package/dist/{index-CY7Z8cHV.cjs → index-DKBAh6hV.cjs} +1 -1
  135. package/dist/index-DsZwTfG0.cjs +1 -0
  136. package/dist/index-Dw6tj2Mi.js +144 -0
  137. package/dist/index-OCPjOaeP.cjs +1 -0
  138. package/dist/{index-G_5h_FJQ.js → index-s9pF4zkG.js} +999 -1014
  139. package/dist/internal-BYza1h8_.cjs +1 -0
  140. package/dist/{internal-ldPYiUF0.js → internal-vsfNQycj.js} +15 -16
  141. package/dist/internal.cjs +1 -1
  142. package/dist/internal.js +2 -2
  143. package/dist/iris/compute.d.ts +25 -0
  144. package/dist/iris/index.d.ts +4 -0
  145. package/dist/iris/lead.d.ts +19 -0
  146. package/dist/iris/types.d.ts +87 -0
  147. package/dist/landmarkMask-CbTFzBYG.js +224 -0
  148. package/dist/landmarkMask-u7es2k4X.cjs +1 -0
  149. package/dist/low-level.cjs +1 -1
  150. package/dist/low-level.js +1104 -639
  151. package/dist/main.cjs +1 -1
  152. package/dist/main.js +1519 -462
  153. package/dist/measures-9dQPdw96.js +154 -0
  154. package/dist/measures-KfJzpvod.cjs +1 -0
  155. package/dist/models/Base/index.d.ts +49 -0
  156. package/dist/models/Body/index.d.ts +1 -1
  157. package/dist/models/Face/index.d.ts +1 -1
  158. package/dist/models/Hand/GestureTracking.d.ts +1 -1
  159. package/dist/models/Hand/HandTracking.d.ts +1 -1
  160. package/dist/models/Holistic/index.d.ts +49 -0
  161. package/dist/models/Holistic/result.d.ts +68 -0
  162. package/dist/models/SemanticSegmentation/index.d.ts +31 -0
  163. package/dist/models/SemanticSegmentation/presets.d.ts +28 -0
  164. package/dist/models/index.d.ts +2 -0
  165. package/dist/product/ProductDimensions.d.ts +16 -0
  166. package/dist/product/ProductHotspot.d.ts +16 -0
  167. package/dist/product/ProductViewer.d.ts +9 -0
  168. package/dist/product/details.d.ts +53 -0
  169. package/dist/product/index.d.ts +14 -0
  170. package/dist/product/loadModelViewer.d.ts +4 -0
  171. package/dist/product/types.d.ts +71 -0
  172. package/dist/product/useProductAr.d.ts +37 -0
  173. package/dist/product/useProductDetails.d.ts +60 -0
  174. package/dist/product.cjs +1 -0
  175. package/dist/product.js +414 -0
  176. package/dist/providers-C0Dlhh9Z.js +365 -0
  177. package/dist/providers-YNwkmNeK.cjs +62 -0
  178. package/dist/quality/XRQualityController.d.ts +25 -0
  179. package/dist/quality/controller.d.ts +33 -0
  180. package/dist/quality/index.d.ts +6 -0
  181. package/dist/quality/types.d.ts +100 -0
  182. package/dist/quality/useXRQuality.d.ts +28 -0
  183. package/dist/{renderGroups-CFt1yYLB.js → renderGroups-CN1cGFUo.js} +1 -1
  184. package/dist/{renderGroups-BJsx32q5.cjs → renderGroups-O4KmaMl9.cjs} +1 -1
  185. package/dist/renderPasses-Cg-zqmEz.cjs +21 -0
  186. package/dist/renderPasses-NnKZVfBy.js +1460 -0
  187. package/dist/scene-object.cjs +1 -1
  188. package/dist/scene-object.js +2 -2
  189. package/dist/screen2d/coordinates.d.ts +20 -0
  190. package/dist/screenTransform-BenfCtZt.js +44 -0
  191. package/dist/screenTransform-BiXdpVf0.cjs +1 -0
  192. package/dist/segmentation-9PhSvc-R.cjs +1 -0
  193. package/dist/segmentation-CeOTKemr.js +5 -0
  194. package/dist/segmentation2d/faceMemory.d.ts +28 -0
  195. package/dist/segmentation2d/faceRegions.d.ts +24 -0
  196. package/dist/segmentation2d/index.d.ts +9 -0
  197. package/dist/segmentation2d/maskUv.d.ts +23 -0
  198. package/dist/segmentation2d/rasterize.d.ts +14 -0
  199. package/dist/segmentation2d/types.d.ts +46 -0
  200. package/dist/selectors-C5G1PMYL.js +195 -0
  201. package/dist/selectors-Du_PsVnW.cjs +1 -0
  202. package/dist/{session-CANvoLD6.cjs → session-BBr0yN8h.cjs} +7 -7
  203. package/dist/{session-Dhi0OqZo.js → session-Cy0m7l7-.js} +76 -76
  204. package/dist/settings-DRR7-snV.cjs +1 -0
  205. package/dist/settings-aQk845NJ.js +73 -0
  206. package/dist/subjects/index.d.ts +2 -0
  207. package/dist/subjects/select.d.ts +62 -0
  208. package/dist/subjects/types.d.ts +55 -0
  209. package/dist/tracking/coordinates.d.ts +5 -0
  210. package/dist/tracking-config/apply.d.ts +57 -0
  211. package/dist/tracking-config/index.d.ts +7 -0
  212. package/dist/tracking-config/resolve.d.ts +25 -0
  213. package/dist/tracking-config/storage.d.ts +8 -0
  214. package/dist/tracking-config/types.d.ts +106 -0
  215. package/dist/tracking-config/useXRTrackingConfig.d.ts +38 -0
  216. package/dist/tracking.cjs +1 -1
  217. package/dist/tracking.js +1056 -380
  218. package/dist/types/common.d.ts +11 -1
  219. package/dist/types/compositor.d.ts +21 -0
  220. package/dist/types/gesture-tracking.d.ts +2 -2
  221. package/dist/types/hand-tracking.d.ts +3 -3
  222. package/dist/types/holistic.d.ts +121 -0
  223. package/dist/types/index.d.ts +1 -0
  224. package/dist/types/model.d.ts +22 -2
  225. package/dist/types/nodes.d.ts +29 -4
  226. package/dist/types/segmentation.d.ts +19 -0
  227. package/dist/types/session.d.ts +48 -4
  228. package/dist/useFaceInfo-Blf7FCu3.js +303 -0
  229. package/dist/useFaceInfo-C5mO8NQY.cjs +1 -0
  230. package/dist/useXRCapabilities-B1R2ghy-.cjs +1 -0
  231. package/dist/useXRCapabilities-C68v__wB.js +30 -0
  232. package/dist/utils/fns.d.ts +6 -3
  233. package/dist/utils/integrity.d.ts +4 -1
  234. package/dist/utils/updateThrottle.d.ts +2 -0
  235. package/dist/utils/useStableUniforms.d.ts +18 -0
  236. package/docs/SDK_DOCUMENTATION.md +3 -0
  237. package/docs/api/adaptive-quality.mdx +104 -0
  238. package/docs/api/asset-lifecycle.mdx +78 -0
  239. package/docs/api/camera-permission.mdx +142 -0
  240. package/docs/api/capture-recording.mdx +9 -1
  241. package/docs/api/coordinate-spaces.mdx +165 -0
  242. package/docs/api/cross-camera.mdx +88 -0
  243. package/docs/api/diagnostics-calibration.mdx +97 -0
  244. package/docs/api/face-effects.mdx +6 -0
  245. package/docs/api/face-mesh.mdx +35 -0
  246. package/docs/api/gestures.mdx +173 -0
  247. package/docs/api/holistic-tracker.mdx +159 -0
  248. package/docs/api/iris-tracking.mdx +242 -0
  249. package/docs/api/multi-subject.mdx +95 -0
  250. package/docs/api/person-cutout.mdx +136 -0
  251. package/docs/api/product-ar.mdx +111 -0
  252. package/docs/api/product-details.mdx +78 -0
  253. package/docs/api/runtime-3d.mdx +5 -0
  254. package/docs/api/screen-image.mdx +3 -1
  255. package/docs/api/screen-space-coordinates.mdx +35 -1
  256. package/docs/api/segmentation-overlay.mdx +301 -0
  257. package/docs/api/session-lifecycle.mdx +136 -0
  258. package/docs/api/trackers.mdx +18 -0
  259. package/docs/api/tracking-config.mdx +108 -0
  260. package/docs/docs.json +23 -1
  261. package/docs/examples/lip-shaders.mdx +224 -0
  262. package/docs/guides/lip-effect-textures.mdx +292 -0
  263. package/docs/guides/lip-uv-template.png +0 -0
  264. package/docs/guides/lip-uv-template.svg +1 -0
  265. package/package.json +16 -1
  266. package/dist/FaceMeshRenderer-D1WsrTLA.cjs +0 -155
  267. package/dist/FaceMeshRenderer-DSCd1H55.js +0 -1264
  268. package/dist/FaceRetouch-CMJbGThL.js +0 -551
  269. package/dist/FaceRetouch-DsE0Dbw-.cjs +0 -134
  270. package/dist/contexts-h-9TRH0r.cjs +0 -1
  271. package/dist/index-C0-fg1Hk.cjs +0 -1
  272. package/dist/index-CxMEeCRP.cjs +0 -1
  273. package/dist/index-D67hHm44.cjs +0 -1
  274. package/dist/index-DPPpU5By.cjs +0 -17
  275. package/dist/index-DYfY9ljy.js +0 -111
  276. package/dist/index-I4eF5TKT.js +0 -1457
  277. package/dist/internal-MvKrz0Nt.cjs +0 -1
  278. package/dist/renderLayers-lDtL0_43.cjs +0 -1
  279. package/dist/renderLayers-zrK2pv5O.js +0 -13
  280. package/dist/renderPasses-C72MbZ-a.js +0 -1500
  281. package/dist/renderPasses-D-Krl6D0.cjs +0 -21
  282. package/dist/selectors-ByKNTXeA.js +0 -59
  283. package/dist/selectors-CAwmL9VW.cjs +0 -1
  284. package/dist/useFaceInfo-BSZq7AO1.cjs +0 -1
  285. package/dist/useFaceInfo-FVQBBNdY.js +0 -62
  286. package/dist/useFaceResults-C3oZkn2Z.cjs +0 -1
  287. package/dist/useFaceResults-D62STzT1.js +0 -19
@@ -0,0 +1,301 @@
1
+ ---
2
+ title: "Segmentation Overlay (2D)"
3
+ description: "Draw semantic segmentation regions (lips, eyes, brows, face skin, mouth, person, background, or any label an external model emits) as tinted 2D layers over the camera, without canonical 3D face geometry."
4
+ sidebarTitle: "Segmentation Overlay"
5
+ icon: "layer-group"
6
+ ---
7
+
8
+ `SegmentationOverlay` renders one or more regions of a **semantic mask** as a
9
+ screen-aligned 2D layer composited over the camera feed. Masks come from the
10
+ SDK's own providers or from any external segmentation model (face parsing,
11
+ matting), as plain data.
12
+
13
+ Import doors: the component, both providers, settings and the `SemanticMask`
14
+ type are in `@vincentt-xr/sdk/face-effects`; `SegmentationOverlay` and
15
+ `usePersonMask` are also in `@vincentt-xr/sdk/tracking` for person /
16
+ background work; the material object API, shaders, mask textures,
17
+ rasterizer primitives and `computeMaskUvTransform` are
18
+ `@vincentt-xr/sdk/low-level`.
19
+
20
+ ```tsx
21
+ import {
22
+ SegmentationOverlay,
23
+ useFaceRegionMask,
24
+ usePersonMask,
25
+ } from "@vincentt-xr/sdk/face-effects";
26
+
27
+ const Lips = () => {
28
+ const faces = useFaceRegionMask(); // lips, mouth, eyes, brows, face_skin
29
+ return <SegmentationOverlay mask={faces} label="lips" color="#c0304f" blendMode="multiply" />;
30
+ };
31
+
32
+ const DimBackground = () => {
33
+ const person = usePersonMask(); // bg, person
34
+ return (
35
+ <SegmentationOverlay
36
+ mask={person}
37
+ label="bg"
38
+ color="#000000"
39
+ opacity={0.6}
40
+ blendMode="normal"
41
+ />
42
+ );
43
+ };
44
+ ```
45
+
46
+ ## When to use 2D segmentation versus canonical 3D geometry
47
+
48
+ | Need | Use |
49
+ | -------------------------------------------------------------------------------------------------------- | ------------------------------------- |
50
+ | A region tint, cut-out, or image stamped **in screen space** | `SegmentationOverlay` (this page) |
51
+ | Regions with **no landmarks**: background and person (built in); hair, beard, ears with an external mask | `SegmentationOverlay` + a mask source |
52
+ | Lipstick with highlights, gradients, UV-mapped designs that **follow the lip surface** | `LipEffect` (3D, canonical UVs) |
53
+ | Textures that must stay attached under head rotation, depth, occlusion | `FaceMeshRenderer` / `LipEffect` |
54
+
55
+ The 2D path has no UV atlas and no per-vertex normals: a design texture is
56
+ sampled in **screen** space (it does not rotate with the head), and lighting
57
+ comes only from the camera pixels via the blend mode. It is cheaper (one
58
+ full-screen quad, one small texture) and works for any label a mask carries.
59
+ The 3D path needs the canonical mesh and landmark depth but gives stable
60
+ texture mapping and real shading. Both can run at once.
61
+
62
+ ## The mask data model
63
+
64
+ ```ts
65
+ type SemanticMask = {
66
+ width: number;
67
+ height: number;
68
+ indices: Uint8Array; // one class index per pixel, row 0 = top
69
+ labels: readonly string[]; // labels[index]; index 0 is conventionally "bg"
70
+ space?: "feed" | "source";
71
+ version: number; // bump on every change; consumers upload lazily
72
+ };
73
+ ```
74
+
75
+ Any model can feed this: write its per-pixel class ids into `indices` and
76
+ name them in `labels`. `SEMANTIC_MASK_LABELS` lists what the SDK's own
77
+ providers produce (`bg`, `brows`, `eyes`, `face_skin`, `lips`, `mouth`,
78
+ `person`); any other string works, so a face-parsing model's `beard`, `ears`
79
+ or `hair` render the same way. The SDK does not ship a model for those labels:
80
+ landmarks carry no ear points and cannot tell whether facial hair is present,
81
+ and the portrait segmenter only separates person from background. Helpers: `createSemanticMask`, `clearSemanticMask`, `fillPolygon`
82
+ (normalized points, y down), `fillLandmarkPolygon`, `semanticMaskLabelIndex`.
83
+ The mask object is meant to be mutated in place; bump `version` after writing.
84
+
85
+ ## Built-in providers
86
+
87
+ - `useFaceRegionMask({ faceIndex = "all", regions, resolution = 256, maxUpdateFps, holdMs = 250, predictMs = 0 })`
88
+ rasterizes the face tracker's landmarks into `face_skin`, `brows`, `eyes`,
89
+ `lips` and `mouth` every frame (`FACE_REGION_POLYGONS` lists the landmark
90
+ loops; `rasterizeFaceRegions` is the pure rasterizer). `faceIndex: "all"`
91
+ stacks every tracked face into one mask; a number isolates one face.
92
+ Small or fast faces make the tracker drop out for a frame or two at a
93
+ time; `holdMs` keeps the last mask alive across such gaps (the same bridge
94
+ `useFaceInfos` applies), and `predictMs` moves the held landmarks along
95
+ their last smoothed velocity for up to that long so the mask keeps pace
96
+ with a moving subject instead of lagging behind it. Set `holdMs: 0` to
97
+ show raw tracker state. The bridge is `FaceTrackMemory`, usable with any
98
+ landmark set.
99
+ - `usePersonMask({ maxUpdateFps = 15 })` exposes the portrait segmentation as
100
+ `bg` / `person`. It acquires the segmentation model itself. Each update is
101
+ a GPU→CPU readback, hence the default cap. The model downloads on first
102
+ use, so the mask stays at `version` 0 for a few seconds; an overlay draws
103
+ nothing until the first real mask arrives. The selfie segmenter is a
104
+ person-class model: every person in frame is `person`, so multi-person
105
+ frames work without configuration.
106
+ - For `bg` and `person` prefer `blendMode="normal"` (a flat tint) or
107
+ `screen`; `multiply` darkens, which is right for lips on skin but nearly
108
+ invisible on a dark background.
109
+ - `useHairMask({ modelUrl, quality, active })` runs MediaPipe's **Hair
110
+ Segmenter** and returns `{ mask, status, error, labels }` with labels
111
+ `bg` / `hair`. See [Hair segmentation](#hair-segmentation) below.
112
+ - `useSemanticSegmentationMask({ preset, modelUrl, quality, active })` is
113
+ the generic form behind it; `preset: "multiclass"` runs the Selfie
114
+ Multiclass model (`bg`, `hair`, `body_skin`, `face_skin`, `clothes`,
115
+ `others`). One preset per session: the first acquirer decides.
116
+
117
+ ## Hair segmentation
118
+
119
+ Hair is a dedicated region with its own model, because neither landmarks
120
+ (no hair points) nor the portrait segmenter (person as one class) can
121
+ produce it.
122
+
123
+ **Model and runtime contract.** `XRModel.SEMANTIC_SEGMENTATION` runs a
124
+ MediaPipe `ImageSegmenter` in `VIDEO` mode with `outputCategoryMask` on the
125
+ GPU delegate. The `hair` preset is MediaPipe's Hair Segmenter
126
+ (`hair_segmenter.tflite`, 512×512 input, classes `background` / `hair`). Its
127
+ category mask carries the class index per pixel (0 background, 1 hair; the
128
+ multiclass model likewise emits 0–5; both measured against the live models); the SDK's portrait selfie segmenter is
129
+ different and emits 255 / 0, which `usePersonMask` normalises separately.
130
+ By default the model loads from MediaPipe's published file, whose bytes are
131
+ pinned in the SDK's integrity manifest, so the default is verified: a
132
+ republished file fails loudly with an SRI mismatch instead of silently
133
+ changing behaviour. The same hash is pinned under the SDK CDN path
134
+ `/assets/xr/models/hair_segmenter.tflite`; once that file is uploaded, pass
135
+ it as `modelUrl` (or the SDK will flip the default). `useHairMask()` with no
136
+ options works:
137
+
138
+ ```tsx
139
+ import { SegmentationOverlay, useHairMask } from "@vincentt-xr/sdk/face-effects";
140
+
141
+ const HairTint = () => {
142
+ const hair = useHairMask();
143
+ if (hair.status === "error") console.warn(hair.error);
144
+ return (
145
+ <SegmentationOverlay
146
+ mask={hair.mask}
147
+ label="hair"
148
+ color="#7c3aed"
149
+ blendMode="overlay"
150
+ feather={2}
151
+ />
152
+ );
153
+ };
154
+ ```
155
+
156
+ **Lifecycle.** `status` is `loading` while the WASM and model download and
157
+ the segmenter initialises, `ready` once inference runs, `error` if the asset
158
+ fails verification or the segmenter throws (`error` carries the reason). An
159
+ inference error is transient: the next successful frame returns the status
160
+ to `ready`, so consumers always see the current state. `labels` is the
161
+ preset's label list from the first render, before any data.
162
+ The mask stays at `version` 0 until the first inference, so the overlay
163
+ draws nothing during loading. The model is refcounted like every tracker:
164
+ mount it once above scene switches, pause it with `active: false`, and it
165
+ is disposed when the last consumer unmounts.
166
+
167
+ **Coordinates and resolution.** The feed is stretched into a process canvas
168
+ at `quality` × feed size (low 0.5, medium 0.75, high 1), so the mask is in
169
+ feed space with the feed's aspect ratio and mirroring, exactly like the
170
+ other providers; the overlay needs no coordinate setup. The mask is
171
+ nearest-filtered at the process resolution; use `feather` for soft hair
172
+ edges and `quality: "high"` when flyaway strands matter.
173
+
174
+ **Performance.** Inference runs at the model's `targetFps` (15 by default,
175
+ overridable through the session `perf` / pipeline options). Each inference
176
+ reads the category mask back to the CPU, which is why the rate is capped;
177
+ the hair model is heavier than the portrait segmenter, so prefer `medium`
178
+ quality on phones. Only one semantic segmenter runs per session; use the
179
+ `multiclass` preset when you need hair and clothes together.
180
+
181
+ **Hair versus the face mesh and the portrait segmenter.** The face mesh is
182
+ geometry: it follows landmarks, supports UV-mapped textures and lighting,
183
+ and knows nothing about hair. The portrait segmenter answers "is this pixel
184
+ a person", which includes hair, clothes and skin together. The hair
185
+ segmenter answers "is this pixel hair", so it is the right source for hair
186
+ tints, recolours, highlights, and for occluding or cutting out hair in a
187
+ composite, all as 2D screen-space effects.
188
+
189
+ Both return a stable object in **feed space**.
190
+
191
+ ## Coordinates, mirroring, aspect and resolution
192
+
193
+ - **Feed space** is the session camera texture: what trackers see, already
194
+ cover-cropped to the viewport and mirrored for webcams. The overlay quad
195
+ covers the viewport exactly like `VideoBackground`, so a feed-space mask
196
+ needs no coordinate work. Row 0 of the mask is the top of the screen.
197
+ - **Source space** (`space: "source"`) is the raw media frame. The overlay
198
+ cover-fits it to the feed (centred crop) using the mask's own aspect ratio
199
+ and the camera texture size, via `computeMaskUvTransform`. Set `mirrored`
200
+ when the feed is mirrored relative to that source (a webcam selfie view;
201
+ image and video sources are not mirrored).
202
+ - **Resolution** is independent of the viewport: masks are nearest-filtered
203
+ class indices, so edges are as sharp as the mask. Raise `resolution` (long
204
+ edge) for crisper edges or use `feather` (in mask texels) to soften them.
205
+ - Landmarks are normalized to the feed, so the face providers are resolution
206
+ and orientation agnostic; rotating the device re-crops the feed and the
207
+ mask follows.
208
+
209
+ ## Rendering
210
+
211
+ Props are `Partial<SegmentationOverlaySettings>` plus `mask`, `texture`,
212
+ `material`, `renderOrder` (default just above the video background) and
213
+ `visible`.
214
+
215
+ | Setting | Meaning |
216
+ | ----------- | --------------------------------------------------------------------------- |
217
+ | `label` | Region(s) to show; comma-separate up to four (`"lips,mouth"`) |
218
+ | `invert` | Show everything except the selected labels |
219
+ | `color` | Tint; with `texture`, multiplied into the image |
220
+ | `opacity` | Layer opacity |
221
+ | `blendMode` | `normal` (flat paint), `multiply` (keeps skin shading), `screen`, `overlay` |
222
+ | `feather` | Edge softening radius in mask texels (0 = hard class edges) |
223
+ | `mirrored` | Flip the mask horizontally |
224
+
225
+ Output is a transparent layer, so other content composites normally above
226
+ and below it. `texture` is sampled in screen UV (cover of the viewport);
227
+ `resolveSegmentationOverlaySettings` / `patchSegmentationOverlaySettings`
228
+ resolve partial settings, and `SegmentationOverlaySettingsPanel` from
229
+ `@vincentt-xr/sdk/debug-ui` edits them live.
230
+
231
+ ### Custom materials
232
+
233
+ `material` accepts a node, a `THREE.Material` instance (shareable), or a
234
+ factory receiving:
235
+
236
+ ```ts
237
+ type SegmentationOverlayMaterialContext = {
238
+ cameraTexture: THREE.Texture | null;
239
+ maskTexture: THREE.Texture; // red channel = class index / 255, nearest-filtered
240
+ maskTransform: THREE.Matrix3; // feed UV → mask UV
241
+ labelIndices: number[]; // the selected classes
242
+ mask: SemanticMask | null;
243
+ settings: SegmentationOverlaySettings;
244
+ };
245
+ ```
246
+
247
+ Sample membership as `abs(floor(texture2D(maskTexture, (maskTransform *
248
+ vec3(uv, 1.0)).xy).r * 255.0 + 0.5) - index) < 0.5`. `labelIndices` is empty
249
+ until the mask has data, and `maskTexture` is re-allocated when the mask
250
+ changes size (the factory is re-run with the new one), so read both from the
251
+ context on every call rather than caching them. The built-in shader is
252
+ exported as `SEGMENTATION_OVERLAY_VERTEX_SHADER` /
253
+ `SEGMENTATION_OVERLAY_FRAGMENT_SHADER`, and the material object API
254
+ (`createSegmentationOverlayMaterial`, `updateSegmentationOverlayMaterial`,
255
+ `useSegmentationOverlayMaterial`) lets you share one material.
256
+
257
+ ## Multiple faces
258
+
259
+ `useFaceRegionMask({ faceIndex: "all" })` merges every face into one mask, so
260
+ one overlay paints all lips. For per-face looks, create one provider per face
261
+ (`faceIndex: 0`, `faceIndex: 1`) and one overlay each. Match the tracker's
262
+ `numFaces`. `usePersonMask` is per scene, not per face.
263
+
264
+ ## Performance
265
+
266
+ - One overlay is one full-screen quad and one small `R8` texture upload per
267
+ mask version. Combine labels (`label="lips,mouth"`) rather than stacking
268
+ overlays when they share a look.
269
+ - Face masks default to a 256 px long edge; rasterizing a few landmark
270
+ polygons at that size costs well under a millisecond. Lower `resolution`
271
+ or set `maxUpdateFps` on the provider to trade sharpness for CPU.
272
+ - `usePersonMask` reads the segmentation mask back from the GPU; keep its
273
+ `maxUpdateFps` low (15 by default) and share one provider between overlays.
274
+ - The session's `perf={{ targetFps, camRes }}` still governs tracking cost.
275
+
276
+ ## Manual visual test plan
277
+
278
+ Run `/segmentation-overlay` in the playground and check each row with the
279
+ region picker, on a webcam and on an image/video source:
280
+
281
+ | Mask / region | Source | Expect |
282
+ | ----------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
283
+ | `lips` (face provider) | webcam, video | Tint hugs the outer lip line; mouth interior untinted; follows talking |
284
+ | `mouth` | video, mouth open | Only the opening is painted |
285
+ | `eyes`, `brows` | video | Both eyes / both brows painted; nothing on the lids' skin |
286
+ | `face_skin` | video | Oval painted; eyes, brows, lips and mouth cut out |
287
+ | `bg` (person provider) | video | Everything but the person painted; silhouette edge follows the mask |
288
+ | `person`, `invert` on | video | Same as `bg` |
289
+ | `mirrored` toggle | webcam | Feed-space masks drift off the face when mirrored (expected); off again aligns |
290
+ | portrait vs landscape pane | resize the pane | Regions stay on the face after the feed re-crops |
291
+ | `feather` 0 → 4 | any | Class edges go from stepped to soft |
292
+ | `hold` 0 vs 250, `predict` 0 vs 150 | "Person Fullbody", 2–8 s | At 0 the regions blink while the far-away face is lost and re-found; with hold they stay on, and with predict they follow the walk |
293
+ | `texture` on | any | Image appears in screen space, clipped to the region |
294
+ | custom stripes material | any | Stripes clipped to the region, still following the face |
295
+ | two faces | "Two People Smiling" | `All faces`: both painted; `Face 0 only`: one |
296
+ | `hair`, short dark hair | "Person ThumbUp" | Hair painted to the hairline; no bleed onto forehead or background |
297
+ | `hair`, long hair, tied | "Person Fullbody", "Person Shaking Head" | Painted mass follows head turns and shakes; strands at the edges soften with `feather` |
298
+ | `hair`, two people | "Two People Smiling" | Both heads painted; the mask is per pixel, not per face |
299
+ | `hair`, lighting | webcam, bright vs dim room | Mask holds under both; dark hair on a dark background is the hard case |
300
+ | `hair`, orientation | webcam, rotate device | Mask re-crops with the feed and stays on the hair |
301
+ | `hair`, `status` | load with wrong `modelUrl` | Status line reads `error` with the reason; nothing is painted |
@@ -0,0 +1,136 @@
1
+ ---
2
+ title: "Capabilities & Session Lifecycle"
3
+ description: "Check what the device can do before a session starts, read structured fallback reasons, and drive a hardened start / pause / resume / stop / reset lifecycle that survives camera loss and tab switches."
4
+ sidebarTitle: "Capabilities & Lifecycle"
5
+ icon: "heart-pulse"
6
+ ---
7
+
8
+ Two things decide whether an AR experience works on a given phone: whether the
9
+ platform can run it at all, and whether the session stays healthy once it
10
+ runs. The SDK answers the first with a capability check that never opens the
11
+ camera, and the second with a session state machine that releases resources
12
+ exactly once and recovers from a lost camera.
13
+
14
+ ## Capability detection
15
+
16
+ ```tsx
17
+ import { useXRCapabilities, resolveTrackerSupport, XRModel } from "@vincentt-xr/sdk";
18
+
19
+ const { capabilities, status, refresh } = useXRCapabilities({
20
+ models: [XRModel.FACE_TRACKER], // optional: HEAD-probe each tracker's asset
21
+ });
22
+ if (status === "ready" && capabilities) {
23
+ const face = resolveTrackerSupport(capabilities, XRModel.FACE_TRACKER);
24
+ if (!face.supported) showFallback(face.reason); // { code, message, recoverable }
25
+ if (face.needsPermission) showCameraPrimer(); // the first getUserMedia will prompt
26
+ }
27
+ ```
28
+
29
+ `useXRCapabilities` needs no `<XRProvider>`, so it runs on a landing screen.
30
+ `detectXRCapabilities(options)` is the same check as a plain promise.
31
+
32
+ The report (`XRCapabilities`):
33
+
34
+ | Field | What it checks |
35
+ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
36
+ | `camera` | Secure context, `getUserMedia` presence, number of video inputs, Permissions API state. `status` is `supported`, `permission-prompt`, `permission-denied`, `unsupported` or `unknown`. |
37
+ | `rendering` | WebGL version (`0`/`1`/`2`), OffscreenCanvas, WebAssembly, WebGPU presence (informational). |
38
+ | `models` | For each `XRModel` in `models`: a `HEAD` request to its asset (status, reason, byte size). Unlisted models report `unknown`. |
39
+ | `canRunTracking` | Rendering is supported and the camera is not unsupported. |
40
+
41
+ `permission-denied` and `unsupported` are distinct: the first is fixable by the
42
+ viewer (recoverable), the second is not. The Permissions API is consulted
43
+ before the device list, so a browser that hides devices until permission is
44
+ granted still reads as denied, not as "no camera".
45
+
46
+ ### Fallback reasons
47
+
48
+ Every degraded state in the SDK is described by one `XRFallbackReason`:
49
+
50
+ ```ts
51
+ type XRFallbackReason = { code: XRFallbackCode; message: string; recoverable: boolean };
52
+ ```
53
+
54
+ | Code | Source |
55
+ | ----------------------------------------------------------------------- | -------------------------- |
56
+ | `camera-permission-denied`, `camera-in-use`, `no-camera` | camera check / session |
57
+ | `camera-insecure-context`, `camera-unsupported` | camera check |
58
+ | `camera-lost` | session (track ended) |
59
+ | `webgl-unavailable`, `offscreen-canvas-unavailable`, `wasm-unavailable` | rendering check |
60
+ | `model-unreachable`, `model-integrity`, `model-init-failed` | model probe / `useXRModel` |
61
+
62
+ Branch on `code`; `message` is display prose. `resolveTrackerSupport` returns
63
+ the most fundamental blocking reason first (rendering, then camera, then the
64
+ model).
65
+
66
+ ### Tracker consumers
67
+
68
+ Inside a session, components that do not own a tracker read its status:
69
+
70
+ ```tsx
71
+ import { useXRTrackerStatus } from "@vincentt-xr/sdk/low-level";
72
+
73
+ const { status, fallback } = useXRTrackerStatus(XRModel.FACE_TRACKER);
74
+ // status: "unavailable" | "loading" | "ready" | "blocked" | "error"
75
+ ```
76
+
77
+ `blocked` means the camera failed (the session error maps to `fallback`);
78
+ `error` means the model's own initialize failed (`useXRModelErrors()` lists
79
+ them; `useXRModel().fallback` carries the same reason for the owner).
80
+
81
+ ## Session lifecycle
82
+
83
+ ```
84
+ idle ──setMediaSource──> media-bound ──start──> running ──stop──> stopped
85
+ ↑ │ ↑ │
86
+ │ pause │ │ resume │
87
+ │ ▼ │ │
88
+ │ paused ─┘ │
89
+ └────────────reset──────────────────────┘
90
+ ```
91
+
92
+ | Call | Effect |
93
+ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
94
+ | `session.pause()` | `running` → `paused`: feed loop stops, every model pauses, the video element pauses. The stream stays open. No-op elsewhere. |
95
+ | `session.resume()` | `paused` → `running` with no rebind. `start()` from `paused` does the same. |
96
+ | `session.stop()` | Releases the stream and tracks, keeps the models initialized. `start()` afterwards needs a `setMediaSource()` first; binding the same source again re-opens it. |
97
+ | `session.reset()` | `stop()` + release every model; back to `idle`. Clears errors. |
98
+ | provider unmount | The facade is disposed: `reset()` plus listener removal. Every later call throws. Models and tracks are released exactly once. |
99
+
100
+ `useXRLifecycle()` (from `/low-level`) subscribes to the state; the example's
101
+ log is built from it.
102
+
103
+ ### Camera loss and permission changes
104
+
105
+ When a live video track ends (device unplugged, permission revoked, another
106
+ app took it) or the camera permission flips to `denied`, the session releases
107
+ the media once, suspends every model, publishes a session error with
108
+ `reason: "camera-lost"` (or `"permission-denied"`) and parks in `stopped`.
109
+ Recovery is the normal path: `setMediaSource()` then `start()`. A track that
110
+ the session itself replaced during a source swap never triggers this.
111
+
112
+ ### Visibility
113
+
114
+ A hidden tab throttles `requestAnimationFrame` and iOS Safari pauses the camera
115
+ track. The session always retries `video.play()` when the tab returns. With
116
+ `<XRProvider lifecycle={{ pauseWhenHidden: true }}>` it also pauses the whole
117
+ session while hidden and resumes on return. A session the app paused itself
118
+ is left paused.
119
+
120
+ ### Source swaps while paused
121
+
122
+ `setMediaSource()` on a paused session binds the new media but stays paused;
123
+ `resume()` starts the loop against it.
124
+
125
+ ## Manual validation
126
+
127
+ Run `/session-health`:
128
+
129
+ | Case | Steps | Expect |
130
+ | ----------------- | ------------------------- | ------------------------------------------------------------------- |
131
+ | Capability report | page load, "re-check" | camera / rendering / model rows with reasons; `canRunTracking` |
132
+ | Pause / resume | buttons | lifecycle log `running → paused → running`; feed fps 0 while paused |
133
+ | Stop / start | stop, start | `start` throws (needs a source); pick a media source to recover |
134
+ | Camera loss | "simulate camera loss" | `error camera-lost`, lifecycle `stopped`, trackers `blocked` |
135
+ | Recovery | pick another media source | lifecycle `running`, error cleared |
136
+ | Route change | navigate away and back | no leaked stream; the page starts clean |
@@ -28,13 +28,31 @@ import {
28
28
  `scaleWithFace`, and `followFaceRoll`
29
29
  - `HandTracker`: 21-joint skeleton; use `hand` or `handIndex` for selection
30
30
  - `GestureTracker`: named gesture recognition
31
+ - `HolisticTracker`: one inference for face + hands + pose with
32
+ `HolisticTracker.Face` / `.Hand` / `.Body` sub-trackers; see
33
+ [Holistic Tracker](/api/holistic-tracker)
31
34
  - `BodyTracker`: 33 pose landmarks
32
35
  - `Segmentation`: portrait mask for background replacement
36
+ - `useHairMask` / `useSemanticSegmentationMask` (hair or multiclass category
37
+ masks for 2D overlays; see [Segmentation Overlay](/api/segmentation-overlay))
33
38
 
34
39
  The example app route `/body` shows the recommended body-tracking pattern:
35
40
  mount `BodyTracker` once, then attach visible content with `TrackingAnchor`
36
41
  targets such as `body.head`, `body.leftShoulder`, and `body.rightWrist`.
37
42
 
43
+ For a low-level hand, gesture, or body landmark, SDK tracker coordinates have a
44
+ center origin, X right, Y up, and positive Z toward the viewer. X/Y occupy the
45
+ usual `[-1, 1]` tracker range; Z is tracker-relative depth and is not a
46
+ normalized screen axis. Convert a point to `ScreenImage`, `ScreenText`, or
47
+ `ScreenShape` design pixels with `trackerPointToScreenPixels` from
48
+ `@vincentt-xr/sdk`. Do not mirror, cover-map, or origin-flip it again.
49
+
50
+ This is distinct from a raw MediaPipe face landmark: face landmarks use a
51
+ top-left normalized image plane and are consumed by `FaceTracker`,
52
+ `FaceMesh`, and `FaceMeshRenderer` internally. Attach 3D face content with
53
+ `TrackingAnchor`; do not pass a raw face landmark to
54
+ `trackerPointToScreenPixels`.
55
+
38
56
  Gesture names are exactly `closed_fist`, `open_palm`, `pointing_up`,
39
57
  `thumb_down`, `thumb_up`, `victory`, `i_love_you`, and `none`. Use
40
58
  `GestureTrigger` for one-shot callbacks and `useXRModelNode` from
@@ -0,0 +1,108 @@
1
+ ---
2
+ title: "Persistent Tracking Configuration"
3
+ description: "A serializable, validated, versioned document for tracker selection and tuning (fps, camera, smoothing, prediction, assets, fallbacks), projected onto the provider, trackers and model options."
4
+ sidebarTitle: "Tracking Config"
5
+ icon: "sliders"
6
+ ---
7
+
8
+ ```tsx
9
+ import { XRProvider, useXRTrackingConfig } from "@vincentt-xr/sdk";
10
+ import { FaceTracker } from "@vincentt-xr/sdk/tracking";
11
+
12
+ const App = () => {
13
+ // Above the provider: perf / lifecycle / quality come from the document.
14
+ const tracking = useXRTrackingConfig({
15
+ storageKey: "myapp.tracking",
16
+ initial: { trackers: { hand: { enabled: true } } },
17
+ });
18
+ const face = tracking.trackerProps("face");
19
+ // Wait for the persisted document before mounting the provider: a different
20
+ // perf / camera after hydration would re-initialize it (camera restart).
21
+ if (!tracking.hydrated) return null;
22
+ return (
23
+ <XRProvider {...tracking.providerProps}>
24
+ <XRScene>
25
+ {face.active && <FaceTracker numFaces={face.numFaces} targetFps={face.targetFps} />}
26
+ </XRScene>
27
+ </XRProvider>
28
+ );
29
+ };
30
+
31
+ tracking.update({ perf: { targetFps: 20 }, camera: { facingMode: "environment" } });
32
+ tracking.reset();
33
+ tracking.json; // the serialized document
34
+ tracking.importJson(text);
35
+ ```
36
+
37
+ ## The document (`XRTrackingConfig`, version 1)
38
+
39
+ | Section | Fields |
40
+ | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | `trackers` | `face { enabled, numFaces 1..10, targetFps \| null, holdMs, predictMs, smoothing }`, `hand / gesture / body { enabled, targetFps \| null }`, `holistic { enabled, targetFps, hands, holdMs, predictMs, analysisMaxDimension, outputFaceBlendshapes, confidence 0..1, delegate }`, `segmentation { enabled, quality }`, `semanticSegmentation { enabled, preset, quality }` |
42
+ | `perf` | `targetFps` (0 = uncapped), `camRes`, `cameraZoom`, `analysisScale` |
43
+ | `lifecycle` | `pauseWhenHidden` |
44
+ | `quality` | an adaptive-quality partial config, or `false` |
45
+ | `camera` | `facingMode`, `deviceId \| null`, `mirrored: boolean \| "auto"` |
46
+ | `fallbacks` | `onUnsupported: "block" \| "degrade"`, `delegate: "auto" \| "GPU" \| "CPU"` |
47
+ | `assets` | `holisticModelUrl`, `semanticSegmentationModelUrl` (http(s) or `null` for the SDK default) |
48
+ | `updatedAt` | wall-clock ms of the last change |
49
+
50
+ `targetFps: null` on a tracker means "inherit `perf.targetFps`".
51
+
52
+ ## What is excluded, on purpose
53
+
54
+ Runtime resources never enter the document: media streams, textures, model
55
+ instances, the session state, calibration measurements (they have their own
56
+ store, `useXRCalibration`), quality decisions, and permission state.
57
+ `serializeTrackingConfig` re-resolves the object, so anything attached at
58
+ runtime is dropped on the way out, and `parseTrackingConfig` drops unknown
59
+ fields on the way in.
60
+
61
+ ## Validation and defaults
62
+
63
+ `resolveTrackingConfig(input, base?)` fills every field from `base`
64
+ (`DEFAULT_TRACKING_CONFIG` unless given), type-checks and clamps each leaf,
65
+ and reports dropped or clamped values as `warnings` instead of throwing.
66
+ `DEFAULT_TRACKING_CONFIG` is the template's shipping default: face only,
67
+ `perf { targetFps 24, camRes 720, cameraZoom 1.2 }`, adaptive quality on,
68
+ front camera, `degrade` on unsupported devices.
69
+
70
+ ## Migration
71
+
72
+ `parseTrackingConfig(json)` accepts any version: a document from an older
73
+ (or missing) `version` is migrated field-by-field onto the current
74
+ defaults, keeping the fields that still exist, with `migrated: true` and a
75
+ warning. Corrupt JSON yields the defaults. `useXRTrackingConfig` re-saves a
76
+ migrated document immediately, so the next load is clean. Bump
77
+ `TRACKING_CONFIG_VERSION` and extend the parse path when the shape changes.
78
+
79
+ ## Projections
80
+
81
+ - `toProviderProps(config)` → `{ perf, lifecycle, quality }` for `<XRProvider>`.
82
+ - `toTrackerProps(config, kind)` → props for `<FaceTracker>` (`numFaces`, `targetFps`, `holdMs`, `predictMs`, `active`), `<HandTracker>`, `<BodyTracker>`, `<GestureTracker>`, `<HolisticTracker>` (incl. `modelUrl`, resolved `delegate`), `<Segmentation>`.
83
+ - `toCameraRequest(config)` → `{ facingMode, deviceId?, mirrored? }` for `setMediaSource` / `switchCamera`.
84
+ - `toModelOptions(config, model)` → acquire options for `useXRModel` / `registry.acquire`.
85
+ - `enabledModels(config)` → the `XRModel[]` to probe with `useXRCapabilities` or preload.
86
+
87
+ `fallbacks.delegate` replaces a tracker's `"auto"` delegate; `fallbacks.onUnsupported`
88
+ is advisory for the app's own gate (see Capabilities).
89
+
90
+ ## Storage
91
+
92
+ `loadTrackingConfig` / `saveTrackingConfig` / `clearTrackingConfig` use
93
+ `localStorage` under `vincentt.xr.tracking-config` by default, with an
94
+ injectable storage for tests or a server-backed store. The hook persists on
95
+ every `update`, clears on `reset`, and exposes `hydrated`.
96
+
97
+ ## Manual validation
98
+
99
+ Run `/tracking-config`:
100
+
101
+ | Case | Steps | Expect |
102
+ | -------- | -------------------------------------------------------------------- | ------------------------------------------------------- |
103
+ | Edit | toggle hand, set face.numFaces 2 | diagnostics shows hand ready; JSON updates |
104
+ | Persist | "reload page" | the same values come back; `persisted` |
105
+ | Validate | numFaces 40 via "edit JSON" + import | clamped to 10 with a warning |
106
+ | Migrate | import `{ "version": 0, "trackers": { "face": { "numFaces": 3 } } }` | numFaces 3, warning "migrated from version 0" |
107
+ | Camera | facingMode environment (phone) | rear camera opens straight; mirrored override respected |
108
+ | Reset | "reset" | defaults, storage cleared |
package/docs/docs.json CHANGED
@@ -29,18 +29,35 @@
29
29
  "pages": [
30
30
  "api/runtime-3d",
31
31
  "api/screen-space-coordinates",
32
+ "api/coordinate-spaces",
32
33
  "api/screen-image",
33
34
  "api/screen-text",
34
35
  "api/screen-shape",
35
36
  "api/trackers",
37
+ "api/holistic-tracker",
36
38
  "api/face-info",
39
+ "api/iris-tracking",
37
40
  "api/face-actions",
41
+ "api/gestures",
42
+ "api/session-lifecycle",
43
+ "api/product-ar",
44
+ "api/product-details",
45
+ "api/tracking-config",
46
+ "compatibility",
47
+ "api/camera-permission",
48
+ "api/cross-camera",
49
+ "api/adaptive-quality",
50
+ "api/multi-subject",
51
+ "api/asset-lifecycle",
52
+ "api/diagnostics-calibration",
38
53
  "api/face-attribute-expression",
39
54
  "api/face-binding-anchors",
40
55
  "api/face-effects",
41
56
  "api/face-mesh",
42
57
  "api/face-painting",
43
58
  "api/face-retouching",
59
+ "api/segmentation-overlay",
60
+ "api/person-cutout",
44
61
  "api/capture-recording",
45
62
  "api/audio",
46
63
  "api/texture-sources",
@@ -60,10 +77,15 @@
60
77
  "icon": "plug",
61
78
  "pages": ["guides/harness-debug-ui"]
62
79
  },
80
+ {
81
+ "group": "Creator Guides",
82
+ "icon": "palette",
83
+ "pages": ["guides/lip-effect-textures"]
84
+ },
63
85
  {
64
86
  "group": "Examples",
65
87
  "icon": "code",
66
- "pages": ["examples/scene-object-recipes"]
88
+ "pages": ["examples/scene-object-recipes", "examples/lip-shaders"]
67
89
  }
68
90
  ]
69
91
  }