lecodes-sdk 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (367) hide show
  1. package/README.md +114 -0
  2. package/dist/global.d.ts +503 -0
  3. package/dist/host.d.ts +61 -0
  4. package/dist/inject.js +4730 -0
  5. package/dist/types/animate/animate.d.ts +20 -0
  6. package/dist/types/animate/bezier.d.ts +2 -0
  7. package/dist/types/animate/easings.d.ts +21 -0
  8. package/dist/types/canvas/Canvas.d.ts +168 -0
  9. package/dist/types/core/Aspect.d.ts +171 -0
  10. package/dist/types/core/InspectorUI.d.ts +88 -0
  11. package/dist/types/core/StateMachine.d.ts +81 -0
  12. package/dist/types/core/color.d.ts +13 -0
  13. package/dist/types/core/compWrite.d.ts +11 -0
  14. package/dist/types/core/events.d.ts +11 -0
  15. package/dist/types/core/fields.d.ts +58 -0
  16. package/dist/types/core/registry.d.ts +7 -0
  17. package/dist/types/core/signals.d.ts +29 -0
  18. package/dist/types/core/time.d.ts +31 -0
  19. package/dist/types/g2/Camera2D.d.ts +19 -0
  20. package/dist/types/g2/CharacterController2D.d.ts +134 -0
  21. package/dist/types/g2/Node2D.d.ts +91 -0
  22. package/dist/types/g2/OneWay2D.d.ts +24 -0
  23. package/dist/types/g2/Physics2D.d.ts +147 -0
  24. package/dist/types/g2/Scene2D.d.ts +72 -0
  25. package/dist/types/g2/Shape2D.d.ts +58 -0
  26. package/dist/types/g2/Sprite.d.ts +51 -0
  27. package/dist/types/g2/SpriteAnimation.d.ts +50 -0
  28. package/dist/types/g2/SpriteSheet.d.ts +69 -0
  29. package/dist/types/g2/Texture2D.d.ts +16 -0
  30. package/dist/types/g2/Tilemap.d.ts +20 -0
  31. package/dist/types/g2/Tileset.d.ts +32 -0
  32. package/dist/types/g2/Trigger2D.d.ts +20 -0
  33. package/dist/types/g2/autotile.d.ts +133 -0
  34. package/dist/types/g2/cells.d.ts +14 -0
  35. package/dist/types/g2/defineScene2d.d.ts +141 -0
  36. package/dist/types/g2/groups2d.d.ts +16 -0
  37. package/dist/types/g2/loop.d.ts +4 -0
  38. package/dist/types/g2/scenarios2d.d.ts +30 -0
  39. package/dist/types/g2/touch.d.ts +2 -0
  40. package/dist/types/gl/Camera.d.ts +79 -0
  41. package/dist/types/gl/CameraPlace.d.ts +26 -0
  42. package/dist/types/gl/CharacterController.d.ts +98 -0
  43. package/dist/types/gl/Gearbox.d.ts +86 -0
  44. package/dist/types/gl/Geometry.d.ts +39 -0
  45. package/dist/types/gl/IK.d.ts +53 -0
  46. package/dist/types/gl/InstancedMesh.d.ts +46 -0
  47. package/dist/types/gl/Light.d.ts +73 -0
  48. package/dist/types/gl/Lightmap.d.ts +85 -0
  49. package/dist/types/gl/Locomotion.d.ts +279 -0
  50. package/dist/types/gl/Material.d.ts +100 -0
  51. package/dist/types/gl/Mesh.d.ts +33 -0
  52. package/dist/types/gl/Model.d.ts +53 -0
  53. package/dist/types/gl/NavAgent.d.ts +99 -0
  54. package/dist/types/gl/NavMesh.d.ts +169 -0
  55. package/dist/types/gl/Node.d.ts +99 -0
  56. package/dist/types/gl/Noise.d.ts +20 -0
  57. package/dist/types/gl/Particles.d.ts +277 -0
  58. package/dist/types/gl/Physics.d.ts +91 -0
  59. package/dist/types/gl/Plane.d.ts +19 -0
  60. package/dist/types/gl/Ragdoll.d.ts +86 -0
  61. package/dist/types/gl/Ray.d.ts +8 -0
  62. package/dist/types/gl/Scene.d.ts +221 -0
  63. package/dist/types/gl/Shape.d.ts +122 -0
  64. package/dist/types/gl/Terrain.d.ts +280 -0
  65. package/dist/types/gl/Texture.d.ts +27 -0
  66. package/dist/types/gl/Trigger.d.ts +10 -0
  67. package/dist/types/gl/Vehicle.d.ts +191 -0
  68. package/dist/types/gl/Wheel.d.ts +95 -0
  69. package/dist/types/gl/animation/AnimationClip.d.ts +60 -0
  70. package/dist/types/gl/animation/Animator.d.ts +219 -0
  71. package/dist/types/gl/animation/Layer.d.ts +31 -0
  72. package/dist/types/gl/animation/Loop.d.ts +17 -0
  73. package/dist/types/gl/animation/Playback.d.ts +36 -0
  74. package/dist/types/gl/animation/core.d.ts +367 -0
  75. package/dist/types/gl/controls.d.ts +19 -0
  76. package/dist/types/gl/physicsEvents.d.ts +1 -0
  77. package/dist/types/gl/scenarios.d.ts +98 -0
  78. package/dist/types/gl/state.d.ts +5 -0
  79. package/dist/types/gl/terrainMesh.d.ts +46 -0
  80. package/dist/types/gl/touch.d.ts +2 -0
  81. package/dist/types/inject.d.ts +123 -0
  82. package/dist/types/math/Mathf.d.ts +39 -0
  83. package/dist/types/math/mat4.d.ts +66 -0
  84. package/dist/types/math/quat.d.ts +53 -0
  85. package/dist/types/math/vec.d.ts +129 -0
  86. package/dist/types/net/codec.d.ts +25 -0
  87. package/dist/types/net/core.d.ts +106 -0
  88. package/dist/types/net/index.d.ts +80 -0
  89. package/dist/types/net/replication.d.ts +101 -0
  90. package/dist/types/plugins/camera.d.ts +25 -0
  91. package/dist/types/plugins/geolocation.d.ts +39 -0
  92. package/dist/types/plugins/oauth.d.ts +25 -0
  93. package/dist/types/plugins/permission.d.ts +1 -0
  94. package/dist/types/plugins/push.d.ts +46 -0
  95. package/dist/types/plugins/qr.d.ts +21 -0
  96. package/dist/types/plugins/service.d.ts +33 -0
  97. package/dist/types/runtime/app.d.ts +67 -0
  98. package/dist/types/runtime/appEvents.d.ts +7 -0
  99. package/dist/types/runtime/channel.d.ts +1 -0
  100. package/dist/types/runtime/clipboard.d.ts +7 -0
  101. package/dist/types/runtime/datetime.d.ts +80 -0
  102. package/dist/types/runtime/device.d.ts +126 -0
  103. package/dist/types/runtime/fetch.d.ts +31 -0
  104. package/dist/types/runtime/files.d.ts +33 -0
  105. package/dist/types/runtime/input.d.ts +121 -0
  106. package/dist/types/runtime/media.d.ts +46 -0
  107. package/dist/types/runtime/misc.d.ts +11 -0
  108. package/dist/types/runtime/net.d.ts +15 -0
  109. package/dist/types/runtime/rpc.d.ts +44 -0
  110. package/dist/types/runtime/service.d.ts +1 -0
  111. package/dist/types/runtime/share.d.ts +2 -0
  112. package/dist/types/runtime/storage.d.ts +5 -0
  113. package/dist/types/runtime/touch.d.ts +56 -0
  114. package/dist/types/scene/defineScene.d.ts +296 -0
  115. package/dist/types/scene/editorPlugins.d.ts +82 -0
  116. package/dist/types/scene/gizmos.d.ts +56 -0
  117. package/dist/types/scene/grammar.d.ts +56 -0
  118. package/dist/types/scene/level.d.ts +98 -0
  119. package/dist/types/scene/material.d.ts +76 -0
  120. package/dist/types/server/auth/appConfig.d.ts +16 -0
  121. package/dist/types/server/auth/global.d.ts +56 -0
  122. package/dist/types/server/auth/models.d.ts +164 -0
  123. package/dist/types/server/auth/types.d.ts +56 -0
  124. package/dist/types/server/channel.d.ts +28 -0
  125. package/dist/types/server/context.d.ts +27 -0
  126. package/dist/types/server/db/defineDb.d.ts +36 -0
  127. package/dist/types/server/db/fields.d.ts +146 -0
  128. package/dist/types/server/db/index.d.ts +6 -0
  129. package/dist/types/server/db/marci/query.d.ts +326 -0
  130. package/dist/types/server/db/types.d.ts +163 -0
  131. package/dist/types/server/errors.d.ts +8 -0
  132. package/dist/types/server/inject.d.ts +5 -0
  133. package/dist/types/ui/NativeView.d.ts +38 -0
  134. package/dist/types/ui/UI.d.ts +27 -0
  135. package/dist/types/ui/UIBottomSheet.d.ts +71 -0
  136. package/dist/types/ui/UIButton.d.ts +30 -0
  137. package/dist/types/ui/UIContainer.d.ts +33 -0
  138. package/dist/types/ui/UIImage.d.ts +35 -0
  139. package/dist/types/ui/UIInput.d.ts +110 -0
  140. package/dist/types/ui/UIModal.d.ts +70 -0
  141. package/dist/types/ui/UINode.d.ts +363 -0
  142. package/dist/types/ui/UIPager.d.ts +123 -0
  143. package/dist/types/ui/UIPopover.d.ts +39 -0
  144. package/dist/types/ui/UIScreen.d.ts +57 -0
  145. package/dist/types/ui/UIScrollable.d.ts +40 -0
  146. package/dist/types/ui/UISpacer.d.ts +9 -0
  147. package/dist/types/ui/UITabs.d.ts +96 -0
  148. package/dist/types/ui/UIText.d.ts +19 -0
  149. package/dist/types/ui/UIVideo.d.ts +28 -0
  150. package/dist/types/ui/UIVirtualizedList.d.ts +97 -0
  151. package/dist/types/ui/UIWidget.d.ts +65 -0
  152. package/dist/types/ui/fonts.d.ts +9 -0
  153. package/dist/types/ui/presentable.d.ts +68 -0
  154. package/dist/types/ui/router.d.ts +40 -0
  155. package/dist/types/ui/theme.d.ts +26 -0
  156. package/dist/types.json +1 -0
  157. package/package.json +46 -0
  158. package/prompts/2d.md +395 -0
  159. package/prompts/3d-scene-files.md +113 -0
  160. package/prompts/3d-scene.md +119 -0
  161. package/prompts/3d.md +303 -0
  162. package/prompts/README.md +142 -0
  163. package/prompts/ar.md +109 -0
  164. package/prompts/canvas.md +27 -0
  165. package/prompts/compose.ts +46 -0
  166. package/prompts/concept.md +49 -0
  167. package/prompts/core-design.md +75 -0
  168. package/prompts/core.md +264 -0
  169. package/prompts/design.md +218 -0
  170. package/prompts/dist/2d-game.md +1530 -0
  171. package/prompts/dist/3d-app.md +1672 -0
  172. package/prompts/dist/ar-app.md +1548 -0
  173. package/prompts/dist/concept.md +49 -0
  174. package/prompts/dist/design.md +498 -0
  175. package/prompts/dist/ui-app.md +1134 -0
  176. package/prompts/index.md +37 -0
  177. package/prompts/intake-prompt.md +33 -0
  178. package/prompts/namer-prompt.md +12 -0
  179. package/prompts/router-prompt.md +50 -0
  180. package/prompts/select.ts +111 -0
  181. package/prompts/ui-design.md +203 -0
  182. package/prompts/ui.md +803 -0
  183. package/src/animate/animate.ts +238 -0
  184. package/src/animate/bezier.ts +138 -0
  185. package/src/animate/easings.ts +126 -0
  186. package/src/bridges.d.ts +1345 -0
  187. package/src/canvas/Canvas.ts +305 -0
  188. package/src/compile/__tests__/assetIconMacro.test.ts +219 -0
  189. package/src/compile/__tests__/assetMacro.test.ts +74 -0
  190. package/src/compile/__tests__/assetName.test.ts +55 -0
  191. package/src/compile/__tests__/compile.test.ts +287 -0
  192. package/src/compile/__tests__/detectEntry.test.ts +132 -0
  193. package/src/compile/__tests__/fontMacro.test.ts +199 -0
  194. package/src/compile/aspectMacro.ts +86 -0
  195. package/src/compile/assetIconMacro.ts +384 -0
  196. package/src/compile/assetMacro.ts +146 -0
  197. package/src/compile/assetName.ts +50 -0
  198. package/src/compile/bundler.ts +287 -0
  199. package/src/compile/compileProject.ts +127 -0
  200. package/src/compile/detectEntry.ts +128 -0
  201. package/src/compile/fontMacro.ts +459 -0
  202. package/src/compile/fontRegistry.ts +78 -0
  203. package/src/compile/header.ts +67 -0
  204. package/src/compile/index.ts +100 -0
  205. package/src/compile/libraryImports.ts +52 -0
  206. package/src/compile/liteMaterial.ts +247 -0
  207. package/src/compile/sceneEditor.ts +88 -0
  208. package/src/compile/serverSplit.ts +233 -0
  209. package/src/compile/serverTypes.ts +227 -0
  210. package/src/compile/sfnt.ts +98 -0
  211. package/src/compile/shaderSchema.ts +202 -0
  212. package/src/compile/shaderTargets.ts +77 -0
  213. package/src/compile/sourcemap.ts +25 -0
  214. package/src/core/Aspect.ts +568 -0
  215. package/src/core/InspectorUI.ts +212 -0
  216. package/src/core/StateMachine.ts +308 -0
  217. package/src/core/__tests__/stateMachine.test.ts +132 -0
  218. package/src/core/color.ts +66 -0
  219. package/src/core/compWrite.ts +42 -0
  220. package/src/core/events.ts +38 -0
  221. package/src/core/fields.ts +120 -0
  222. package/src/core/registry.ts +23 -0
  223. package/src/core/signals.ts +277 -0
  224. package/src/core/time.ts +84 -0
  225. package/src/g2/Camera2D.ts +40 -0
  226. package/src/g2/CharacterController2D.ts +276 -0
  227. package/src/g2/Node2D.ts +267 -0
  228. package/src/g2/OneWay2D.ts +66 -0
  229. package/src/g2/Physics2D.ts +346 -0
  230. package/src/g2/Scene2D.ts +209 -0
  231. package/src/g2/Shape2D.ts +259 -0
  232. package/src/g2/Sprite.ts +89 -0
  233. package/src/g2/SpriteAnimation.ts +171 -0
  234. package/src/g2/SpriteSheet.ts +166 -0
  235. package/src/g2/Texture2D.ts +47 -0
  236. package/src/g2/Tilemap.ts +41 -0
  237. package/src/g2/Tileset.ts +71 -0
  238. package/src/g2/Trigger2D.ts +77 -0
  239. package/src/g2/autotile.ts +433 -0
  240. package/src/g2/cells.ts +91 -0
  241. package/src/g2/defineScene2d.ts +381 -0
  242. package/src/g2/groups2d.ts +106 -0
  243. package/src/g2/loop.ts +50 -0
  244. package/src/g2/scenarios2d.ts +69 -0
  245. package/src/g2/touch.ts +83 -0
  246. package/src/gl/Camera.ts +160 -0
  247. package/src/gl/CameraPlace.ts +52 -0
  248. package/src/gl/CharacterController.ts +238 -0
  249. package/src/gl/Gearbox.ts +212 -0
  250. package/src/gl/Geometry.ts +279 -0
  251. package/src/gl/IK.ts +193 -0
  252. package/src/gl/InstancedMesh.ts +132 -0
  253. package/src/gl/Light.ts +135 -0
  254. package/src/gl/Lightmap.ts +402 -0
  255. package/src/gl/Locomotion.ts +498 -0
  256. package/src/gl/Material.ts +279 -0
  257. package/src/gl/Mesh.ts +83 -0
  258. package/src/gl/Model.ts +124 -0
  259. package/src/gl/NavAgent.ts +337 -0
  260. package/src/gl/NavMesh.ts +397 -0
  261. package/src/gl/Node.ts +350 -0
  262. package/src/gl/Noise.ts +30 -0
  263. package/src/gl/Particles.ts +676 -0
  264. package/src/gl/Physics.ts +222 -0
  265. package/src/gl/Plane.ts +53 -0
  266. package/src/gl/Ragdoll.ts +270 -0
  267. package/src/gl/Ray.ts +16 -0
  268. package/src/gl/Scene.ts +505 -0
  269. package/src/gl/Shape.ts +409 -0
  270. package/src/gl/Terrain.ts +1092 -0
  271. package/src/gl/Texture.ts +63 -0
  272. package/src/gl/Trigger.ts +45 -0
  273. package/src/gl/Vehicle.ts +473 -0
  274. package/src/gl/Wheel.ts +240 -0
  275. package/src/gl/animation/AnimationClip.ts +204 -0
  276. package/src/gl/animation/Animator.ts +329 -0
  277. package/src/gl/animation/Layer.ts +39 -0
  278. package/src/gl/animation/Loop.ts +31 -0
  279. package/src/gl/animation/Playback.ts +52 -0
  280. package/src/gl/animation/core.ts +670 -0
  281. package/src/gl/controls.ts +95 -0
  282. package/src/gl/physicsEvents.ts +20 -0
  283. package/src/gl/scenarios.ts +291 -0
  284. package/src/gl/state.ts +6 -0
  285. package/src/gl/terrainMesh.ts +219 -0
  286. package/src/gl/touch.ts +68 -0
  287. package/src/host.d.ts +61 -0
  288. package/src/inject.ts +203 -0
  289. package/src/math/Mathf.ts +118 -0
  290. package/src/math/mat4.ts +278 -0
  291. package/src/math/quat.ts +232 -0
  292. package/src/math/vec.ts +255 -0
  293. package/src/net/codec.ts +119 -0
  294. package/src/net/core.ts +384 -0
  295. package/src/net/index.ts +181 -0
  296. package/src/net/replication.ts +622 -0
  297. package/src/plugins/camera.ts +81 -0
  298. package/src/plugins/geolocation.ts +123 -0
  299. package/src/plugins/oauth.ts +61 -0
  300. package/src/plugins/permission.ts +7 -0
  301. package/src/plugins/push.ts +132 -0
  302. package/src/plugins/qr.ts +73 -0
  303. package/src/plugins/service.ts +47 -0
  304. package/src/runtime/app.ts +101 -0
  305. package/src/runtime/appEvents.ts +54 -0
  306. package/src/runtime/channel.ts +61 -0
  307. package/src/runtime/clipboard.ts +20 -0
  308. package/src/runtime/datetime.ts +329 -0
  309. package/src/runtime/device.ts +293 -0
  310. package/src/runtime/fetch.ts +77 -0
  311. package/src/runtime/files.ts +108 -0
  312. package/src/runtime/input.ts +175 -0
  313. package/src/runtime/media.ts +111 -0
  314. package/src/runtime/misc.ts +16 -0
  315. package/src/runtime/net.ts +36 -0
  316. package/src/runtime/rpc.ts +218 -0
  317. package/src/runtime/service.ts +83 -0
  318. package/src/runtime/share.ts +9 -0
  319. package/src/runtime/storage.ts +13 -0
  320. package/src/runtime/touch.ts +76 -0
  321. package/src/scene/defineScene.ts +1396 -0
  322. package/src/scene/editorPlugins.ts +110 -0
  323. package/src/scene/gizmos.ts +148 -0
  324. package/src/scene/grammar.ts +120 -0
  325. package/src/scene/level.ts +296 -0
  326. package/src/scene/material.ts +188 -0
  327. package/src/server/auth/appConfig.ts +12 -0
  328. package/src/server/auth/global.ts +80 -0
  329. package/src/server/auth/host.ts +318 -0
  330. package/src/server/auth/models.ts +83 -0
  331. package/src/server/auth/types.ts +50 -0
  332. package/src/server/channel.ts +56 -0
  333. package/src/server/context.ts +36 -0
  334. package/src/server/db/defineDb.ts +237 -0
  335. package/src/server/db/fields.ts +132 -0
  336. package/src/server/db/httpTransport.ts +93 -0
  337. package/src/server/db/index.ts +7 -0
  338. package/src/server/db/marci/query.ts +412 -0
  339. package/src/server/db/types.ts +202 -0
  340. package/src/server/errors.ts +12 -0
  341. package/src/server/host.ts +74 -0
  342. package/src/server/inject.ts +13 -0
  343. package/src/server/runtime.ts +133 -0
  344. package/src/server/validate.ts +87 -0
  345. package/src/ui/NativeView.ts +142 -0
  346. package/src/ui/UI.ts +39 -0
  347. package/src/ui/UIBottomSheet.ts +139 -0
  348. package/src/ui/UIButton.ts +101 -0
  349. package/src/ui/UIContainer.ts +60 -0
  350. package/src/ui/UIImage.ts +83 -0
  351. package/src/ui/UIInput.ts +185 -0
  352. package/src/ui/UIModal.ts +139 -0
  353. package/src/ui/UINode.ts +830 -0
  354. package/src/ui/UIPager.ts +362 -0
  355. package/src/ui/UIPopover.ts +100 -0
  356. package/src/ui/UIScreen.ts +123 -0
  357. package/src/ui/UIScrollable.ts +87 -0
  358. package/src/ui/UISpacer.ts +14 -0
  359. package/src/ui/UITabs.ts +236 -0
  360. package/src/ui/UIText.ts +51 -0
  361. package/src/ui/UIVideo.ts +88 -0
  362. package/src/ui/UIVirtualizedList.ts +241 -0
  363. package/src/ui/UIWidget.ts +127 -0
  364. package/src/ui/fonts.ts +13 -0
  365. package/src/ui/presentable.ts +117 -0
  366. package/src/ui/router.ts +132 -0
  367. package/src/ui/theme.ts +84 -0
@@ -0,0 +1,1345 @@
1
+ // The host ABI. These four objects are injected onto the global scope by the runtime host
2
+ // (web viewer, desktop, iOS) before any user/SDK code runs. The SDK is a thin, typed re-skin over
3
+ // them — it never bundles them; it only references them as free globals. Faithfully tracks the
4
+ // surface in packages/worker/src/global.d.ts (the proven contract).
5
+
6
+ declare global {
7
+
8
+ // ---- 3D engine (Filament / creator-gl) -------------------------------------------------------
9
+ var _creator: {
10
+ backend: string
11
+
12
+ createScene(): number
13
+ createOverlayScene(sceneId: number): number
14
+ createGLView(): void
15
+ openScene(sceneId: number): void
16
+ closeScene(): void
17
+ launchAR(sceneId: number, onComplete: () => void, onReject: () => void): void
18
+ stopAR(): void
19
+ launchVR(sceneId: number, onComplete: () => void, onReject: (err?: unknown) => void): void
20
+ stopVR(): void
21
+ addEntityToScene(sceneId: number, entityId: number): void
22
+ removeEntityFromScene(sceneId: number, entityId: number): void
23
+ getSceneMaterial(sceneId: number): number
24
+ warmRender(sceneId: number, onComplete: () => void): void
25
+
26
+ createEntity(): number
27
+ cloneEntity(entityId: number): number
28
+ destroyEntity(entityId: number): void
29
+
30
+ setMatrix(entityId: number, mat: Float32Array): void
31
+ setWorldMatrix(entityId: number, mat: Float32Array): void
32
+ getChildren(entityId: number): number[]
33
+ getChildCount(entityId: number): number
34
+ getChild(entityId: number, index: number): number
35
+
36
+ setPosition(entityId: number, x: number, y: number, z: number): void
37
+ setQuaternion(entityId: number, x: number, y: number, z: number, w: number): void
38
+ setEulerAngles(entityId: number, x: number, y: number, z: number, order: number): void
39
+ setScale(entityId: number, x: number, y: number, z: number): void
40
+
41
+ addChildren(parentEntityId: number, entityIds: number[]): void
42
+ setParent(entityId: number, parentEntityId: number, worldPositionStays: boolean): void
43
+ setParentNull(entityId: number, worldPositionStays: boolean): void
44
+ getParent(entityId: number): number
45
+ traverse(entityId: number, callback: (entityId: number) => void): void
46
+ /** Descendant of `entityId` by name, with the Animator's binding rule: exact name first, then the
47
+ * short name (after the last `:` / `|` — Mixamo `mixamorig:Hips`); a skin joint beats a plain node
48
+ * of the same name (merged GLBs carrying a leftover skeleton copy), otherwise first in tree order.
49
+ * 0 when absent. Optional: hosts that predate it fall back to the SDK's traverse walk (node.bone). */
50
+ findNode?(entityId: number, name: string): number
51
+
52
+ createSunLight(entityId: number, x: number, y: number, z: number, intensity: number, color: number, shadowsQuality: number, shadowDistance: number): void
53
+ /** Punctual light. `intensity` is luminous POWER in lumens; `falloff` is the metres of
54
+ * influence (filament's own default is 1 m, i.e. invisible, so it is always passed). */
55
+ createPointLight?(entityId: number, intensity: number, color: number, falloff: number, castShadows: boolean): void
56
+ /** Live intensity for any light (sun: lux, point: lumens) — a flash animates instead of rebuilding. */
57
+ setLightIntensity?(entityId: number, intensity: number): void
58
+ setLightColor?(entityId: number, color: number): void
59
+ /** `iblFetchId` (optional, -1 = none) names the scene's own probe; without it the host looks for a project-wide `ibl.ktx`. */
60
+ setDefaultIbl(sceneId: number, intensity: number, iblFetchId?: number): void
61
+ setBloomOptions(sceneId: number, enabled: boolean, strength: number, quality: number): void
62
+ setToneMapping?(sceneId: number, mode: number): void
63
+ /** Exposure of the scene's camera — filament's physical model: `aperture` f-stops,
64
+ * `shutterSpeed` seconds, `sensitivity` ISO (default f/16, 1/125 s, ISO 100 = EV100 15, bright
65
+ * sunlight). ISO ×2 = one stop brighter. Scene-referred light follows it; particle emission is
66
+ * post-exposure and does not. Optional: hosts that predate it keep the fixed default. */
67
+ setCameraExposure?(sceneId: number, aperture: number, shutterSpeed: number, sensitivity: number): void
68
+ /** Display output range (macOS/iOS EDR, `device.hdr`). `getDisplayHeadroom` is what the engine
69
+ * renders to right now: the screen's peak as a multiple of SDR white, quantised to half-stops;
70
+ * 0 or 1 = SDR. Live — it ramps up from 1 over the first seconds, follows brightness and the
71
+ * screen under the window. `getDisplayMaxHeadroom` is the peak the surface can reach at all
72
+ * (1 = SDR), constant once the surface exists: the value to branch content on. Optional: hosts
73
+ * that predate it, or render 8-bit, are SDR. */
74
+ getDisplayHeadroom?(): number
75
+ getDisplayMaxHeadroom?(): number
76
+ /** HDR look, engine-wide (creator.h `setHdrStrength` / `setHdrPaperWhite`): `strength` 0..1 =
77
+ * how much of the picture reaches for the headroom (0 only what SDR clipped, 1 nearly
78
+ * everything), `paperWhite` 1..8 = where the operator's white lands as a multiple of SDR white
79
+ * (the "HDR brightness" of a console calibration screen; clamped to the headroom). A host may
80
+ * pin either from its environment — the getters report what is in force. Optional. */
81
+ setHdrStrength?(strength: number): void
82
+ getHdrStrength?(): number
83
+ setHdrPaperWhite?(paperWhite: number): void
84
+ getHdrPaperWhite?(): number
85
+ /** Screen-space ambient occlusion: the contact darkening in creases and where objects meet the
86
+ * ground. `radius` is world-space metres, `power` the falloff contrast, `quality` 0 LOW … 3
87
+ * ULTRA (sample count — not the buffer resolution, which stays half-res).
88
+ * Optional: hosts that predate it render without AO and the SDK skips the call. */
89
+ setAmbientOcclusionOptions?(sceneId: number, enabled: boolean, intensity: number, radius: number, power: number, quality: number): void
90
+ /** Distance fog / aerial perspective. `color` is packed 0xRRGGBB used as a TINT on the
91
+ * in-scattered ambient — the engine multiplies it by the environment luminance, so white means
92
+ * "as bright as the ambient" and it is NOT the absolute-radiance convention `setSkybox` uses.
93
+ * `density` = extinction per metre at `height`, `heightFalloff` 1/m (0 = uniform),
94
+ * `cutOff` <= 0 = apply at every distance (the skybox included — that is what blends the
95
+ * horizon), `fromIbl` = take the colour from the environment in the view direction and tint
96
+ * it by `color`.
97
+ * Optional: hosts that predate it render without fog and the SDK skips the call. */
98
+ setFogOptions?(sceneId: number, enabled: boolean, color: number, distance: number, density: number, height: number, heightFalloff: number, maxOpacity: number, cutOff: number, fromIbl: boolean): void
99
+ setSkybox(sceneId: number, color: number): void
100
+ /** Draw the scene's IBL environment as the sky instead of a flat colour. No-op until the IBL
101
+ * exists (setDefaultIbl runs first). Optional: hosts that predate it keep the flat skybox. */
102
+ setSkyboxFromEnvironment?(sceneId: number): void
103
+ /** The sky from its own KTX1 cubemap. `fetchId` is a local-resource id (`_creatorUtils.fetchLocal`
104
+ * / an `asset()` handle), pointing at cmgen's `<name>_skybox.ktx` — the sharp single-mip file,
105
+ * not the roughness-prefiltered `_ibl.ktx` beside it. Optional: hosts that predate it keep the
106
+ * flat skybox. */
107
+ setSkyboxTexture?(sceneId: number, fetchId: number): void
108
+ setSceneMultiSampleAntiAliasing(sceneId: number, enabled: boolean, scale: number): void
109
+ /** Render resolution: `renderScale` (0.25–1) = fixed 3D-buffer scale vs the viewport (the UI is
110
+ * untouched; a host that owns the 3D texture resizes it, one rendering into the swapchain may
111
+ * ignore it), `dynamicResolution` = engine-adaptive scaling under that down to `minScale`.
112
+ * Optional: older hosts predate it and the SDK skips the call. */
113
+ setSceneRenderOptions?(sceneId: number, renderScale: number, dynamicResolution: boolean, minScale: number): void
114
+ /** Anisotropic filtering for every texture bound FROM HERE ON (1 = off, 2 = the engine default,
115
+ * 16 = max; clamped). Engine-wide rather than per scene, because a sampler is baked when its
116
+ * texture is bound — a glTF binds during `Model.load`, and two scenes cannot disagree about a
117
+ * texture they share. Call it before loading the assets it should apply to; `SceneOptions.
118
+ * anisotropy` does that automatically (a scene's env runs before its nodes build). Optional:
119
+ * hosts that predate it keep isotropic filtering and the SDK skips the call. */
120
+ setTextureAnisotropy?(level: number): void
121
+ setMaterialGlobalParameter(sceneId: number, i: number, x: number, y: number, z: number, w: number): void
122
+ getCameraFov(sceneId: number, fovType: number): number
123
+ /** The scene camera's projection: VERTICAL fov in degrees + near/far clip distances (defaults
124
+ * 60 / 0.01 / 1000). Aspect stays host-owned — the host STORES these per scene and re-applies
125
+ * them whenever the viewport changes, so one call outlives every resize. Optional: hosts that
126
+ * predate it keep the fixed defaults and the SDK feature-detects (Camera.setProjection). */
127
+ setCameraProjection?(sceneId: number, fov: number, near: number, far: number): void
128
+
129
+ getMatrix(entityId: number, mat: Float32Array): boolean
130
+ getWorldMatrix(entityId: number, mat: Float32Array): void
131
+ getWorldPosition(entityId: number, mat: Float32Array): void
132
+ getWorldDirection(entityId: number, mode: number, mat: Float32Array): void
133
+
134
+ getWorldMatrixInverse(entityId: number, mat: Float32Array): void
135
+ getCameraViewDirection(sceneId: number, screenX: number, screenY: number, mat: Float32Array): void
136
+
137
+ /** `uv1` (optional, 2 floats per vertex) is the lightmap UV set (Geometry.uv1); absent → the
138
+ * host duplicates `uv` into UV1. Hosts that predate the argument ignore it. */
139
+ setMesh(entityId: number, materialId: number, vertices: Float32Array, normals: Float32Array, indices: Uint16Array, uv: Float32Array, meshType: number, uv1?: Float32Array): void
140
+ // Terrain (gl/Terrain.ts ↔ creator-gl/src/terrain.cpp, docs/terrain-plan.md §1.4): a heightmap grid of
141
+ // sizeX × sizeZ samples `cellSize` apart (+X across columns, +Z across rows, height on +Y, sample 0 at
142
+ // the node origin), drawn as one renderable per `chunk`×`chunk` cells on internal children of the
143
+ // entity (u16 indices → chunk ≤ 255). `holes` = one byte per sample, 1 = hole (a triangle exists only
144
+ // when its three samples are valid — the same rule as the Jolt height field), null = none. UV0 = local
145
+ // XZ in metres, UV1 = the terrain's unit square. Returns the chunk count (0 = refused). terrainUpdate
146
+ // re-reads the FULL arrays and rebuilds the chunks the sample rectangle touches. All optional: a host
147
+ // without them gets the SDK's own chunk meshes through setMesh (gl/terrainMesh.ts).
148
+ terrainCreate?(entityId: number, materialId: number, heights: Float32Array, sizeX: number, sizeZ: number, cellSize: number, chunk: number, holes: Uint8Array | null): number
149
+ terrainUpdate?(entityId: number, heights: Float32Array, holes: Uint8Array | null, x0: number, z0: number, w: number, h: number): void
150
+ terrainSetMaterial?(entityId: number, materialId: number): void
151
+ terrainSetShadows?(entityId: number, cast: boolean, receive: boolean): void
152
+ // Instanced mesh (gl/InstancedMesh.ts ↔ creator-gl/src/instanced.cpp): `count` transforms,
153
+ // column-major 4×4 local to the node, all-zero = hidden. setInstanceTransforms writes
154
+ // matrices.length / 16 of them starting at `first` (a subarray view is fine — consumed synchronously).
155
+ createInstancedMesh(entityId: number, materialId: number, vertices: Float32Array, normals: Float32Array, indices: Uint16Array, uv: Float32Array, count: number): void
156
+ setInstanceTransforms(entityId: number, matrices: Float32Array, first: number): void
157
+ setInstancedMeshMaterial(entityId: number, materialId: number): void
158
+ setInstancedMeshShadows(entityId: number, cast: boolean, receive: boolean): void
159
+ setCulling(entityId: number, culling: boolean): void
160
+ setCastShadows(entityId: number, culling: boolean): void
161
+ setReceiveShadows(entityId: number, culling: boolean): void
162
+
163
+ createTexture(systemId: number, onComplete: (id: number, width: number, height: number) => void, onReject: () => void): void
164
+ // Texture from a baked _creatorCanvas surface (RGBA8, already rasterized — synchronous, no decode).
165
+ createTextureFromCanvas(surfaceId: number): number
166
+ updateTextureFromCanvas(texId: number, surfaceId: number): void
167
+ /** A texture from raw UBYTE pixels — `channels` 1–4, row-major, `width*height*channels` bytes, no
168
+ * mips; `srgb` selects the sRGB internal format for 3/4 channels (colour) vs linear (data — a
169
+ * terrain's control map). The bytes are copied. Returns 0xFFFFFFFF on failure. `updateTexturePixels`
170
+ * re-uploads a sub-rectangle with the creation channel count. Optional (Texture.fromPixels throws). */
171
+ createTexturePixels?(width: number, height: number, channels: number, data: Uint8Array, srgb: boolean): number
172
+ updateTexturePixels?(textureId: number, x: number, y: number, width: number, height: number, data: Uint8Array): void
173
+
174
+ createMaterial(systemId: number): number
175
+ createMaterialS(name: string): number
176
+ setMaterial(entityId: number, materialId: number, index: number): void
177
+ getMaterial(entityId: number, index: number): number
178
+
179
+ setUniformRgb(materialId: number, uniform: string, color: number): void
180
+ setUniformRgba(materialId: number, uniform: string, color: number): void
181
+ setUniformFloat(materialId: number, uniform: string, value: number): void
182
+ setUniformBoolean(materialId: number, uniform: string, value: boolean): void
183
+ setUniformTexture(materialId: number, uniform: string, systemId: number, wrapS: number, wrapT: number): void
184
+ setUniformArray(materialId: number, uniform: string, value: Float32Array): void
185
+ setUniformNull(materialId: number, uniform: string): void
186
+
187
+ attachCameraToAR(sceneId: number, cameraEntityId: number, onProjectionChange: (mat: Float32Array, fov: number) => void): void
188
+ getDisplaySize(): Float32Array
189
+
190
+ setVisible(entityId: number, visible: boolean): void
191
+ isVisible(entityId: number): boolean
192
+
193
+ createGlb(systemId: number, onComplete: (buff: number) => void, onReject: () => void): void
194
+ setGlbCulling(entityId: number, culling: boolean): void
195
+ /** Optional. True when the host refits a skinned model's frustum-culling bounds to its joints every
196
+ * frame (creator-gl animation/skinning.cpp), i.e. culling is safe for animated GLBs. Model.load
197
+ * defaults culling ON only where this returns true; absent / false = the always-draw default. */
198
+ skinnedCullingSupported?(): boolean
199
+ /** Optional. Level-of-detail override for a GLB instance (docs/lod-plan.md): `mesh` / `anim` are -1 for
200
+ * automatic (the engine picks by screen size and visibility) or a forced level 0..3. Backs Model.lod
201
+ * and Animator.lod; a host without it (no LOD pass) leaves everything at full detail. */
202
+ setLod?(entityId: number, mesh: number, anim: number): void
203
+
204
+ // Render-synced aspect update(dt) dispatch, called from inside render(). Early = before the physics
205
+ // step; late = after animations/particles, just before draw. Single-slot per phase (the SDK's Aspect
206
+ // dispatcher registers one callback per phase that iterates its ordered updater list).
207
+ setEarlyUpdate(cb: (dt: number) => void): void
208
+ setLateUpdate(cb: (dt: number) => void): void
209
+ /** FIXED phase - the SDK's `updateFixed`: once per physics substep with dt = 1/60 exactly, BEFORE
210
+ * that substep's Jolt step (move commands / velocities written here feed the same step). Under
211
+ * `setTimeScale` the NUMBER of substeps changes, never the dt; at most 4 per frame. `null` clears it -
212
+ * registered lazily, only once an aspect declares the phase (one flag check per substep otherwise).
213
+ * Optional: a host without it gets the SDK's own 1/60 accumulator inside the early phase. */
214
+ setFixedUpdate?(cb: ((dt: number) => void) | null): void
215
+ /** The engine's clock multiplier for physics / animators / particles (1 normal, 0 frozen) — the
216
+ * SDK's `Time.scale` / `Time.paused` pushed down so the sims stay in step with game code. The
217
+ * two aspect phases still receive the RAW wall-clock dt (the SDK scales it itself). Optional:
218
+ * a host without it keeps its sims at wall-clock speed. */
219
+ setTimeScale?(scale: number): void
220
+ // Physics contact/sensor events (drained after each step): a/b = entity ids, type 0 enter / 1 exit.
221
+ setOnPhysicsEvent(cb: (a: number, b: number, type: number) => void): void
222
+
223
+ // ---- Animation system (docs/animation-v2-plan.md): AnimationClip + Animator — THE skeletal animation
224
+ // path, evaluated by creator-anim on every host. A clip set is parsed straight from GLB bytes (tracks
225
+ // target bone NAMES, no entities); a model's embedded clips are registered by createGlb (getGlbClipSet,
226
+ // 0 = none). An Animator binds clips to one node hierarchy by name; the native evaluator runs the WHOLE
227
+ // per-frame loop (sources, transitions, blend spaces, one-shot hand-over, root motion) — the SDK only
228
+ // issues play/stop/blend calls and has no tick. Skinning is flushed AFTER the late phase so IK /
229
+ // procedural joint writes land in the skin.
230
+ // TRANSITIONS ARE INERTIAL, NOT CROSSFADES: a layer evaluates exactly ONE source (its one-shot, its
231
+ // loop, or nothing); a switch records the difference between the pose the layer SHOWED and the pose the
232
+ // new source shows and decays it away over `fade` seconds (halflife = 0.4 × fade). A replaced source is
233
+ // silent from that moment — it costs nothing, its weight is 0 at once, and it may be replaced again
234
+ // mid-transition (the offset is re-recorded from the displayed pose). `fade` 0 = cut.
235
+ loadClips(systemId: number, onComplete: (clipSetId: number) => void, onReject: (err: unknown) => void): void
236
+ // names = newline-joined track targets; data = [trackCount, duration (<=0 → max key time), (path 0 T/1 R/2 S,
237
+ // interp 0 linear/1 step/2 cubic, comps, keyCount, times…, values…)*]. Returns a one-clip set id (0 = bad).
238
+ createClipFromTracks(names: string, data: Float32Array): number
239
+ getClipSetInfo(clipSetId: number): { name: string, duration: number, trackCount: number }[]
240
+ // A sub-range [start, end] seconds of one clip as a NEW single-clip set (index 0), re-timed to 0;
241
+ // interior keys copied, boundary values interpolated in at the cut points; events re-timed. 0 = bad id/range.
242
+ sliceClip(clipSetId: number, clip: number, start: number, end: number): number
243
+ // Clip events on the CLIP: normalized times (sorted ascending). Every slot bound to the clip, in every
244
+ // animator, fires slot event type 4 + i on crossing event i. Empty = clear.
245
+ setClipEvents(clipSetId: number, clip: number, times: Float32Array): void
246
+ getGlbClipSet(entityId: number): number
247
+ destroyClipSet(clipSetId: number): void
248
+ animatorCreate(entityId: number): number // 0 = not a transform node
249
+ animatorDestroy(animatorId: number): void // leaves the skeleton in rest pose
250
+ // One (clip, layer) slot, silent until played / made a blend member. slot index or -1.
251
+ animatorBind(animatorId: number, clipSetId: number, clipIndex: number, layer: number): number
252
+ animatorBoundTracks(animatorId: number, slot: number): number
253
+ // The layer's LOOP: member slots + positions (dims 1: x per member, 2: x,y; one member = a plain looping
254
+ // clip). The members share ONE cycle clock (aligned by their foot contacts, not by normalized time) and
255
+ // animatorSetBlendValue picks the mix (1D linear between neighbours / 2D gradient band). Setting it
256
+ // takes the layer over from whatever it shows — the previous loop and any one-shot on it go silent — with
257
+ // a transition of `fade`. Empty = no loop. speed = the members' rate.
258
+ animatorSetBlend(animatorId: number, layer: number, dims: 1 | 2, slots: Uint16Array, positions: Float32Array, fade: number, speed: number): void
259
+ animatorSetBlendValue(animatorId: number, layer: number, x: number, y: number): void
260
+ // Play a slot as the layer's one-shot, transitioned in over fadeIn (0 = cut) from whatever the layer
261
+ // showed (playing a blend MEMBER brings the whole blend back instead). A non-looping one-shot hands the
262
+ // layer back to the loop at the end, the hand-over firing fadeOut seconds BEFORE the end and the return
263
+ // transition taking fadeOut; fadeOut < 0 = not given (cut back; on a loopless layer hold the last frame).
264
+ // restart = rewind even if already playing (a restart is a new source: it transitions from the pose shown).
265
+ animatorPlay(animatorId: number, slot: number, loop: boolean, speed: number, fadeIn: number, fadeOut: number, restart: boolean): void
266
+ // TURN WARP for the slot's current play (right after animatorPlay): the node's turn from the clip's baked heading is
267
+ // scaled to `radians` total; the pose keeps its own turn, the extra pivots about the planted foot. enabled false =
268
+ // the clip's own turn. Optional: older hosts lack it.
269
+ animatorSlotSetTurn?(animatorId: number, slot: number, radians: number, enabled: boolean): void
270
+ // Release over `fade` (a transition toward what is left — the loop, or the rest pose): one slot; a
271
+ // layer's one-shot + loop (slot -1); everything (layer -1).
272
+ animatorStop(animatorId: number, layer: number, slot: number, fade: number): void
273
+ animatorSeek(animatorId: number, slot: number, time: number): void // seeking a blend member moves the blend
274
+ animatorGetSlotTime(animatorId: number, slot: number): number
275
+ animatorGetSlotWeight(animatorId: number, slot: number): number // 1 = the layer's source, a loop member = its share, else 0
276
+ // Layer config: weight 0–1; additive = each slot's DELTA vs its clip's first frame on top of the layers
277
+ // below; maskRoot = bone name(s, '\n'-separated) whose subtrees the layer drives ("" = all).
278
+ animatorSetLayer(animatorId: number, layer: number, weight: number, additive: boolean, maskRoot: string): void
279
+ // ANTICIPATION for the layer's next source change: the transition starts with -amount x the new source's
280
+ // joint velocity (a wind-up against the coming motion), consumed by that switch. Optional: older hosts lack it.
281
+ animatorSetLayerAnticipation?(animatorId: number, layer: number, amount: number): void
282
+ animatorSetGlobal(animatorId: number, speed: number, paused: boolean): void
283
+ // Root motion: "" off, "*" auto (the shallowest joint a base-layer clip translates), else a bone name. The
284
+ // root's horizontal travel (animator-node frame) is stripped from the pose and, per apply: 0 accumulated
285
+ // only (animatorGetRootMotion copies + clears out[3]), 1 added to the node's transform, 2 fed to the
286
+ // CharacterController on the node or an ancestor as a world velocity (falls back to 1 without one).
287
+ /** `apply` bits 0–1: 0 accumulate only / 1 move the node / 2 feed the CharacterController; bit 2 (+4): the
288
+ * root joint's yaw about the node's up is root motion too (off the pose, onto the node the travel lands on). */
289
+ animatorSetRootMotion(animatorId: number, bone: string, apply: number): void
290
+ animatorGetRootMotion(animatorId: number, out: Float32Array): void
291
+ // Slot events: 0 completed (non-loop end) / 1 loop wrapped / 2 settled (no longer a source, after a stop
292
+ // or a replacement) / 3 HAND-OVER (a one-shot's return starts — for the SDK the clip is over; what the app
293
+ // starts in response takes the layer over instead) / 4+i clip event i. Single-slot (routed by animatorId).
294
+ setOnAnimatorEvent(callback: (animatorId: number, slot: number, type: number) => void): void
295
+ // ---- contacts, phase, root curves (docs/animation-v2-plan.md §2.5–2.6) ----
296
+ // Every clip bound to a skeleton is baked once against it: when each foot is planted, the gait phase
297
+ // φ(t) (0 at a left-foot-down, 0.5 at a right-foot-down, unwrapped over the clip; absent for a clip
298
+ // with no gait cycle) and the root's cumulative travel / yaw / speed. A controller asks these instead
299
+ // of shipping measured tables of its own.
300
+ // The feet: '\n'-joined bone names per side (foot[, toe/ball]); both "" = classify by name. Re-bakes.
301
+ animatorSetFeet(animatorId: number, left: string, right: string): void
302
+ // One curve of a slot's clip at `time` seconds (< 0 = the slot's clock now): which 0 φ (-1 = no gait)
303
+ // / 1 travel (m) / 2 yaw (rad, + = left) / 3 speed (m/s) / 4-5 unit travel direction x / z
304
+ // (model space, held through stills — integrate dir × d(travel) for the root's 2D path).
305
+ animatorSlotCurveAt(animatorId: number, slot: number, which: 0 | 1 | 2 | 3 | 4 | 5, time: number): number
306
+ // out ← [curve sample dt, total travel (m), mean speed (m/s), in-place flag]. False = unbound slot.
307
+ animatorSlotCurveInfo(animatorId: number, slot: number, out: Float32Array): boolean
308
+ animatorSlotTurn(animatorId: number, slot: number): number // the clip's total root yaw, rad
309
+ // The first time the clip has turned `yaw` radians — where a turn is entered by a body already
310
+ // that far into the same turn, so the two read as one move.
311
+ animatorSlotTimeAtTurn(animatorId: number, slot: number, yaw: number): number
312
+ // What the LAYER shows, as a cycle phase in [0, 1) — its loop's clock, or its one-shot's φ; -1 = none.
313
+ animatorLayerPhase(animatorId: number, layer: number): number
314
+ // Seek the slot to the first time whose φ ≡ phase (mod 1); a blend member moves its whole group.
315
+ animatorSeekPhase(animatorId: number, slot: number, phase: number): void
316
+ // How far the slot's pose is from `target`'s at the same cycle phase, metres (joint distance +
317
+ // the velocity difference over 0.1 s, the planted foot weighted most) — what handing over to that
318
+ // clip would hand the inertializer. Fills `out` at the curve rate, returns the sample count.
319
+ // align: 0 = compare at the same cycle phase, 1 = at the same time (two clips that both begin
320
+ // from standing have no shared cycle to line up on).
321
+ animatorSlotFit(animatorId: number, slot: number, target: number, align: 0 | 1, out: Float32Array): number
322
+ // The earliest time in the slot where that hand-over costs no more than `tolerance` metres;
323
+ // atContact snaps to the next foot-down at or after it. -1 = never that close.
324
+ animatorSlotExit(animatorId: number, slot: number, target: number, tolerance: number, atContact: boolean, align: 0 | 1): number
325
+ // Contact spans as (side 0 left / 1 right, from, to, atX, atY, atZ) sextuplets (seconds, model space);
326
+ // returns the span count, filling `out` up to its capacity.
327
+ animatorSlotContacts(animatorId: number, slot: number, out: Float32Array): number
328
+ // A CLIMBING clip's tread levels (model-space plant heights, sorted ascending): out[0] = the
329
+ // riser (median level spacing), out[1..] = the levels, filled up to out's capacity. Returns the
330
+ // level count — 0 for a flat clip.
331
+ animatorSlotTreads(animatorId: number, slot: number, out: Float32Array): number
332
+ // The clip's baked physics at `time` seconds (< 0 = the slot's current time), unit body mass,
333
+ // model space: out11 = COM position xyz, COM velocity xyz (= linear momentum per kg), angular
334
+ // momentum about the COM xyz, then per-foot support left/right (contact-gated, seesaw split,
335
+ // scaled by the vertical force proxy — > 1 on a landing, 0 in flight). 0 = no body segments
336
+ // classified on this skeleton.
337
+ animatorSlotPhysics(animatorId: number, slot: number, time: number, out: Float32Array): number
338
+ // The clip's MATCHING FEATURE ROW at `time` (37 floats, the clip's heading frame at that time —
339
+ // x lateral (+ left), y up, z forward): 0–5 feet positions (relative to the pelvis' ground
340
+ // point), 6–11 feet velocities, 12–14 pelvis velocity, 15 pelvis height, 16–18 COM velocity,
341
+ // 19–20 support L/R, 21–22 contact phase L/R, 23 yaw angular momentum, 24–31 the clip's own
342
+ // path 0.3/0.6/1.0/1.5 s ahead as (lateral, forward) pairs, 32–35 facing change at those
343
+ // horizons (rad, + = left), 36 cyclic flag. Returns 37, or 0 without a leg chain.
344
+ animatorSlotFeatures(animatorId: number, slot: number, time: number, out: Float32Array): number
345
+ // The calibrated knee HINGE AXIS of a side (0 left / 1 right): a unit vector in the thigh's local
346
+ // frame — a skeleton property, measured over every bound clip's knee rotation track. out8 = axis
347
+ // xyz, spread mean (rad), spread max (rad), measurement count, 0, 0. 0 = no leg / no knee motion.
348
+ animatorKneeAxis(animatorId: number, side: number, out: Float32Array): number
349
+ // The knee's bend plane of a slot's clip at `time` seconds (< 0 = the slot's current time),
350
+ // predicted from the hinge axis + the clip's own thigh rotation — continuous even where the leg
351
+ // is straight. out6 = pole xyz (unit, model space, toward the knee — a two-bone solver's bend
352
+ // direction), then the plane normal xyz. 0 = uncalibrated / no leg.
353
+ animatorSlotKneePole(animatorId: number, slot: number, side: number, time: number, out: Float32Array): number
354
+ // Step warp v2: stride scales the feet's travel-direction offsets from the hips (uniform through
355
+ // stance and swing), lift = metres ADDED to their height (swing-gated by the contact marks;
356
+ // 0 = neutral, negative = a shuffle; half of what it adds raises the pelvis, capped by the
357
+ // planted legs' remaining extension), pitchDeg rotates each foot about its lateral axis
358
+ // (+ = toes up), slopeDeg the invisible staircase (+ = ascending: foot heights follow the
359
+ // incline + the feet auto-pitch; the HOST climbs the body at tan(slope) × the stride-scaled
360
+ // travel, which holds each planted foot's world height constant on its tread). Solved in the
361
+ // calibrated knee hinge plane with a soft reach; the pelvis lowers by any leg's overreach
362
+ // (marks-weighted, spring-followed, zero when nothing overreaches). 1/0/0/0 = identity;
363
+ // on false = off.
364
+ animatorSetStepWarp(animatorId: number, on: boolean, stride: number, lift: number, pitchDeg: number, slopeDeg: number): void
365
+ // Footsteps: a contact that BEGAN this evaluation on what the base layer shows — side 0 left / 1 right,
366
+ // x/y/z = the foot's WORLD position at the plant. Fired after the frame's transform commit.
367
+ setOnAnimatorStep(callback: (animatorId: number, side: number, x: number, y: number, z: number) => void): void
368
+
369
+ // ---- drives + warping ----
370
+ // The character's world velocity this frame: the speed the stride warp fits the stride to and the
371
+ // direction the orientation warp turns the lower body toward.
372
+ animatorSetMotion(animatorId: number, vx: number, vy: number, vz: number): void
373
+ // Cumulative capsule travel (m) and yaw (rad, + = left) — what a slot's distance / angle drive reads.
374
+ animatorSetDriveInput(animatorId: number, distance: number, angle: number): void
375
+ // Drive a slot's clock by a quantity instead of time: 0 time / 1 distance / 2 angle. `entry` is the
376
+ // curve value that corresponds to NOW (a start 0; a stop with D metres left: total travel − D), so
377
+ // the clip is entered where it already agrees with the body. Stalls fall back to time.
378
+ animatorSetSlotDrive(animatorId: number, slot: number, mode: 0 | 1 | 2, entry: number): void
379
+ // [stride on, stride min, stride max, orient on, orient max°, orient time, min speed, pelvis drop]
380
+ animatorSetWarpParams(animatorId: number, params: Float32Array): void
381
+
382
+ // ---- feet: foot lock + ground IK (docs/animation-v2-plan.md §2.9) ----
383
+ // The feet stage runs in WORLD space inside the evaluation, after the clips are composited: a foot
384
+ // the shown clip calls planted is pinned where it landed and the leg re-solved to keep it there
385
+ // while the body moves on (the lock), and each foot is put on the ground the ENGINE probed under it,
386
+ // the pelvis lowered so the leg reaches (ground IK). The engine casts the probe rays itself against
387
+ // what a character can stand on (static + moving solids; never the character's own bodies, debris
388
+ // or sensors) and reads the CharacterController's ground state — nothing per frame from the SDK.
389
+ // [ik on, lock on, pelvis drop m, unlock distance m, lock-in s, lock-out s, pelvis spring s,
390
+ // align to ground normal 0..1, probe half-length m, detector max speed m/s, detector max height m]
391
+ // Optional: a host without it has no feet stage (the legs stay as animated).
392
+ animatorSetFeetParams?(animatorId: number, params: Float32Array): void
393
+ // One foot after this frame's evaluation, side 0 left / 1 right: out ← [locked, lock weight,
394
+ // anchor xyz, target xyz] (world) — a debug overlay's beam under the foot. False = no such foot.
395
+ animatorFootState?(animatorId: number, side: number, out: Float32Array): boolean
396
+
397
+ // ---- locomotion: the movement model + the clip selector, both engine-side ----
398
+ // The intent (`locoSetInput`) becomes a desired velocity; the simulated velocity springs toward it
399
+ // one fixed substep at a time (`locoStep`, given the capsule's place), and `locoUpdate` — called
400
+ // before the animators are evaluated — picks and drives what the base layer shows. The host moves
401
+ // the character with the velocity and facing it reads back: in displacement `code` the animation
402
+ // never moves the body, so "how fast am I" is an input to the animation, not an output of it.
403
+ locoCreate(animatorId: number): number
404
+ locoDestroy(loco: number): void
405
+ // [halflife walk, halflife run, halflife facing, speed walk, speed run, speed sprint, deadzone,
406
+ // turn min°, turn big°, blend, stop blend, start tap, resume, predict, displacement (0 code /
407
+ // 1 data / 2 hybrid), mode (0 rules / 1 matching), match interval, match blend, spin min°, turn
408
+ // rate cap (deg/s, 0 = uncapped), halflife braking, hybrid adjustment clamp (m/s)]
409
+ locoSetParams(loco: number, params: Float32Array): void
410
+ locoDefaults(out: Float32Array): void
411
+ // Register a bound slot: kind 0 idle / 1 gait / 2 start / 3 stop / 4 turn / 5 match / 6 spin
412
+ // (a turn on the spot); `angle` degrees (+ = left) for the starts / turns / spins, `speed` m/s for
413
+ // a gait (0 = the clip's own measured speed), `gait` the gait a transition belongs to (0 walk /
414
+ // 1 run / 2 sprint, -1 = any) — a walking body plays the walking starts, stops and turns.
415
+ locoSetEntry(loco: number, slot: number, kind: number, angle: number, speed: number, gait: number): void
416
+ locoClearSet(loco: number): void
417
+ // [dir x, dir z, magnitude 0–1, face x, face z, gait 0 walk / 1 run / 2 sprint] — the facing is
418
+ // independent of the movement: a released key with a heading still owed turns the body on the spot.
419
+ locoSetInput(loco: number, input: Float32Array): void
420
+ // One simulation step at the capsule's world place — from the fixed substep loop.
421
+ locoStep(loco: number, dt: number, px: number, py: number, pz: number): void
422
+ // Run the selector for this frame — before the animators are evaluated.
423
+ locoUpdate(loco: number, dt: number): void
424
+ // out ← [state (0 idle / 1 start / 2 move / 3 turn / 4 stop / 5 spin), speed, vel x/y/z, yaw (rad,
425
+ // 0 = +Z, + = toward +X), yaw rate, phase, state sequence, then 3 × (x, z, dir x, dir z) predicted
426
+ // at +0.2 / +0.4 / +0.7 s].
427
+ locoRead(loco: number, out: Float32Array): void
428
+ // Why the last STOP was the one played: one row of 9 floats per candidate weighed — [slot, entry
429
+ // time, metres it still travels, metres the body needs, seconds to its next foot-down, foot-downs
430
+ // left, flags (1 = its phase matched, 2 = at/after its first foot-down, 4 = played, 8 = entered
431
+ // ahead of that foot-down by its pose), score, pose distance from what showed (m, -1 = not
432
+ // measured)]. Returns the rows written. A debug read; absent on hosts predating it.
433
+ locoStopReport?(loco: number, out: Float32Array): number
434
+ // Build the motion-matching database over the registered set (selector mode 1); returns frames.
435
+ locoBuildDatabase(loco: number): number
436
+
437
+ /** Subtree AABB in the entity's OWN local space (its own transform excluded) as
438
+ * [minX,minY,minZ, maxX,maxY,maxZ] — zeros for an empty / not-yet-loaded subtree. What
439
+ * `Shape.fit()` measures. Optional: absent on hosts predating the binding. */
440
+ computeBoundingBox?(entityId: number): Float32Array
441
+ setColliderFromMesh(entityId: number, meshEntityId: number, form: number): void
442
+ setColliderBox(entityId: number, centerX: number, centerY: number, centerZ: number, sizeX: number, sizeY: number, sizeZ: number): void
443
+ setColliderSphere(entityId: number, centerX: number, centerY: number, centerZ: number, radius: number): void
444
+
445
+ // JoltPhysics. motionType: 0 static / 1 kinematic / 2 dynamic.
446
+ physicsHasSupport(): boolean
447
+ /** Lightmap bake (docs/lightmap-plan.md) — a dev-time tool compiled into the desktop host only;
448
+ * `lightmapBake` exists only where `lightmapHasSupport()` is true. Writes the raster (lightmap.png, an intermediate the CLI turns into lightmap.ktx2)
449
+ * — one per atlas PAGE (`lightmap_<n>.png`, up to `maxPages` of `size` before the texel coarsens) —, the point lights' baked
450
+ * irradiance (`lightmap-light[_<n>].png`, sRGB(E / lightScale), `lightRays` shadow rays toward a `lightRadius`-metre disc) when the
451
+ * scene holds point lights, lightmap.bake (rects, pages, the baked lights) and lightmap.volume (`volumeCell` > 0)
452
+ * into outDir (R = sun visibility, G = ambient occlusion) for the given static instances (GLB roots /
453
+ * Mesh entities; `keysJoined` = one key per id, '
454
+ '-joined). Synchronous; progress on stdout. */
455
+ lightmapHasSupport?(): boolean
456
+ /** Lightmap consumption (docs/lightmap-plan.md §3): the next createGlb takes lightmap.filamat (the material
457
+ * behind that instance id) instead of the ubershader and keeps TEXCOORD_1; UINT32_MAX clears. */
458
+ setNextGlbLightmapped?(materialInstanceId: number): void
459
+ /** The level-wide knobs every lightmap-material instance shares (applied to the ones that exist and to every one
460
+ * created afterwards): the sun-mask shadow math (ambientScale, sunStrength), how much baked AO applies (aoStrength,
461
+ * 1 = all) and the light atlas' / volume's physical irradiance per encoded 1.0 (lightScale; 0 = no baked lights).
462
+ * volumeLightScale = the light VOLUME's own scale (movers); omitted or < 0 = same as lightScale. lightGamma = the light
463
+ * store's curve, texel = sRGB(pow(E / lightScale, 1 / lightGamma)) — 1 (default) for the linear v2 store, 2 for the
464
+ * square-root store bakes write since 2026-08-31 (the .bake's `lightGamma`). */
465
+ lightmapSetOptions?(ambientScale: number, sunStrength: number, aoStrength: number, lightScale: number, volumeLightScale?: number, lightGamma?: number): void
466
+ /** Bind the atlas page + this instance's rect (uv1 * [sx, sy] + [ox, oy]) and, unless 0xFFFFFFFF, the matching page of
467
+ * the light atlas; the sun / IBL terms come from filament's per-frame uniforms inside the shader. Returns how many
468
+ * material instances took it (0 = not loaded lightmapped). */
469
+ lightmapApply?(entityId: number, textureId: number, sx: number, sy: number, ox: number, oy: number, lightTextureId: number): number
470
+ /** Shadow flags on every renderable of a GLB instance (Mesh has setCastShadows/setReceiveShadows). */
471
+ setGlbShadows?(entityId: number, cast: boolean, receive: boolean): void
472
+ lightmapBake?(outDir: string, entityIds: Uint32Array, keysJoined: string, size: number, texel: number, sunRays: number, aoRays: number, aoDistance: number, bias: number, sunAngleDeg: number, volumeCell: number, maxPages: number, lightRays: number, lightRadius: number, bounceAlbedo?: number, bounceDistance?: number, bounceRays?: number, coplanarSkip?: number, denoise?: number): boolean
473
+ /** Light VOLUME for dynamic objects (docs/lightmap-plan.md §9). `lightmapVolumeLoad` turns a fetched
474
+ * lightmap.volume (`fetchSystemId` from `_creatorUtils.fetch`) into RGBA8 3D textures and returns its
475
+ * header as JSON — `{"texture":id,"dims":[nx,ny,nz],"min":[x,y,z],"cell":m,"light"?:id,"faces"?:n}` (`light` = the
476
+ * point lights' block: a version-3 volume's six ambient-cube faces (+X −X +Y −Y +Z −Z, `faces` 6), a version-2
477
+ * volume's one directionless value (`faces` 1) — uploaded either way as six face slabs stacked along z, so the
478
+ * light texture's depth is nz × 6) — or "" on failure. */
479
+ lightmapVolumeLoad?(fetchSystemId: number): string
480
+ /** Make those textures THE volume (`textureId` 0xFFFFFFFF clears; `lightTextureId` 0xFFFFFFFF = no baked lights):
481
+ * every lightmap-material instance without an atlas rect — loaded already or later — samples it at the pixel's
482
+ * world position (+ half a `cell` along the normal). `size` = dims × cell. Returns how many instances switched. */
483
+ lightmapVolumeSet?(textureId: number, lightTextureId: number, minX: number, minY: number, minZ: number, sizeX: number, sizeY: number, sizeZ: number, cell: number): number
484
+ physicsConfigure(gx: number, gy: number, gz: number, maxBodies: number): void
485
+ setInterpolation(enabled: boolean): void
486
+ // Shape/Physics/Trigger aspects: build a shape once, create bodies from it.
487
+ physicsBuildBox(hx: number, hy: number, hz: number): number
488
+ physicsBuildSphere(radius: number): number
489
+ physicsBuildCylinder(halfHeight: number, radius: number): number
490
+ physicsBuildCapsule(halfHeight: number, radius: number): number
491
+ /** Mesh shape from node-local triangles. convex=false → triangle mesh (static/kinematic/pick/character
492
+ * only; physicsCreateBody returns 0 for a dynamic one), convex=true → convex hull (any motion).
493
+ * (sx,sy,sz) = world scale, applied natively. Returns 0 if the shape can't be built. */
494
+ physicsBuildMesh(vertices: Float32Array, indices: Uint32Array, convex: boolean, sx: number, sy: number, sz: number): number
495
+ /** Same from a loaded GLB root (non-skinned primitives, bind pose, baked sub-node transforms; cached per asset). */
496
+ physicsBuildMeshFromEntity(entityId: number, convex: boolean, sx: number, sy: number, sz: number): number
497
+ /** Terrain collider (Shape { heightfield: true }, docs/terrain-plan.md §1.4): a Jolt HeightFieldShape over
498
+ * the grid `terrainCreate` draws (same arrays, same hole rule). Static / kinematic / pick / character
499
+ * ground only. (sx,sy,sz) = world scale. `physicsUpdateHeightField` rewrites a sample rectangle in
500
+ * place from the FULL arrays (live bodies keep the shape); heights beyond the range chosen at build
501
+ * time (25 % headroom) clamp — the SDK rebuilds the shape when an edit leaves that range. Optional. */
502
+ physicsBuildHeightField?(heights: Float32Array, sizeX: number, sizeZ: number, cellSize: number, holes: Uint8Array | null, sx: number, sy: number, sz: number): number
503
+ physicsUpdateHeightField?(shapeId: number, heights: Float32Array, sizeX: number, sizeZ: number, x0: number, z0: number, w: number, h: number, holes: Uint8Array | null): void
504
+ /** A loaded GLB root's triangle soup — the one `physicsBuildMeshFromEntity` collides — as 9 floats per
505
+ * triangle in the asset root's space (empty when the entity is not a GLB). `Terrain.conform` stamps a
506
+ * road model into the ground with it. Optional. */
507
+ glbTriangles?(entityId: number): Float32Array
508
+ /** Offset a built shape's centre from the node's origin (Shape `origin`) — world units in the
509
+ * body's rotated, UNSCALED frame, sitting outside a mesh shape's scale wrapper. Sets rather
510
+ * than accumulates; (0,0,0) clears it. Optional: an older host just centres on the node. */
511
+ physicsSetShapeOrigin?(shapeId: number, x: number, y: number, z: number): void
512
+ /** Swap a live body's shape, keeping its id, velocity and transform (Shape.fit / a re-attach).
513
+ * updateMass recomputes the inertia tensor. Refuses a triangle mesh on a dynamic body. */
514
+ physicsSetBodyShape?(bodyId: number, shapeId: number, updateMass: boolean): void
515
+ physicsDestroyShape(shapeId: number): void
516
+ /** sensor = trigger (overlap events, no response); pickOnly = raycast-only, non-colliding body. */
517
+ physicsCreateBody(entityId: number, shapeId: number, motion: number, mass: number, sensor: boolean, pickOnly: boolean): number
518
+ physicsSetPickable(bodyId: number, pickable: boolean): void
519
+ /** Surface friction of one body (0 = ice, ~1 = grippy asphalt). Values COMBINE as sqrt(a * b), so
520
+ * a low value on either side dominates. New bodies start at 0.6 — a neutral solid surface.
521
+ * The vehicle wheel cast reads the GROUND body's value — this is what caps a car's cornering. */
522
+ physicsSetFriction(bodyId: number, friction: number): void
523
+ physicsGetFriction(bodyId: number): number
524
+ /** Ray vs pickable bodies → hit entity id (0 = miss); fills `out` = [px,py,pz,nx,ny,nz,fraction]. */
525
+ physicsRaycast(ox: number, oy: number, oz: number, dx: number, dy: number, dz: number, maxDist: number, out?: Float32Array): number
526
+ /** Dev-time dump of the static collision geometry (docs/navmesh-plan.md §4) — the input of
527
+ * `lecodes navmesh bake`: every STATIC solid body's triangles in world space, as an NGEO file at
528
+ * `outPath`. `entityIds`/`areas` are per-entity overrides (area 0..15, 255 unwalkable, 254 skip).
529
+ * Returns the triangle count (-1 = failed). Desktop hosts with physics only. */
530
+ physicsStaticGeometry?(outPath: string, entityIds: Uint32Array, areas: Uint8Array): number
531
+ // CharacterController (Jolt CharacterVirtual). The character steps on the engine's FIXED clock like
532
+ // every body (frame-rate independent) and is render-interpolated; the engine owns its gravity, so
533
+ // the SDK never ticks it. Both velocity halves are LATCHED STATE, never one-shot events — JS runs
534
+ // once per frame while the sim runs 0..4 sub-steps, so a one-shot would double-apply or vanish.
535
+ // groundState: 0 OnGround / 1 OnSteepGround / 2 NotSupported / 3 InAir.
536
+ characterCreate(entityId: number, shapeId: number, maxSlopeDeg: number): number
537
+ characterDestroy(charId: number): void
538
+ /** This frame's HORIZONTAL command (world units/s), cleared once a step consumes it — no command
539
+ * means standing still, not coasting. Held across the frame's sub-steps, and it takes the axis
540
+ * back from a latched velocity: the last writer owns X/Z. */
541
+ characterMove(charId: number, x: number, z: number): void
542
+ /** The same command including the vertical — free mode (gravityScale 0): swimming / flying. */
543
+ characterMoveFree(charId: number, x: number, y: number, z: number): void
544
+ /** Seed the LATCHED ballistic vertical (jump / dash). No ground check. */
545
+ characterSetVerticalVelocity(charId: number, vy: number): void
546
+ /** Latch the whole velocity — it persists until a characterMove takes the axis back (knockback,
547
+ * wall jump, launch pad, weightless flight). Gravity still acts on the vertical. */
548
+ characterSetVelocity(charId: number, x: number, y: number, z: number): void
549
+ /** Multiplier over the world gravity; 0 = free mode, which ALSO disables stick-to-floor + stairs. */
550
+ characterSetGravityScale(charId: number, scale: number): void
551
+ characterSetMaxSlope(charId: number, maxSlopeDeg: number): void
552
+ /** Swap the collider live (crouch / stand up), keeping the FEET planted. Returns false when the new
553
+ * shape doesn't fit where the character stands — nothing changed, so the caller retries later and
554
+ * that retry is an exact headroom test. Optional: a host without it can't resize a character. */
555
+ characterSetShape?(charId: number, shapeId: number): boolean
556
+ /** The velocity the solver ENDED UP with after the last step (post-collision), not the command. */
557
+ characterGetVelocity(charId: number, out: Float32Array): void
558
+ characterGetGroundState(charId: number): number
559
+ /** Discontinuous move (spawn / respawn / teleport) — also resets the interpolation pair. */
560
+ characterSetPosition(charId: number, x: number, y: number, z: number): void
561
+ // Vehicle (Jolt VehicleConstraint + WheeledVehicleController). One settings BLOB, so tuning knobs
562
+ // never grow this ABI — layout (floats):
563
+ // header[21]: version(5), mass, comAuto, comY,
564
+ // engTorque, engMaxRpm, engIdleRpm, engInertia, engBraking, clutchStrength,
565
+ // diffRatio (<= 0 = a fully OPEN differential),
566
+ // antiRoll (the bar's stiffness as a FRACTION of the wheel spring; 0 = no bars),
567
+ // maxTiltDeg,
568
+ // steerLockDeg, steerAtSpeedDeg, steerSpeedMs, steerRateDeg (speed-sensitive
569
+ // steering: the lock falls to steerAtSpeedDeg by steerSpeedMs and the wheels turn
570
+ // no faster than steerRateDeg per second; 0/0/0 = the raw lock, instantly),
571
+ // aeroDownforce, aeroDrag (each a fraction of the car's own WEIGHT at 30 m/s, scaled
572
+ // by v² from there; 0/0 = no aero, Jolt's own behaviour),
573
+ // wheelCount, curveCount
574
+ // + curveCount * 2: the engine's normalized torque curve (x = rpm/maxRpm, y = torque/maxTorque);
575
+ // 0 points keeps Jolt's default
576
+ // + wheelCount * 15: px, py, pz, radius, width, maxSteerDeg, drive, brakeTorque, handBrakeTorque,
577
+ // axle, travel, stiffness, damping, grip, tireCurve (0 = road, 1 = arcade)
578
+ // NO gear list and no wheel node ids: the gearbox is the SDK's (see vehicleSetTransmission) and the
579
+ // SDK poses the wheel nodes itself. Chassis space is forward -Z / up +Y (matching node.forward);
580
+ // wheel positions are suspension attachment points in unscaled chassis space; `axle` pairs wheels
581
+ // for the differentials + anti-roll bars.
582
+ vehicleCreate(entityId: number, shapeId: number, settings: Float32Array): number
583
+ vehicleDestroy(vehicleId: number): void
584
+ /** forward/right in [-1,1], brake/handBrake in [0,1]. Sticky; any non-zero input wakes the car. */
585
+ vehicleSetInput(vehicleId: number, forward: number, right: number, brake: number, handBrake: number): void
586
+ /** The SDK's gearbox, latched and applied once per fixed step: `ratio` is the ONE ratio the car is
587
+ * running (0 = neutral, negative = reverse — the engine never sees a gear list) and `clutch` is
588
+ * the 0..1 shift envelope scaling the clutch in the coupled engine/wheel solve. */
589
+ vehicleSetTransmission(vehicleId: number, ratio: number, clutch: number): void
590
+ /** Re-apply the TUNABLE half of the blob (same layout) to a live car — differential, per-wheel grip,
591
+ * engine torque/RPM/curve, clutch strength, steering lock/taper/rate, brake torques, tilt limit.
592
+ * Structural values (mass, centre of mass, wheel geometry, driven wheels, suspension, anti-roll)
593
+ * are ignored: those need a re-create. */
594
+ vehicleSetTuning?(vehicleId: number, settings: Float32Array): void
595
+ /** out = [speed, rpm, wheelsInContact, vx, vy, vz] + per wheel
596
+ * [contact, slipLong, slipAngleDeg, suspensionLength, steerDeg, spin]. The last three per wheel are
597
+ * its VISUAL POSE — the SDK's Wheel aspect builds the node transform from them. */
598
+ vehicleGetState(vehicleId: number, out: Float32Array): void
599
+ /** Teleport upright and clear all motion (velocities, engine RPM, gear, wheel spin). */
600
+ vehicleReset(vehicleId: number, x: number, y: number, z: number, qx: number, qy: number, qz: number, qw: number): void
601
+ /** The chassis rigid body, for the plain body calls (physicsApplyImpulse, …). 0 if unknown. */
602
+ vehicleBodyId(vehicleId: number): number
603
+ // Ragdoll (Jolt Ragdoll): a Model's skeleton handed to physics — one dynamic body per listed bone
604
+ // (a capsule from the bone's origin to the next joint) joined to its parent part by a swing-twist
605
+ // constraint. Built ONCE from the pose the bones are in (that pose is the joints' neutral for the
606
+ // limits) and kept out of the world until ragdollActivate, which takes the bones' CURRENT pose,
607
+ // adds the bodies and gives every body one velocity; from then on the engine writes the bodies'
608
+ // poses onto the bone entities every frame AFTER the animator (bones not listed keep their
609
+ // animated local pose under the ragdolled parents — fingers, toes, a spine bone between two
610
+ // parts), until ragdollDeactivate. Every body carries its bone entity as user data, so
611
+ // physicsRaycast / contact events name the bone. Optional: a host without it has no ragdolls.
612
+ // bones[partCount * 2]: bone entity, `to` entity (0 = a leaf: `length` along the parent's line;
613
+ // a leaf ROOT points along the model's up)
614
+ // settings — header[12]: version(2), partCount, stride(12), friction, linearDamping,
615
+ // angularDamping (0 = Jolt's own), collide (0 = everything, 1 = the STATIC world and
616
+ // other such ragdolls only: dynamic bodies, character controllers and vehicles pass
617
+ // through — Jolt's DEBRIS object/broadphase layer), freeze (1 = once every part is
618
+ // asleep the bodies turn STATIC where they lie: the last pose keeps being written
619
+ // onto the bones, raycasts still hit, nothing wakes them, the solver skips them;
620
+ // ragdollActive reads false, ragdollDeactivate / ragdollActivate still work —
621
+ // activate makes the parts dynamic again), freezeAfter (seconds in the world after which the freeze happens
622
+ // regardless; 0 = no cap), reserved x3. A version-1 blob (header[8], stops after
623
+ // angularDamping) is still accepted = collide everything, never freeze.
624
+ // + partCount * 12: parentIndex (-1 = the root; always < the part's own index), radius, length
625
+ // (0 = up to the `to` bone), mass (kg; 0 = from the volume), swingDeg (cone
626
+ // half-angle), twistDeg (half-angle), hinge (0/1), hingeAxisX/Y/Z (in the MODEL
627
+ // node's space), hingeMinDeg, hingeMaxDeg — a hinge is a swing-twist whose cone is
628
+ // flat (2°) across the axis and [min, max] about it (a knee, an elbow)
629
+ ragdollCreate?(rootEntityId: number, bones: Uint32Array, settings: Float32Array): number
630
+ ragdollDestroy?(ragdollId: number): void
631
+ /** Pose the bodies from the bones' current transforms, add them to the world, set every body's
632
+ * linear velocity. Already active = re-wake + velocity. false = unknown id / no world. */
633
+ ragdollActivate?(ragdollId: number, vx: number, vy: number, vz: number): boolean
634
+ /** Take the bodies out of the world; the animator owns the bones again from the next frame. */
635
+ ragdollDeactivate?(ragdollId: number): void
636
+ /** In the world AND at least one body still awake (a settled ragdoll reads false). */
637
+ ragdollActive?(ragdollId: number): boolean
638
+ /** The rigid body of part `index` (for physicsApplyImpulseAt / velocities). 0 if unknown. */
639
+ ragdollBodyId?(ragdollId: number, index: number): number
640
+ // Legacy coupled shape+body (still used by the worker RigidBody).
641
+ physicsCreateBox(entityId: number, hx: number, hy: number, hz: number, motionType: number, mass: number): number
642
+ physicsCreateSphere(entityId: number, radius: number, motionType: number, mass: number): number
643
+ physicsCreateCylinder(entityId: number, halfHeight: number, radius: number, motionType: number, mass: number): number
644
+ physicsSetLinearVelocity(bodyId: number, x: number, y: number, z: number): void
645
+ physicsGetLinearVelocity(bodyId: number, out: Float32Array): void
646
+ /** Angular velocity about each world axis, RADIANS/second — Jolt's unit; the SDK exposes degrees.
647
+ * The only way to stop a spin: a position write leaves both velocities untouched. */
648
+ physicsSetAngularVelocity?(bodyId: number, x: number, y: number, z: number): void
649
+ physicsGetAngularVelocity?(bodyId: number, out: Float32Array): void
650
+ physicsApplyImpulse(bodyId: number, x: number, y: number, z: number): void
651
+ physicsApplyImpulseAt?(bodyId: number, x: number, y: number, z: number, px: number, py: number, pz: number): void
652
+ physicsSetBodyPosition(bodyId: number, x: number, y: number, z: number): void
653
+ /** The rotation twin (normalized host-side). Both snap the body's render-interpolation pair, so a
654
+ * discontinuous move is drawn as one rather than as a one-frame slide/spin across the gap. */
655
+ physicsSetBodyRotation?(bodyId: number, qx: number, qy: number, qz: number, qw: number): void
656
+ physicsRemoveBody(bodyId: number): void
657
+
658
+ createMediaPlayerTexture(id: number): number
659
+
660
+ getName(entityId: number): string
661
+ setName(entityId: number, name: string): void
662
+
663
+ hasMesh(entityId: number): boolean
664
+
665
+ createARController(sceneId: number, cameraId: number, mode: string, onTrack: (entityId: number, track: boolean) => void): void
666
+ createRootAnchor(sceneId: number): number
667
+ createAnchor(sceneId: number, physicalWidth: number, systemId: number): number
668
+
669
+ // Particles. All emitter/curve config crosses as ONE Float32Array of [tag, payloadLen,
670
+ // ...payload] records, parsed once in creator-gl (PsTag in creator-gl/src/particles.h; the
671
+ // SDK mirror is in gl/Particles.ts, guarded by sdk/tests/particles-tags.test.ts). Unknown
672
+ // tags skip by length. maxParticles 0 = default (1000).
673
+ createParticleSystem(entityId: number, materialInstanceId: number, maxParticles: number): void
674
+ spawnParticles(entityId: number, count: number): void
675
+ setParticleSystemConfig(entityId: number, data: Float32Array): void
676
+
677
+ createNoise(): number
678
+ setNoiseFrequency(noiseId: number, frequency: number): void
679
+ setNoiseOctaves(noiseId: number, octaves: number): void
680
+ setNoiseFractalLunacrity(noiseId: number, lunacrity: number): void
681
+ setNoiseFractalGain(noiseId: number, fractalGain: number): void
682
+ getNoise2D(noiseId: number, x: number, y: number): number
683
+ getNoise3D(noiseId: number, x: number, y: number, z: number): number
684
+
685
+ captureImage(sceneId: number, onComplete: (bufferId: number, name: string, size: number) => void, onReject: () => void): void
686
+ }
687
+
688
+ // ---- 2D engine (sokol / creator-2d) ----------------------------------------------------------
689
+ // A 2D-only project loads ONLY creator2d.wasm — never Filament.
690
+ var _creator2d: {
691
+ backend: string
692
+ version(): number
693
+ render(nowMs: number): void
694
+ onUpdate(cb: (dt: number) => void): void
695
+ offUpdate(cb: (dt: number) => void): void
696
+ // Render-synced aspect update(dt) dispatch, called from inside c2dRender. Early = before the
697
+ // physics step; late = after animations, just before draw. Single-slot per phase (the SDK's
698
+ // Aspect dispatcher registers one callback that iterates its ordered updater list).
699
+ setEarlyUpdate(cb: (dt: number) => void): void
700
+ setLateUpdate(cb: (dt: number) => void): void
701
+ /** Clock multiplier for the 2D physics step + sprite animations (see `_creator.setTimeScale`). */
702
+ setTimeScale?(scale: number): void
703
+ setOnAnimEvent(cb: (ev: { entityId: number, clipId: number, type: number }) => void): void
704
+ // Raw scene pointer events from the host (canvas / iOS view). phase: 0 down, 1 move, 2 up, 3 cancel;
705
+ // x,y in logical (CSS px / iOS point) coords relative to the surface. The SDK hit-tests + tracks.
706
+ setOnPointer(cb: (phase: number, pointerId: number, x: number, y: number) => void): void
707
+ setClearColor(r: number, g: number, b: number, a: number): void
708
+
709
+ createScene(): number
710
+ destroyScene(sceneId: number): void
711
+ openScene(sceneId: number): void
712
+ closeScene(): void
713
+ sceneSetClearColor(sceneId: number, r: number, g: number, b: number, a: number): void
714
+ addEntityToScene(sceneId: number, entityId: number): void
715
+ removeEntityFromScene(sceneId: number, entityId: number): void
716
+ setLayerYSort(sceneId: number, layer: number, enabled: boolean): void
717
+
718
+ cameraSetPosition(sceneId: number, x: number, y: number): void
719
+ cameraSetZoom(sceneId: number, zoom: number): void
720
+ cameraSetRotation(sceneId: number, deg: number): void
721
+ cameraScreenToWorld(sceneId: number, sx: number, sy: number): Float32Array
722
+ cameraWorldToScreen(sceneId: number, wx: number, wy: number): Float32Array
723
+
724
+ createEntity(): number
725
+ destroyEntity(entityId: number): void
726
+ setPosition(entityId: number, x: number, y: number): void
727
+ setRotation(entityId: number, deg: number): void
728
+ setScale(entityId: number, sx: number, sy: number): void
729
+ setLayer(entityId: number, layer: number): void
730
+ setZ(entityId: number, z: number): void
731
+ setVisible(entityId: number, visible: boolean): void
732
+ getPosition(entityId: number): Float32Array
733
+
734
+ setParent(childId: number, parentId: number, keepWorld: boolean): void
735
+ getParent(entityId: number): number
736
+ getChildCount(entityId: number): number
737
+ getChild(entityId: number, index: number): number
738
+ getWorldPosition(entityId: number): Float32Array
739
+ getWorldMatrix(entityId: number): Float32Array
740
+ worldToLocal(entityId: number, wx: number, wy: number): Float32Array
741
+ localToWorld(entityId: number, lx: number, ly: number): Float32Array
742
+ getLocalTransform(entityId: number): Float32Array
743
+
744
+ setSprite(entityId: number, textureId: number): void
745
+ setSpriteFrame(entityId: number, u0: number, v0: number, u1: number, v1: number): void
746
+ setSpriteFramePx(entityId: number, px: number, py: number, pw: number, ph: number): void
747
+ setSpriteSize(entityId: number, w: number, h: number): void
748
+ setSpriteAnchor(entityId: number, ax: number, ay: number): void
749
+ setSpriteColor(entityId: number, r: number, g: number, b: number): void
750
+ setSpriteOpacity(entityId: number, a: number): void
751
+ setSpriteFlip(entityId: number, flipX: boolean, flipY: boolean): void
752
+
753
+ createTexture(systemId: number, onComplete: (texId: number, w: number, h: number) => void, onReject: (e: any) => void): void
754
+ getTextureWidth(texId: number): number
755
+ getTextureHeight(texId: number): number
756
+ destroyTexture(texId: number): void
757
+ setDefaultFilter(linear: boolean): void
758
+
759
+ // Textures from a _creatorCanvas surface (RGBA8, already rasterized — no decode, so synchronous).
760
+ // See docs/canvas-contract.md.
761
+ createTextureFromCanvas(surfaceId: number): number
762
+ updateTextureFromCanvas(texId: number, surfaceId: number): void
763
+
764
+ defineAnimation(entityId: number, frames: Float32Array, fps: number, loop: boolean): number
765
+ playAnimation(entityId: number, clipId: number): void
766
+ stopAnimation(entityId: number): void
767
+ setAnimationSpeed(entityId: number, speed: number): void
768
+ getAnimationFrame(entityId: number): number
769
+
770
+ createTilemap(textureId: number, cols: number, rows: number, tileW: number, tileH: number, atlasCols: number, atlasRows: number, data: Int32Array): number
771
+ setTile(tilemapId: number, x: number, y: number, index: number): void
772
+
773
+ bulkSetPositions(ids: Uint32Array, xy: Float32Array, count: number): void
774
+
775
+ // physics (Box2D v3; present only in CREATOR_2D_PHYSICS builds — physicsHasSupport() reports it).
776
+ // See docs/2d-physics-plan.md. physicsHasSupport() reports the BUILD, not the world: the world is
777
+ // created lazily by the first physicsCreateBody, so configure() is optional.
778
+ physicsHasSupport(): boolean
779
+ /** Live and non-destructive: an existing world keeps its bodies and takes the new gravity.
780
+ * pixelsPerMeter only applies to a world that doesn't exist yet (global Box2D tolerance). */
781
+ physicsConfigure(gx: number, gy: number, pixelsPerMeter: number, subStepCount: number): void
782
+ setInterpolation(enabled: boolean): void
783
+ /** type 0 contactBegin / 1 contactEnd / 2 sensorBegin / 3 sensorEnd. A contact BEGIN carries the
784
+ * manifold — world point, normal pointing A→B, and the approach speed at impact; the other three
785
+ * carry zeros. (The begin manifold is pre-solve, so its impulses are all zero — `speed` is the
786
+ * pre-solve relative normal velocity, which is the number an impact actually wants.) */
787
+ setOnPhysicsEvent(cb: (entityA: number, entityB: number, type: number, px: number, py: number, nx: number, ny: number, speed: number) => void): void
788
+ physicsCreateBody(entityId: number, motionType: number): number
789
+ physicsRemoveBody(bodyHandle: number): void
790
+ // Shapes carry no `density` (built at density 1, so mass == area — physicsSetMass overrides) and
791
+ // do carry the collision filter. category/mask are 32-bit; 0 means "everything".
792
+ physicsAddBox(bodyHandle: number, hw: number, hh: number, ox: number, oy: number, friction: number, bounce: number, isSensor: boolean, category: number, mask: number): void
793
+ physicsAddCircle(bodyHandle: number, radius: number, ox: number, oy: number, friction: number, bounce: number, isSensor: boolean, category: number, mask: number): void
794
+ physicsAddCapsule(bodyHandle: number, x1: number, y1: number, x2: number, y2: number, radius: number, friction: number, bounce: number, isSensor: boolean, category: number, mask: number): void
795
+ physicsAddSegment(bodyHandle: number, x1: number, y1: number, x2: number, y2: number, friction: number, bounce: number, category: number, mask: number): void
796
+ physicsAddPolygon(bodyHandle: number, pts: Float32Array, count: number, friction: number, bounce: number, isSensor: boolean, category: number, mask: number): void
797
+ /** A polyline of connected segments — long CONCAVE surfaces in one seam-free piece. STATIC bodies
798
+ * only, >= 4 points; the SDK synthesises the tangent points an open chain needs, because Box2D's
799
+ * first and last edges do not collide. One-sided: solid on the right of the point order. */
800
+ physicsAddChain(bodyHandle: number, pts: Float32Array, count: number, isLoop: boolean, friction: number, bounce: number, category: number, mask: number): void
801
+ /** Destroy every shape on a body, keeping the body (id, transform, velocity) — the first half of a
802
+ * live collider rebuild. */
803
+ physicsClearShapes(bodyHandle: number): void
804
+ /** Contact events are OFF per body until this turns them on — otherwise every crate-on-crate pair
805
+ * crosses the bridge every frame. The SDK enables it on the first 'enter'/'exit' listener. */
806
+ physicsSetContactEvents(bodyHandle: number, enabled: boolean): void
807
+ physicsSetLinearVelocity(bodyHandle: number, x: number, y: number): void
808
+ physicsGetLinearVelocity(bodyHandle: number): Float32Array
809
+ physicsSetAngularVelocity(bodyHandle: number, degPerSec: number): void
810
+ physicsGetAngularVelocity(bodyHandle: number): number
811
+ physicsApplyLinearImpulse(bodyHandle: number, x: number, y: number): void
812
+ /** Impulse at a WORLD point — the lever arm becomes angular impulse (a central impulse never
813
+ * spins a body). Mirrors 3D physicsApplyImpulseAt. */
814
+ physicsApplyImpulseAt(bodyHandle: number, x: number, y: number, px: number, py: number): void
815
+ physicsApplyForce(bodyHandle: number, x: number, y: number): void
816
+ /** Teleport the body to its ENTITY's current world transform — how node.position / .rotation reach
817
+ * it. The parent-chain math stays native instead of being re-derived in TS. Snaps prev/cur so the
818
+ * renderer doesn't interpolate across the jump; the body KEEPS its velocity. */
819
+ physicsSetFromEntity(bodyHandle: number): void
820
+ physicsSetFixedRotation(bodyHandle: number, enabled: boolean): void
821
+ physicsSetGravityScale(bodyHandle: number, scale: number): void
822
+ physicsSetLinearDamping(bodyHandle: number, damping: number): void
823
+ physicsSetAngularDamping(bodyHandle: number, damping: number): void
824
+ physicsSetBullet(bodyHandle: number, enabled: boolean): void
825
+ physicsSetEnabled(bodyHandle: number, enabled: boolean): void
826
+ physicsSetAwake(bodyHandle: number, awake: boolean): void
827
+ /** Scales the shape-derived mass data, so the rotational inertia keeps its ratio. <= 0 restores
828
+ * the area-derived default. */
829
+ physicsSetMass(bodyHandle: number, mass: number): void
830
+ physicsGetMass(bodyHandle: number): number
831
+ physicsSetMotionType(bodyHandle: number, motionType: number): void
832
+ physicsSetFriction(bodyHandle: number, friction: number): void
833
+ physicsSetBounce(bodyHandle: number, bounce: number): void
834
+ physicsSetFilter(bodyHandle: number, category: number, mask: number): void
835
+ // Queries. `mask` = the union of group categories the query may hit (0 = everything); `ignore` is
836
+ // a list of ENTITY ids to skip, passed as floats because ids are small ints and that reuses the
837
+ // one buffer helper. Box2D's closest-ray API has no exclusion, hence the explicit list.
838
+ physicsRaycastClosest(x0: number, y0: number, x1: number, y1: number, mask: number, ignore: Float32Array, ignoreCount: number): Float32Array
839
+ /** All hits along the segment, nearest first: maxHits records of [entityId, px, py, nx, ny, fraction]. */
840
+ physicsRaycastAll(x0: number, y0: number, x1: number, y1: number, mask: number, ignore: Float32Array, ignoreCount: number, maxHits: number): Float32Array
841
+ /** Overlap an arbitrary convex shape given as a point cloud + radius (Box2D's b2ShapeProxy form),
842
+ * so one call covers circle / capsule / box / polygon — and the swept test for a moving circle.
843
+ * Returns up to maxHits entity ids. */
844
+ physicsOverlap(pts: Float32Array, count: number, radius: number, mask: number, ignore: Float32Array, ignoreCount: number, maxHits: number): Float32Array
845
+ /** Topmost by DRAW order (layer, then z) — not whatever the broadphase hands back first. */
846
+ physicsOverlapPoint(x: number, y: number, mask: number, ignore: Float32Array, ignoreCount: number): number
847
+ // CharacterController2D (Box2D's kinematic mover). The character steps on the engine's FIXED clock
848
+ // inside the same loop as the bodies, so the SDK does no per-frame work. Capsule ends are in the
849
+ // character's own frame. It also owns a hidden kinematic "shadow" body so raycasts, overlaps and
850
+ // sensors see it — solid shapes strip the reserved character bit from their mask, sensors add it.
851
+ characterCreate(entityId: number, x1: number, y1: number, x2: number, y2: number, radius: number, maxSlopeDeg: number, category: number, mask: number): number
852
+ characterDestroy(charId: number): void
853
+ /** The HORIZONTAL command, world units/s — a per-frame command that EXPIRES once a step consumes it. */
854
+ characterMove(charId: number, x: number): void
855
+ /** Free mode (gravityScale 0): both axes as one expiring command. */
856
+ characterMoveFree(charId: number, x: number, y: number): void
857
+ characterSetVerticalVelocity(charId: number, vy: number): void
858
+ /** LATCH the whole velocity — it persists until a characterMove takes the axis back. */
859
+ characterSetVelocity(charId: number, x: number, y: number): void
860
+ characterSetGravityScale(charId: number, scale: number): void
861
+ characterSetMaxSlope(charId: number, maxSlopeDeg: number): void
862
+ characterSetFilter(charId: number, category: number, mask: number): void
863
+ /** Ignore this entity's one-way surfaces (0 = none) — dropping through a semisolid platform. */
864
+ characterSetDropThrough(charId: number, entityId: number): void
865
+ /** Resize keeping the FEET planted; false = it didn't fit and NOTHING changed, so the caller
866
+ * retries later and that retry is an exact headroom test. */
867
+ characterSetCapsule(charId: number, x1: number, y1: number, x2: number, y2: number, radius: number): boolean
868
+ /** Teleport to the entity's world transform (how node.position reaches a character). Drops fall speed. */
869
+ characterSetFromEntity(charId: number): void
870
+ /** out8 = [velX, velY, groundState (0 ground / 1 slope / 2 air), normalX, normalY, groundEntity,
871
+ * collisionCount, 0]. The velocity is MEASURED, not commanded. */
872
+ characterGetState(charId: number, out: Float32Array): void
873
+ /** maxN records of [entityId, normalX, normalY] — what the mover pushed out of this step. */
874
+ characterGetCollisions(charId: number, maxN: number): Float32Array
875
+ /** Mark a body's surfaces one-way: solid only from the side (nx, ny) points to, within arcDeg.
876
+ * Honoured by the mover and, via a native pre-solve callback, by ordinary rigid bodies. */
877
+ physicsSetOneWay(bodyHandle: number, nx: number, ny: number, arcDeg: number, enabled: boolean): void
878
+ pickSprite(sceneId: number, wx: number, wy: number): number
879
+ }
880
+
881
+ // ---- multiplayer transport (creator-net / yojimbo) -------------------------------------------
882
+ // A byte pipe pumped on the JS thread (docs/multiplayer-plan.md). Present only on hosts built
883
+ // with CREATOR_PKG_NET (desktop, the 3d/full Android variants, iOS); the SDK feature-detects it.
884
+ // One endpoint per process: `listen` (server / host) or `connect` (client), never both.
885
+ var _creatorNet: {
886
+ /** What the exe was launched as (`--server --port N` / `--connect ADDR`), or null. */
887
+ launch(): { role: string, address: string, port: number, maxClients: number } | null
888
+ /** Serve on every interface at `port` (wildcard public address). */
889
+ listen(port: number, maxClients: number): boolean
890
+ /** Insecure (dev / LAN) connect to "ip:port"; the outcome arrives as a poll record. */
891
+ connect(address: string): boolean
892
+ disconnect(): void
893
+ kick(client: number): void
894
+ /** Queue a payload: channel 0 reliable-ordered, 1 unreliable; `client` ignored on a client.
895
+ * A string travels as UTF-8 text and arrives as a string; bytes arrive as an ArrayBuffer. */
896
+ send(client: number, channel: number, data: string | ArrayBuffer | Uint8Array): boolean
897
+ /** Pump once and return every record since the last call as a flat array of 4-tuples
898
+ * [kind, client, arg, payload] — kind 1 connected · 2 disconnected · 3 message · 4 connectFailed;
899
+ * arg = channel (message) or reason code; payload = string | ArrayBuffer | null. */
900
+ poll(): any[] | null
901
+ /** 0 idle · 1 client · 2 server */
902
+ mode(): number
903
+ /** Client: 0 disconnected · 1 connecting · 2 connected · 3 failed. A listening server: 2. */
904
+ state(): number
905
+ /** [connected, rttMs, lossPct, sentKbps, receivedKbps] for a slot (server) / the link (client). */
906
+ clientInfo(client: number): Float32Array
907
+ /** yojimbo network simulator on this side of the wire. */
908
+ simulate(latencyMs: number, jitterMs: number, lossPercent: number): void
909
+ reasonString(reason: number, serverSide: boolean): string
910
+ }
911
+
912
+ // ---- navigation meshes (creator-nav / Recast+Detour) -----------------------------------------
913
+ // Runtime only (docs/navmesh-plan.md §5): load a baked `.navmesh` (LNAV) from a fetch system id,
914
+ // query it, run a DetourCrowd of agents one fixed step at a time. Present on hosts built with
915
+ // CREATOR_PKG_NAV (desktop, the 3d/full Android variants, iOS) and on every web host through the
916
+ // wasm build; the SDK feature-detects it. Parameter blocks are fixed-order Float32Arrays
917
+ // (creator-nav.h CNAV_AGENT_* / CNAV_READ_*). Handles (meshId / crowdId, from 1) die with the world.
918
+ var _creatorNav: {
919
+ /** Web hosts only: loads the wasm lazily. Absent on native hosts — call it when present. */
920
+ ready?(): Promise<void>
921
+ lastError(): string
922
+ /** LNAV bytes already fetched (the createGlb pattern) → meshId, 0 on failure. */
923
+ load(systemId: number): number
924
+ unload(meshId: number): void
925
+ /** The file's JSON header: agent size, cell, bounds, stats, `meta` (the CLI's provenance). */
926
+ header(meshId: number): string
927
+ polyCount(meshId: number): number
928
+ setAreaCost(meshId: number, area: number, cost: number): void
929
+ /** Straight path from → to into `out` (xyz per corner); returns the corner count, NEGATIVE when
930
+ * the path is partial (the target is unreachable — it ends at the closest polygon). `ex <= 0`
931
+ * = default search extents. */
932
+ findPath(meshId: number, fx: number, fy: number, fz: number, tx: number, ty: number, tz: number, ex: number, ey: number, ez: number, include: number, exclude: number, maxCorners: number, out: Float32Array): number
933
+ nearest(meshId: number, x: number, y: number, z: number, ex: number, ey: number, ez: number, include: number, exclude: number, out: Float32Array): boolean
934
+ /** Walkability ray along the surface; true = BLOCKED, out = [hit xyz, wall normal xyz]. */
935
+ raycast(meshId: number, fx: number, fy: number, fz: number, tx: number, ty: number, tz: number, include: number, exclude: number, out: Float32Array): boolean
936
+ /** `radius <= 0` = anywhere on the mesh; deterministic per seed. */
937
+ randomPoint(meshId: number, cx: number, cy: number, cz: number, radius: number, seed: number, include: number, exclude: number, out: Float32Array): boolean
938
+ /** The polygon mesh as a triangle soup (xyz per vertex) for debug drawing. */
939
+ debugTriangles(meshId: number): Float32Array
940
+ crowdCreate(meshId: number, maxAgents: number, maxRadius: number): number
941
+ crowdDestroy(crowdId: number): void
942
+ /** → the agent's slot (0..maxAgents-1), -1 when full / off the mesh. */
943
+ crowdAdd(crowdId: number, x: number, y: number, z: number, params: Float32Array): number
944
+ crowdRemove(crowdId: number, agent: number): void
945
+ crowdSetParams(crowdId: number, agent: number, params: Float32Array): void
946
+ crowdSetTarget(crowdId: number, agent: number, x: number, y: number, z: number): boolean
947
+ crowdSetVelocity(crowdId: number, agent: number, vx: number, vy: number, vz: number): void
948
+ crowdResetTarget(crowdId: number, agent: number): void
949
+ crowdWarp(crowdId: number, agent: number, x: number, y: number, z: number): boolean
950
+ /** Once per fixed step. */
951
+ crowdUpdate(crowdId: number, dt: number): void
952
+ /** Every slot into `out` (CNAV_AGENT_READ_STRIDE floats each); returns maxAgents. */
953
+ crowdRead(crowdId: number, out: Float32Array): number
954
+ }
955
+
956
+ // ---- UI engine (creator-ui) ------------------------------------------------------------------
957
+ var _creatorUI: {
958
+ // Global back-press fallback (app.onBackPressed): a PROPERTY the SDK writes, not a call —
959
+ // the engine reads it off this world's _creatorUI object at the END of its back chain
960
+ // (widget → destination → pager → router → this), so it dies with the world's context.
961
+ // Old hosts never read it and the handler is silently inert there.
962
+ _backButtonCallback?: () => void
963
+ openScreen(screen: object): void
964
+ closeScreen(): void
965
+ updateStyle(nodeId: number, prop: string, value: any): void
966
+ mergeStyle(nodeId: number, style: object): void
967
+ // Toggle a user style class (a `$`-block declared in .style()) on a node. `name` arrives
968
+ // WITHOUT the leading `$`. Class state CASCADES down the node tree: a class set on a node is
969
+ // active on all its descendants within the same root (screens/widgets are separate roots) —
970
+ // implemented ONCE in creator-ui's setNodeClass (docs/style-class-cascade-plan.md); hosts must
971
+ // NOT layer their own inheritance on top. `$pressed`/`$focused` are reserved cascading classes
972
+ // hosts toggle alongside the built-in onPressed/onFocused (creator-ui setNodePressed/Focused).
973
+ setClass(nodeId: number, name: string, enabled: boolean): void
974
+
975
+ // Theme variables (docs/ui-theme-plan.md). MERGES `values` into the app theme table and
976
+ // re-resolves live styles: keys are var names (`var(--name)` in style values reads them;
977
+ // the four `comfort-*` keys are the comfort knobs), null removes a key, numbers are lengths
978
+ // (px). Env names (safe-*, vw/vh/vmin/vmax) are reserved — hosts ignore writes to them.
979
+ // Optional during rollout: the SDK feature-detects.
980
+ setTheme?(values: Record<string, string | number | null>): void
981
+
982
+ // Content-property push. Props: "value" (input/textarea text), "focus" (boolean — focus/blur
983
+ // the input, opening/dismissing the keyboard; rides this channel so programmatic focus needs
984
+ // no new ABI method), plus element-specific ones ("src", "text", …).
985
+ updateNode(nodeId: number, prop: string, value: any): void
986
+ setSourceRect(nodeId: number, x: number, y: number, w: number, h: number): void
987
+
988
+ // Canvas.update() → live-refresh every UIImage(canvas) node currently showing this baked surface:
989
+ // the host re-reads the re-rasterized pixels into the image view WITHOUT a relayout (the surface
990
+ // keeps its id and size across an update; a resize goes through a full `src` re-assign). Optional
991
+ // during rollout — the SDK feature-detects and falls back to re-assigning `src`.
992
+ refreshCanvasSurface?(surfaceId: number): void
993
+
994
+ // Font registration is fire-and-forget in practice: hosts load the face, then re-measure /
995
+ // re-layout when it lands (docs/font-system-plan.md — swap is the fallback; boot faces are
996
+ // prefetched via the bundle's `// fonts:` header). The callbacks stay on the wire for old
997
+ // bundles that gate on them — hosts must keep invoking onComplete. Repeated registration of
998
+ // the same (family, weight, style) is a cheap no-op — hosts dedupe.
999
+ registerFont(fontFamily: string, url: string, options: any, onComplete: () => void, onReject: () => void): void
1000
+ // Preferred container format for CDN-served faces: "woff2" on browser hosts, absent (= "ttf")
1001
+ // on native/headless. The font() macro's generated registration code reads it to pick the
1002
+ // file extension; the `// fonts:` header carries a `{fmt}` placeholder for the same choice.
1003
+ fontFormat?: "woff2" | "ttf"
1004
+
1005
+ isButtonPressed(nodeId: number): boolean
1006
+
1007
+ registerTouchStartEvent(callback: (...args: any) => void): void
1008
+ registerTouchEndEvent(callback: (...args: any) => void): void
1009
+ registerResizeEvent(callback: (...args: any) => void): void
1010
+ // Current display size [width, height] in logical px — the same values the resize event delivers.
1011
+ // Surfaced as device.width / device.height. Optional: hosts that don't track it (headless) omit it,
1012
+ // and the getters fall back to 0.
1013
+ getDisplaySize?(): [number, number]
1014
+
1015
+ getTextValue(nodeId: number): string
1016
+
1017
+ // Absolute device-space rect [left, top, width, height] of a laid-out node, in logical px —
1018
+ // the same space UIWidget top/left position in — INCLUDING scroll offsets: computed live at
1019
+ // call time, never cached from layout events. null (or undefined) when the node isn't
1020
+ // mounted/laid out. Backs el.getBoundingClientRect(), the widget-anchoring primitive
1021
+ // (dropdowns, popovers, tooltips — docs/ui-components-plan.md §3.2). Optional: the SDK
1022
+ // feature-detects and returns null on hosts without it.
1023
+ getBoundingClientRect?(nodeId: number): [number, number, number, number] | null
1024
+
1025
+ insertNode(index: number, nodeId: number, childNode: any): void
1026
+ removeNode(nodeId: number, childNode: any): void
1027
+ setContent(nodeId: number, oldChildren: any[], children: any[]): void
1028
+
1029
+ animateTo(nodeId: number, style: any): void
1030
+ animateFrom(nodeId: number, style: any): void
1031
+ toast(msg: string): void
1032
+
1033
+ // Router over destinations. On hosts that implement openView (below), `screen`/`view` is any
1034
+ // view descriptor (not just a screen object) and the extra `transition` args apply; legacy
1035
+ // hosts receive screen objects only and ignore the extras.
1036
+ routerOpen(view: any, onChange: (view: any) => void, showBackButton: boolean): void
1037
+ routerPush(view: any, transition?: string | object): void
1038
+ routerReplace(view: any, transition: string | object): void
1039
+ routerPop(index: number, transition?: string | object): void
1040
+ routerHide(): void
1041
+ routerRestore(): void
1042
+
1043
+ // ---- Presentable navigation (docs/navigation-presentable-plan.md) --------------------------
1044
+ // One visible destination at a time. `view` is either a screen node object (type "screen")
1045
+ // or a descriptor:
1046
+ // { type: "scene3d", sceneId } { type: "scene2d", sceneId }
1047
+ // { type: "native", viewName, params, viewId } { type: "videoView", node }
1048
+ // Descriptors carry `_p` (the SDK Presentable instance): hand the SAME object back through
1049
+ // router onChange, and fire its `ol`/`cl` arrays on present/dismiss (screens carry ol/cl
1050
+ // themselves). Presenting a scene descriptor includes activating the engine (the openScene
1051
+ // equivalent); while covered, scene rendering pauses. `transition` is a TransitionName string
1052
+ // or a TransitionSpec object (see sdk/src/ui/presentable.ts).
1053
+ // Optional during the migration: the SDK feature-detects openView and falls back to
1054
+ // openScreen/openScene; hosts implementing openView should keep openScreen/routerPush(screen)
1055
+ // as thin aliases into it for previously compiled bundles.
1056
+ openView?(view: object, transition: string | object): void
1057
+ closeView?(): void
1058
+
1059
+ // NativeView plugin channel (host `registerView(name, factory)` capabilities). `args`, the
1060
+ // result, and event payloads are JSON strings. Events are delivered by invoking the arrays in
1061
+ // the element's `nvl[event]` or via its `_emitViewEvent(event, json)`.
1062
+ isViewSupported?(name: string): boolean
1063
+ viewCall?(viewId: number, method: string, args: string, onComplete: (result?: string) => void, onError: (err: string) => void): void
1064
+
1065
+ // `owner` (a view descriptor) binds the widget to a destination: it mounts inside the owner's
1066
+ // page — shows/hides with it and rides its transition. Omitted = global overlay (legacy
1067
+ // behavior; legacy hosts ignore the arg).
1068
+ showWidget(widget: object, owner?: object): void
1069
+ hideWidget(widget: object): void
1070
+
1071
+ vlistMount(jsNode: any, key: string, subtree: any): void
1072
+ vlistSetKeys(jsNode: any, keys: string[], estimates: number[]): void
1073
+ vlistInsertKeys(jsNode: any, index: number, keys: string[], estimates: number[]): void
1074
+ vlistInvalidate(jsNode: any, k: string): void
1075
+ vlistRemoveKeys(jsNode: any, keys: string[]): void
1076
+ command(jsNode: any, command: string, ...args: any): void
1077
+ }
1078
+
1079
+ // ---- Platform utilities ----------------------------------------------------------------------
1080
+ var _creatorUtils: {
1081
+ // `fetchOptions.body` is either a string or a FormData: an object carrying `_entries`, an array
1082
+ // of [key, value, filename?]. A `value` object carrying a numeric `_id` is binary (that's the
1083
+ // host buffer id — SDK `File` and `FetchResponse` both expose it under that key); anything else
1084
+ // is a text field. Same `_id` convention as an image `src` object (see _creatorUI.updateNode).
1085
+ fetch(url: string, fetchOptions: any, onComplete: (systemId: number, statusCode: number) => void, onReject: () => void): void
1086
+ fetchLocal(path: string): number
1087
+ fetchToJson(systemId: number): any
1088
+ fetchSlice(systemId: number, start: number, end: number): any
1089
+ fetchToText(systemId: number): string
1090
+ /** The fetched bytes as a Uint8Array copy (a `.terrain` file, any binary the SDK parses itself).
1091
+ * Optional: an older host has only the text/JSON readers (Terrain.load throws there). */
1092
+ fetchToBytes?(systemId: number): Uint8Array
1093
+ disposeFetch(systemId: number): void
1094
+ // ---- Local filesystem (the SDK `files` global) ----------------------------------------------
1095
+ // The read/write/delete half of `fetchLocal`, for hosts where the app OWNS a filesystem
1096
+ // (desktop). All five are OPTIONAL and stand or fall together: a sandboxed host (web, mobile)
1097
+ // implements none of them and `files.supported` is false there.
1098
+ //
1099
+ // ASYNC, like `fetch`: file IO must not block the frame, so each takes an onComplete/onReject
1100
+ // pair (the SDK turns them into a promise) and the host does the work off the JS thread. Hosts
1101
+ // must keep the ops ORDERED — two appends to one file settle in call order — so a queue, not a
1102
+ // thread per call. Exactly one handle is ever settled.
1103
+ //
1104
+ // PATH POLICY, identical for all five and owned by the host: an ABSOLUTE path is used verbatim;
1105
+ // a RELATIVE one is resolved against the app's OWN folder — never the process cwd — which for a
1106
+ // packaged desktop app is the exe's directory, one of the directories `fetchLocal` searches by
1107
+ // name. So `writeLocal("save.json", …)` and `fetchLocal("save.json")` are a pair.
1108
+ /** Write a file, creating the parent folders. `append` adds to the end instead of replacing.
1109
+ * `data` is text (written as UTF-8), raw bytes (Uint8Array / ArrayBuffer), or a host buffer
1110
+ * object carrying `_id` (an SDK `File` / `FetchResponse` — so a picked or generated file is
1111
+ * saved as it is); the bytes are only valid for the duration of the call, so a host that
1112
+ * defers the write copies them first. The core drops the path's cached `fetchLocal` id, so
1113
+ * the next read sees the new bytes. */
1114
+ writeLocal?(path: string, data: string | Uint8Array | ArrayBuffer | { _id: number }, append: boolean, onComplete: () => void, onReject: (err: Error) => void): void
1115
+ /** The whole file as bytes, or null when there is no such file. Unlike `fetchLocal` this
1116
+ * re-reads every call and holds nothing — no buffer id to dispose. Only a real failure
1117
+ * (an unreadable file) rejects; "not there" is a null result. */
1118
+ readLocalBytes?(path: string, onComplete: (bytes: Uint8Array | null) => void, onReject: (err: Error) => void): void
1119
+ /** The whole file decoded as UTF-8 text, or null when there is no such file. */
1120
+ readLocalText?(path: string, onComplete: (text: string | null) => void, onReject: (err: Error) => void): void
1121
+ /** Delete a file, or an EMPTY directory; `recursive` also deletes a directory's contents.
1122
+ * Deleting something that is already gone SUCCEEDS — the post-condition is what matters. */
1123
+ deleteLocal?(path: string, recursive: boolean, onComplete: () => void, onReject: (err: Error) => void): void
1124
+ /** Create a directory and any missing parents. An existing directory is success. */
1125
+ mkdirLocal?(path: string, onComplete: () => void, onReject: (err: Error) => void): void
1126
+ openFilePicker(onComplete: (res: any) => void, onReject: () => void, multiple: boolean, accept?: string): void
1127
+ // Present the OS media share sheet for a host buffer (`systemId` — a File/response id), with an
1128
+ // optional caption. iOS: the share sheet also offers "Save to Files"/Photos. Web: saves (downloads)
1129
+ // the file. Backs the SDK `share(media, text?)`.
1130
+ shareMedia(systemId: number, text?: string): void
1131
+
1132
+ /** Prompt for camera permission (resolve = granted, reject = denied). Shared by everything that
1133
+ * needs the camera — ARScene.prepare, QRScanner, CameraView — hence a platform utility rather
1134
+ * than a 3D-engine call (it moved off `_creator` 2026-07-15). Optional: hosts whose view
1135
+ * factory prompts on its own (web getUserMedia) don't implement it and the SDK resolves
1136
+ * immediately, so callers go through `_requestCameraPermission()`. */
1137
+ requestCamera?(onComplete: () => void, onReject: () => void): void
1138
+
1139
+ // (openScanner/closeScanner removed 2026-07-15 — QRScanner rides the registered "qrScanner"
1140
+ // NativeView; hosts keep the old handlers only for previously compiled bundles.)
1141
+ // ---- Input (docs/input-plan.md — keyboard / mouse / gamepad behind the SDK `Input` global) ----
1142
+ // Design: a BUTTON is anything that goes down and up — keyboard keys, mouse buttons, gamepad
1143
+ // buttons — and they share ONE code vocabulary (KeyboardEvent.code + "MouseLeft|Right|Middle|
1144
+ // Back|Forward" + the W3C standard-gamepad "GamepadSouth|East|West|North|L1|R1|L2|R2|Select|
1145
+ // Start|L3|R3|Up|Down|Left|Right"), one held-poll and one event pair. Continuous signals (mouse
1146
+ // motion, sticks, triggers) are numbered channels behind one poll. Bindings/axes/edge detection
1147
+ // are SDK-side; hosts only report physical state.
1148
+ /** Is this button held right now? `gamepad` (0..3) scopes a Gamepad* code to one pad; omitted =
1149
+ * any connected pad. Keyboard/mouse codes ignore it. */
1150
+ inputKey: (code: string, gamepad?: number) => boolean
1151
+ /** Continuous input poll by channel id (SDK `InputChannel`): 0 MouseX, 1 MouseY (logical px,
1152
+ * last known cursor position inside the viewport), 2 MouseDX, 3 MouseDY (motion accumulated
1153
+ * during the PREVIOUS frame — unaccelerated/raw where the OS offers it, counts while locked),
1154
+ * 4 WheelX, 5 WheelY (wheel notches, previous frame), 6 PointerLocked (0/1), 7 GamepadCount;
1155
+ * gamepad p at 16 + 16·p: +0 Connected, +1 LeftX, +2 LeftY, +3 RightX, +4 RightY (−1..1,
1156
+ * +Y = down like the web), +5 LeftTrigger, +6 RightTrigger (0..1). Unknown channel → 0.
1157
+ * "Previous frame" = the host's own frame boundary (the event pump / rAF / fixed step), so
1158
+ * every read within one frame agrees. Optional: a host without it has no pointer/gamepad. */
1159
+ inputRead?(channel: number): number
1160
+ /** Request (true) / release (false) pointer lock: hide + confine the cursor, keep MouseDX/DY
1161
+ * flowing (FPS look). Returns whether the request was accepted now (web needs a user gesture);
1162
+ * the eventual state is channel 6. Hosts release on focus loss and re-acquire on focus. */
1163
+ inputSetPointerLock?(on: boolean): boolean
1164
+ /** Register THE input-event listener (single slot per JS world, like registerAppEvent — the SDK
1165
+ * fans out). kind: 0 keydown, 1 keyup, 2 gamepadconnected, 3 gamepaddisconnected. `code` is the
1166
+ * button code above ("" for connect events); `gamepad` the pad index for Gamepad* codes /
1167
+ * connect events, −1 otherwise; `repeat` 1 for an OS auto-repeat keydown. Delivered on the JS
1168
+ * thread no later than the end of the frame it arrived in — i.e. always before the NEXT
1169
+ * setLoop tick (creator-pkg drains its dispatch queue after the frame's timers, so a poll can
1170
+ * see the new state one tick before the event; web/headless deliver synchronously). */
1171
+ registerInputEvent?(callback: (kind: number, code: string, gamepad: number, repeat: number) => void): void
1172
+ // ---- World control (run/restart/quit — the platform's `location` analog) --------------------
1173
+ // Worlds carry two host-tracked bits: an IDENTITY (project uuid) and a TRUST flag. The host's
1174
+ // own boot world (the bundled launcher) is trusted; a world created by `run` is trusted only
1175
+ // when its declared uuid is on the host's trusted-launcher allowlist AND the caller was
1176
+ // trusted itself. Push registration attribution reads the identity (docs/push-plan.md).
1177
+ /** Swap in a new project world. `projectUuid` declares the new world's identity — honored
1178
+ * ONLY when the calling world is trusted (launchers); silently dropped otherwise. `url`
1179
+ * (any caller — the caller already controls the code it runs) is the new world's
1180
+ * launchUrl: a string sets it, null clears it, omitted leaves it unchanged. */
1181
+ run(systemId: number, onReject: () => void, projectUuid?: string, url?: string | null): void
1182
+ /** Re-run the CURRENT world's bundle in a fresh world — `location.reload()`. Identity and
1183
+ * trust are inherited (the host re-evaluates bytes it already holds, so nothing is
1184
+ * spoofable). `url`: string sets the new launchUrl, null clears it, omitted keeps the
1185
+ * current one (a plain reload). If the re-run throws, the current world stays live. */
1186
+ restart?(url?: string | null): void
1187
+ /** Leave the current app. A world launched by `run` returns to the caller's boot bundle
1188
+ * (launch URL cleared, boot identity/trust restored). The boot world itself has no caller —
1189
+ * the host's app-exit hook fires instead (Android backgrounds the task; hosts that can't
1190
+ * exit programmatically, iOS/desktop, register nothing and the call is a no-op). */
1191
+ quit?(): void
1192
+
1193
+ animate(callback: (val: number) => void, duration: number): number
1194
+ stopAnimation(id: number): void
1195
+ pauseAnimation(id: number): void
1196
+ resumeAnimation(id: number): void
1197
+
1198
+ localStorageGetValue(key: string): string | null
1199
+ localStorageSetValue(key: string, value: string): void
1200
+ localStorageRemoveValue(key: string): void
1201
+
1202
+ websocketOpen(url: string, onWebsocketMessage: (channel: string, data: any) => void, headers?: any): number
1203
+ websocketSend(id: number, message: string | ArrayBuffer): void
1204
+ websocketClose(id: number): void
1205
+
1206
+ // ---- Service channel (headless registerService capabilities) --------------------------------
1207
+ // The UI-less sibling of the NativeView channel (docs/service-channel-plan.md): the host
1208
+ // registers named service factories (`registerService("geolocation", factory)`) and the SDK
1209
+ // talks to them over the same JSON call/event protocol as viewCall. All four are optional —
1210
+ // feature-detect with typeof; hosts that didn't opt in simply don't have them. Sessions must
1211
+ // open cheaply (no permission prompt, no I/O) — prompts belong in the first call that needs
1212
+ // them, so a rejected call is the failure surface, never a hung open.
1213
+ isServiceSupported?(name: string): boolean
1214
+ /** Open a session against the registered factory. `params` is a JSON string. Returns the
1215
+ * host-allocated session id, or -1 when no factory is registered under `name`. `onEvent`
1216
+ * delivers service events (payload as a JSON string, or omitted) until serviceClose. */
1217
+ serviceOpen?(name: string, params: string, onEvent: (event: string, dataJson?: string) => void): number
1218
+ /** Invoke a method on a live session — args/result JSON-encoded, exactly like viewCall. */
1219
+ serviceCall?(serviceId: number, method: string, args: string,
1220
+ onComplete: (result?: string) => void, onError: (err: string) => void): void
1221
+ serviceClose?(serviceId: number): void
1222
+
1223
+ // ---- App lifecycle & environment (docs/app-lifecycle-plan.md) --------------------------------
1224
+ // One push channel + synchronous pulls. All optional — typeof feature-detect; the SDK wrappers
1225
+ // fall back cleanly (state "active", launchUrl null, online true) on hosts that lack them.
1226
+ /** Register THE app-event listener (single slot per JS world — the SDK fans out; the host/core
1227
+ * scopes the stored callback to the world and frees it on teardown). Events: "pause" / "resume"
1228
+ * (app left / returned to the foreground), "url" (a link arrived while running; data = the URL),
1229
+ * "online" / "offline" (connectivity changed). "pause" is delivered BEFORE the host halts the
1230
+ * frame loop (the dispatch queue must still drain). */
1231
+ registerAppEvent?(callback: (event: string, data?: string) => void): void
1232
+ /** Current lifecycle state: "active" | "background". Surfaced as app.state. */
1233
+ appState?(): string
1234
+ /** The URL the app was (most recently) opened with, or null — the cold-start deep link. A warm
1235
+ * link updates this value first, then fires the "url" event. Surfaced as app.launchUrl. */
1236
+ getLaunchUrl?(): string | null
1237
+ /** Current connectivity (navigator.onLine semantics — best-effort: false only when the platform
1238
+ * is sure there is no network). Surfaced as device.online. */
1239
+ isOnline?(): boolean
1240
+ /** Open a URL in the system browser / external handler (fire-and-forget). Backs openURL(). */
1241
+ openUrl?(url: string): void
1242
+ /** Lock the interface orientation: "landscape" (either landscape direction — sensor
1243
+ * landscape, what a horizontal game wants), "portrait" (upright only) or "auto" (release —
1244
+ * the device's rotation rules apply again). Fire-and-forget; the rotation itself is
1245
+ * animated by the platform and lands as an ordinary resize. PER WORLD: the host resets the
1246
+ * lock to "auto" on every world swap BEFORE the new bundle evaluates, so a game may lock at
1247
+ * synchronous top level and the launcher always comes back unlocked. Optional — hosts
1248
+ * without a rotatable screen (desktop, headless, macOS) omit it. Backs app.setOrientation. */
1249
+ setOrientation?(mode: string): void
1250
+ /** Write text to the system clipboard (fire-and-forget). Backs clipboard.write. */
1251
+ clipboardWrite?(text: string): void
1252
+ /** Read text from the system clipboard — async because web needs a permission prompt; native
1253
+ * hosts may complete synchronously. Reject = no access / nothing readable. Backs clipboard.read. */
1254
+ clipboardRead?(onComplete: (text: string) => void, onReject: (err: string) => void): void
1255
+
1256
+ // ---- Keyboard inset (docs/input-upgrades-plan.md §7) ------------------------------------------
1257
+ /** On-screen keyboard height in logical px currently overlapping the app viewport, 0 when
1258
+ * hidden. The RAW overlap, independent of the focused input's keyboardShrink policy (with
1259
+ * shrink the layout already avoids it; the main consumer is overlay mode). Surfaced as
1260
+ * app.keyboardHeight. Optional — hosts without an on-screen keyboard omit it. */
1261
+ keyboardHeight?(): number
1262
+ /** Register THE keyboard listener (single slot per JS world, like registerAppEvent — the SDK
1263
+ * fans out; a separate channel because the string one can't carry two numbers). Fires on every
1264
+ * keyboard frame change with (height — as above, duration — the platform's animation duration
1265
+ * in ms, 0 where none). Surfaced as the app "keyboard" event. */
1266
+ registerKeyboardEvent?(callback: (height: number, duration: number) => void): void
1267
+
1268
+ // (camera* removed 2026-07-15 — CameraView rides the registered "camera" NativeView; hosts
1269
+ // keep the old handlers only for previously compiled bundles.)
1270
+
1271
+ createAudioPlayer(src: string, onFinished: () => void): number
1272
+ createVideoPlayer(src: string, onFinished: () => void): number
1273
+ updateMediaPlayerVolume(id: number, volume: number): void
1274
+ updateMediaPlayerLoop(id: number, loop: boolean): void
1275
+ updateMediaPlayerPlaying(id: number, play: boolean): void
1276
+ getMediaPlayerTime(id: number): number
1277
+ setMediaPlayerTime(id: number, time: number): void
1278
+ getMediaPlayerDuration(id: number): number
1279
+ removeMediaPlayer(id: number): void
1280
+
1281
+ /** The host bucket surfaced as `device.platform`. Apps BRANCH on it (mouse-look vs on-screen
1282
+ * sticks, hover affordances, key hints), so it names the INPUT MODEL, not the OS — one of
1283
+ * "web" | "ios" | "android" | "desktop". macOS reports "desktop", not "macos": a project
1284
+ * testing `device.platform === "desktop"` must get the same answer on every desktop host.
1285
+ * OS/version detail belongs in the host's own systemInfo callback, never here. Every host
1286
+ * must define it — where it was missing (Android before 2026-07-30, iOS before 2026-08-25)
1287
+ * the SDK read undefined and no platform branch in any app could ever match. */
1288
+ platform: string
1289
+ language: string
1290
+ /** Display device-pixel ratio (physical px per CSS/logical px): 1 on standard displays, 2–3 on
1291
+ * retina / iOS. Host-provided (web: window.devicePixelRatio; iOS: UIScreen.scale). Surfaced as
1292
+ * device.pixelRatio — for baking a Canvas at the right resolution without hardcoding it. */
1293
+ pixelRatio: number
1294
+ /** Opt into the precise-touch system (full-rate coalesced sampling) where the host supports it — iOS
1295
+ * coalesced touches. Surfaced as device.setPreciseTouch. Optional: hosts without a coalesced-input
1296
+ * concept (web, headless) simply don't implement it, so callers guard with `?.`. */
1297
+ setPreciseTouch?(enabled: boolean): void
1298
+ /** Fire a one-shot semantic haptic (device.vibrate). `style` is a HapticStyle string. Optional:
1299
+ * hosts without haptic hardware (web, headless, iPad / older iPhones) simply don't implement it,
1300
+ * so callers guard with `?.`. */
1301
+ vibrate?(style: string): void
1302
+
1303
+ // ---- device.motion (orientation sensor; pull-per-frame model) --------------------------------
1304
+ /** Does this device have the motion sensors (gyro)? Surfaced as device.motion.available. */
1305
+ motionAvailable?(): boolean
1306
+ /** Start fused device-motion updates at `interval` seconds; returns whether it started (false =
1307
+ * no sensor / permission denied). May return a Promise on hosts with an async permission prompt
1308
+ * (web). Surfaced as device.motion.start. */
1309
+ motionStart?(interval: number): boolean | Promise<boolean>
1310
+ /** Stop updates and release the sensor. Surfaced as device.motion.stop. */
1311
+ motionStop?(): void
1312
+ /** The freshest raw sample in the DEVICE frame: `[qx,qy,qz,qw, gx,gy,gz, interfaceOrientation]`
1313
+ * (orientation code: 0 portrait, 1 landscapeLeft, 2 landscapeRight, 3 upsideDown), or null until
1314
+ * the first sample / when not running. The SDK converts it to world/screen space. */
1315
+ getMotionSample?(): number[] | null
1316
+ }
1317
+
1318
+ // ---- 2D canvas (core; native-per-platform — see docs/canvas-contract.md) ---------------------
1319
+ // Immediate-mode 2D drawing that bakes to a texture, shared by 2D/3D/UI. The SDK Canvas records a
1320
+ // command buffer and flushes it here once per bake (opcodes documented in the contract).
1321
+ var _creatorCanvas: {
1322
+ // Rasterize `cmd` (cmdLen floats) into surface `surfaceId` (0 = create), sized w×h logical @
1323
+ // `scale` device px. Clears then replays the whole buffer. Returns the surface id.
1324
+ rasterize(surfaceId: number, w: number, h: number, scale: number,
1325
+ cmd: Float32Array, cmdLen: number, refs: string[]): number
1326
+ // [width, ascent, descent] in logical px for `text` in `font`, measured on this platform.
1327
+ measureText(text: string, font: string): Float32Array
1328
+ // Copy surface `surfaceId`'s pixels into a NEW standalone surface; returns the new id. Backs
1329
+ // Canvas.toBitmap() — an immutable snapshot the source canvas can no longer touch.
1330
+ snapshot(surfaceId: number): number
1331
+ // Decode an image source into a NEW standalone surface (device px) — backs Canvas.loadImage() for
1332
+ // Canvas.drawImage(). `bufferId < 0` → rasterize the SVG markup in `svg`; otherwise decode the
1333
+ // encoded image bytes in host buffer `bufferId` (an already-fetched FetchResponse). Async, because
1334
+ // turning SVG / encoded bytes into pixels needs a decode on web (createImageBitmap / <img>). Returns
1335
+ // (surfaceId, width, height) in device px via onComplete, or onReject on failure.
1336
+ loadImage(svg: string, bufferId: number,
1337
+ onComplete: (surfaceId: number, width: number, height: number) => void, onReject: () => void): void
1338
+ // Encode surface `surfaceId` to an image ('image/png' | 'image/jpeg'), register the bytes as a host
1339
+ // buffer, and return (bufferId, size) — the SDK wraps it in a File. Backs Canvas/Bitmap.toFile().
1340
+ toFile(surfaceId: number, type: string, onComplete: (bufferId: number, size: number) => void, onReject: () => void): void
1341
+ destroySurface(surfaceId: number): void
1342
+ }
1343
+ }
1344
+
1345
+ export {}