@thunderclap-engine/engine 0.0.0-stage → 1.1.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 (566) hide show
  1. package/CHANGELOG.md +390 -0
  2. package/LICENSE +21 -0
  3. package/README.md +333 -2
  4. package/dist/analytics/analytics.d.ts +80 -0
  5. package/dist/analytics/analytics.js +129 -0
  6. package/dist/analytics/index.d.ts +1 -0
  7. package/dist/analytics/index.js +1 -0
  8. package/dist/anim/aim.d.ts +43 -0
  9. package/dist/anim/aim.js +157 -0
  10. package/dist/anim/clip.d.ts +98 -0
  11. package/dist/anim/clip.js +282 -0
  12. package/dist/anim/compress.d.ts +65 -0
  13. package/dist/anim/compress.js +339 -0
  14. package/dist/anim/foot-ik.d.ts +74 -0
  15. package/dist/anim/foot-ik.js +241 -0
  16. package/dist/anim/graph.d.ts +153 -0
  17. package/dist/anim/graph.js +532 -0
  18. package/dist/anim/ik.d.ts +105 -0
  19. package/dist/anim/ik.js +435 -0
  20. package/dist/anim/index.d.ts +36 -0
  21. package/dist/anim/index.js +21 -0
  22. package/dist/anim/match.d.ts +66 -0
  23. package/dist/anim/match.js +153 -0
  24. package/dist/anim/mixer.d.ts +113 -0
  25. package/dist/anim/mixer.js +314 -0
  26. package/dist/anim/morph.d.ts +41 -0
  27. package/dist/anim/morph.js +94 -0
  28. package/dist/anim/pose.d.ts +74 -0
  29. package/dist/anim/pose.js +254 -0
  30. package/dist/anim/quat.d.ts +44 -0
  31. package/dist/anim/quat.js +187 -0
  32. package/dist/anim/retarget.d.ts +97 -0
  33. package/dist/anim/retarget.js +366 -0
  34. package/dist/anim/root-motion.d.ts +44 -0
  35. package/dist/anim/root-motion.js +224 -0
  36. package/dist/anim/skin-pose.d.ts +39 -0
  37. package/dist/anim/skin-pose.js +53 -0
  38. package/dist/anim/socket.d.ts +47 -0
  39. package/dist/anim/socket.js +84 -0
  40. package/dist/anim/spring-chain.d.ts +73 -0
  41. package/dist/anim/spring-chain.js +294 -0
  42. package/dist/assets/atlas.d.ts +90 -0
  43. package/dist/assets/atlas.js +212 -0
  44. package/dist/assets/baked.d.ts +33 -0
  45. package/dist/assets/baked.js +38 -0
  46. package/dist/assets/bundle.d.ts +88 -0
  47. package/dist/assets/bundle.js +464 -0
  48. package/dist/assets/cache.d.ts +56 -0
  49. package/dist/assets/cache.js +123 -0
  50. package/dist/assets/gltf-load.d.ts +252 -0
  51. package/dist/assets/gltf-load.js +661 -0
  52. package/dist/assets/gltf-skin.d.ts +36 -0
  53. package/dist/assets/gltf-skin.js +109 -0
  54. package/dist/assets/gltf.d.ts +706 -0
  55. package/dist/assets/gltf.js +1376 -0
  56. package/dist/assets/index.d.ts +20 -0
  57. package/dist/assets/index.js +10 -0
  58. package/dist/assets/ktx2.d.ts +71 -0
  59. package/dist/assets/ktx2.js +213 -0
  60. package/dist/assets/manifest.d.ts +79 -0
  61. package/dist/assets/manifest.js +7 -0
  62. package/dist/assets/meshopt.d.ts +23 -0
  63. package/dist/assets/meshopt.js +403 -0
  64. package/dist/audio/audio.d.ts +733 -0
  65. package/dist/audio/audio.js +2013 -0
  66. package/dist/audio/dsp.d.ts +61 -0
  67. package/dist/audio/dsp.js +159 -0
  68. package/dist/audio/duck.d.ts +44 -0
  69. package/dist/audio/duck.js +101 -0
  70. package/dist/audio/envelope.d.ts +53 -0
  71. package/dist/audio/envelope.js +106 -0
  72. package/dist/audio/index.d.ts +13 -0
  73. package/dist/audio/index.js +14 -0
  74. package/dist/audio/music.d.ts +113 -0
  75. package/dist/audio/music.js +548 -0
  76. package/dist/audio/occlusion.d.ts +55 -0
  77. package/dist/audio/occlusion.js +61 -0
  78. package/dist/audio/persist.d.ts +23 -0
  79. package/dist/audio/persist.js +27 -0
  80. package/dist/audio/recipes.d.ts +55 -0
  81. package/dist/audio/recipes.js +227 -0
  82. package/dist/audio/send.d.ts +41 -0
  83. package/dist/audio/send.js +118 -0
  84. package/dist/audio/sprites.d.ts +59 -0
  85. package/dist/audio/sprites.js +43 -0
  86. package/dist/audio/synth.d.ts +103 -0
  87. package/dist/audio/synth.js +197 -0
  88. package/dist/audio/variations.d.ts +25 -0
  89. package/dist/audio/variations.js +55 -0
  90. package/dist/audio/zones.d.ts +100 -0
  91. package/dist/audio/zones.js +167 -0
  92. package/dist/core/detached-surface.d.ts +44 -0
  93. package/dist/core/detached-surface.js +104 -0
  94. package/dist/core/events.d.ts +24 -0
  95. package/dist/core/events.js +43 -0
  96. package/dist/core/game.d.ts +409 -0
  97. package/dist/core/game.js +530 -0
  98. package/dist/core/hash.d.ts +24 -0
  99. package/dist/core/hash.js +49 -0
  100. package/dist/core/index.d.ts +17 -0
  101. package/dist/core/index.js +9 -0
  102. package/dist/core/loop.d.ts +113 -0
  103. package/dist/core/loop.js +185 -0
  104. package/dist/core/math.d.ts +58 -0
  105. package/dist/core/math.js +210 -0
  106. package/dist/core/noise.d.ts +40 -0
  107. package/dist/core/noise.js +247 -0
  108. package/dist/core/pool.d.ts +42 -0
  109. package/dist/core/pool.js +85 -0
  110. package/dist/core/random.d.ts +61 -0
  111. package/dist/core/random.js +199 -0
  112. package/dist/core/scene.d.ts +79 -0
  113. package/dist/core/scene.js +195 -0
  114. package/dist/core/timer.d.ts +66 -0
  115. package/dist/core/timer.js +286 -0
  116. package/dist/fx/billboard-wgsl.d.ts +29 -0
  117. package/dist/fx/billboard-wgsl.js +158 -0
  118. package/dist/fx/billboard.d.ts +249 -0
  119. package/dist/fx/billboard.js +702 -0
  120. package/dist/fx/debug-draw.d.ts +127 -0
  121. package/dist/fx/debug-draw.js +523 -0
  122. package/dist/fx/debug-wgsl.d.ts +7 -0
  123. package/dist/fx/debug-wgsl.js +32 -0
  124. package/dist/fx/decals-wgsl.d.ts +15 -0
  125. package/dist/fx/decals-wgsl.js +173 -0
  126. package/dist/fx/decals.d.ts +216 -0
  127. package/dist/fx/decals.js +607 -0
  128. package/dist/fx/effect.d.ts +129 -0
  129. package/dist/fx/effect.js +349 -0
  130. package/dist/fx/effects/hit-spark.json +48 -0
  131. package/dist/fx/effects/index.d.ts +8 -0
  132. package/dist/fx/effects/index.js +9 -0
  133. package/dist/fx/effects/magic-aura.json +47 -0
  134. package/dist/fx/effects/smoke-puff.json +34 -0
  135. package/dist/fx/emit-surface.d.ts +40 -0
  136. package/dist/fx/emit-surface.js +124 -0
  137. package/dist/fx/flicker.d.ts +30 -0
  138. package/dist/fx/flicker.js +42 -0
  139. package/dist/fx/index.d.ts +31 -0
  140. package/dist/fx/index.js +20 -0
  141. package/dist/fx/particles-wgsl.d.ts +57 -0
  142. package/dist/fx/particles-wgsl.js +828 -0
  143. package/dist/fx/particles.d.ts +566 -0
  144. package/dist/fx/particles.js +1533 -0
  145. package/dist/fx/random.d.ts +15 -0
  146. package/dist/fx/random.js +20 -0
  147. package/dist/fx/sky-wgsl.d.ts +23 -0
  148. package/dist/fx/sky-wgsl.js +127 -0
  149. package/dist/fx/sky.d.ts +96 -0
  150. package/dist/fx/sky.js +175 -0
  151. package/dist/fx/text-atlas.d.ts +143 -0
  152. package/dist/fx/text-atlas.js +327 -0
  153. package/dist/fx/trail-wgsl.d.ts +11 -0
  154. package/dist/fx/trail-wgsl.js +42 -0
  155. package/dist/fx/trail.d.ts +194 -0
  156. package/dist/fx/trail.js +555 -0
  157. package/dist/fx/water-wgsl.d.ts +34 -0
  158. package/dist/fx/water-wgsl.js +197 -0
  159. package/dist/fx/water.d.ts +176 -0
  160. package/dist/fx/water.js +371 -0
  161. package/dist/geom/collide/bvh.d.ts +151 -0
  162. package/dist/geom/collide/bvh.js +493 -0
  163. package/dist/geom/collide/debug.d.ts +14 -0
  164. package/dist/geom/collide/debug.js +74 -0
  165. package/dist/geom/collide/grid.d.ts +26 -0
  166. package/dist/geom/collide/grid.js +91 -0
  167. package/dist/geom/collide/index.d.ts +8 -0
  168. package/dist/geom/collide/index.js +8 -0
  169. package/dist/geom/collide/queries.d.ts +70 -0
  170. package/dist/geom/collide/queries.js +583 -0
  171. package/dist/geom/collide/serialize.d.ts +14 -0
  172. package/dist/geom/collide/serialize.js +145 -0
  173. package/dist/geom/collide/shapes.d.ts +161 -0
  174. package/dist/geom/collide/shapes.js +686 -0
  175. package/dist/geom/collide/slide.d.ts +66 -0
  176. package/dist/geom/collide/slide.js +375 -0
  177. package/dist/geom/collide/tile-slide.d.ts +88 -0
  178. package/dist/geom/collide/tile-slide.js +255 -0
  179. package/dist/geom/geometry.d.ts +95 -0
  180. package/dist/geom/geometry.js +322 -0
  181. package/dist/geom/heightfield.d.ts +72 -0
  182. package/dist/geom/heightfield.js +295 -0
  183. package/dist/geom/hull.d.ts +27 -0
  184. package/dist/geom/hull.js +279 -0
  185. package/dist/geom/index.d.ts +35 -0
  186. package/dist/geom/index.js +23 -0
  187. package/dist/geom/iso.d.ts +74 -0
  188. package/dist/geom/iso.js +89 -0
  189. package/dist/geom/lod.d.ts +29 -0
  190. package/dist/geom/lod.js +27 -0
  191. package/dist/geom/ops.d.ts +47 -0
  192. package/dist/geom/ops.js +285 -0
  193. package/dist/geom/primitives.d.ts +97 -0
  194. package/dist/geom/primitives.js +359 -0
  195. package/dist/geom/shapes.d.ts +43 -0
  196. package/dist/geom/shapes.js +88 -0
  197. package/dist/geom/simplify.d.ts +38 -0
  198. package/dist/geom/simplify.js +519 -0
  199. package/dist/geom/split.d.ts +37 -0
  200. package/dist/geom/split.js +413 -0
  201. package/dist/geom/sweep.d.ts +83 -0
  202. package/dist/geom/sweep.js +534 -0
  203. package/dist/geom/tangents.d.ts +16 -0
  204. package/dist/geom/tangents.js +124 -0
  205. package/dist/geom/triangulate.d.ts +19 -0
  206. package/dist/geom/triangulate.js +147 -0
  207. package/dist/host/default.d.ts +10 -0
  208. package/dist/host/default.js +10 -0
  209. package/dist/host/index.d.ts +4 -0
  210. package/dist/host/index.js +3 -0
  211. package/dist/host/types.d.ts +144 -0
  212. package/dist/host/types.js +1 -0
  213. package/dist/host/web-links.d.ts +38 -0
  214. package/dist/host/web-links.js +110 -0
  215. package/dist/host/web.d.ts +30 -0
  216. package/dist/host/web.js +216 -0
  217. package/dist/host/xr.d.ts +24 -0
  218. package/dist/host/xr.js +59 -0
  219. package/dist/i18n/i18n.d.ts +59 -0
  220. package/dist/i18n/i18n.js +241 -0
  221. package/dist/i18n/index.d.ts +3 -0
  222. package/dist/i18n/index.js +3 -0
  223. package/dist/i18n/message.d.ts +72 -0
  224. package/dist/i18n/message.js +369 -0
  225. package/dist/i18n/negotiate.d.ts +17 -0
  226. package/dist/i18n/negotiate.js +58 -0
  227. package/dist/index.d.ts +7 -0
  228. package/dist/index.js +1 -0
  229. package/dist/input/bindings.d.ts +31 -0
  230. package/dist/input/bindings.js +141 -0
  231. package/dist/input/clock.d.ts +44 -0
  232. package/dist/input/clock.js +49 -0
  233. package/dist/input/follow.d.ts +133 -0
  234. package/dist/input/follow.js +218 -0
  235. package/dist/input/gestures.d.ts +281 -0
  236. package/dist/input/gestures.js +662 -0
  237. package/dist/input/glyphs.d.ts +35 -0
  238. package/dist/input/glyphs.js +216 -0
  239. package/dist/input/gyro.d.ts +85 -0
  240. package/dist/input/gyro.js +140 -0
  241. package/dist/input/index.d.ts +19 -0
  242. package/dist/input/index.js +16 -0
  243. package/dist/input/input.d.ts +543 -0
  244. package/dist/input/input.js +1492 -0
  245. package/dist/input/look.d.ts +327 -0
  246. package/dist/input/look.js +681 -0
  247. package/dist/input/magnify.d.ts +177 -0
  248. package/dist/input/magnify.js +131 -0
  249. package/dist/input/pointer-dispatch.d.ts +138 -0
  250. package/dist/input/pointer-dispatch.js +180 -0
  251. package/dist/input/pointer-router.d.ts +204 -0
  252. package/dist/input/pointer-router.js +533 -0
  253. package/dist/input/pointer-sources.d.ts +164 -0
  254. package/dist/input/pointer-sources.js +306 -0
  255. package/dist/input/rays.d.ts +138 -0
  256. package/dist/input/rays.js +615 -0
  257. package/dist/input/record.d.ts +209 -0
  258. package/dist/input/record.js +601 -0
  259. package/dist/input/touch-zones.d.ts +147 -0
  260. package/dist/input/touch-zones.js +300 -0
  261. package/dist/input/types.d.ts +158 -0
  262. package/dist/input/types.js +1 -0
  263. package/dist/input/xr-source.d.ts +53 -0
  264. package/dist/input/xr-source.js +164 -0
  265. package/dist/nav/astar.d.ts +45 -0
  266. package/dist/nav/astar.js +182 -0
  267. package/dist/nav/corridor.d.ts +40 -0
  268. package/dist/nav/corridor.js +95 -0
  269. package/dist/nav/crowd.d.ts +94 -0
  270. package/dist/nav/crowd.js +624 -0
  271. package/dist/nav/flow.d.ts +61 -0
  272. package/dist/nav/flow.js +193 -0
  273. package/dist/nav/grid.d.ts +108 -0
  274. package/dist/nav/grid.js +210 -0
  275. package/dist/nav/heap.d.ts +22 -0
  276. package/dist/nav/heap.js +92 -0
  277. package/dist/nav/index.d.ts +37 -0
  278. package/dist/nav/index.js +19 -0
  279. package/dist/nav/mesh/areas.d.ts +44 -0
  280. package/dist/nav/mesh/areas.js +98 -0
  281. package/dist/nav/mesh/bake-options.d.ts +33 -0
  282. package/dist/nav/mesh/bake-options.js +76 -0
  283. package/dist/nav/mesh/contour.d.ts +22 -0
  284. package/dist/nav/mesh/contour.js +323 -0
  285. package/dist/nav/mesh/flow3.d.ts +35 -0
  286. package/dist/nav/mesh/flow3.js +229 -0
  287. package/dist/nav/mesh/links.d.ts +38 -0
  288. package/dist/nav/mesh/links.js +72 -0
  289. package/dist/nav/mesh/mesh-path.d.ts +152 -0
  290. package/dist/nav/mesh/mesh-path.js +643 -0
  291. package/dist/nav/mesh/mesh-reach.d.ts +78 -0
  292. package/dist/nav/mesh/mesh-reach.js +450 -0
  293. package/dist/nav/mesh/navmesh.d.ts +117 -0
  294. package/dist/nav/mesh/navmesh.js +647 -0
  295. package/dist/nav/mesh/polymesh.d.ts +24 -0
  296. package/dist/nav/mesh/polymesh.js +384 -0
  297. package/dist/nav/mesh/regions.d.ts +12 -0
  298. package/dist/nav/mesh/regions.js +367 -0
  299. package/dist/nav/mesh/serialize.d.ts +13 -0
  300. package/dist/nav/mesh/serialize.js +197 -0
  301. package/dist/nav/mesh/voxelize.d.ts +111 -0
  302. package/dist/nav/mesh/voxelize.js +552 -0
  303. package/dist/nav/obstacles.d.ts +75 -0
  304. package/dist/nav/obstacles.js +294 -0
  305. package/dist/nav/path.d.ts +59 -0
  306. package/dist/nav/path.js +177 -0
  307. package/dist/nav/query-queue.d.ts +46 -0
  308. package/dist/nav/query-queue.js +308 -0
  309. package/dist/nav/reach.d.ts +53 -0
  310. package/dist/nav/reach.js +148 -0
  311. package/dist/nav/steer.d.ts +36 -0
  312. package/dist/nav/steer.js +78 -0
  313. package/dist/platform/boot.d.ts +28 -0
  314. package/dist/platform/boot.js +43 -0
  315. package/dist/platform/device-mode.d.ts +40 -0
  316. package/dist/platform/device-mode.js +104 -0
  317. package/dist/platform/device.d.ts +31 -0
  318. package/dist/platform/device.js +91 -0
  319. package/dist/platform/environment.d.ts +42 -0
  320. package/dist/platform/environment.js +82 -0
  321. package/dist/platform/gpu.d.ts +40 -0
  322. package/dist/platform/gpu.js +57 -0
  323. package/dist/platform/index.d.ts +18 -0
  324. package/dist/platform/index.js +10 -0
  325. package/dist/platform/letterbox.d.ts +43 -0
  326. package/dist/platform/letterbox.js +57 -0
  327. package/dist/platform/links.d.ts +20 -0
  328. package/dist/platform/links.js +43 -0
  329. package/dist/platform/screens.d.ts +36 -0
  330. package/dist/platform/screens.js +138 -0
  331. package/dist/platform/surface.d.ts +72 -0
  332. package/dist/platform/surface.js +157 -0
  333. package/dist/platform/xr-fake.d.ts +168 -0
  334. package/dist/platform/xr-fake.js +434 -0
  335. package/dist/platform/xr-surface.d.ts +120 -0
  336. package/dist/platform/xr-surface.js +214 -0
  337. package/dist/platform/xr-types.d.ts +262 -0
  338. package/dist/post/ao.d.ts +12 -0
  339. package/dist/post/ao.js +136 -0
  340. package/dist/post/blend.d.ts +27 -0
  341. package/dist/post/blend.js +129 -0
  342. package/dist/post/depth-pass.d.ts +43 -0
  343. package/dist/post/depth-pass.js +118 -0
  344. package/dist/post/dof.d.ts +1 -0
  345. package/dist/post/dof.js +66 -0
  346. package/dist/post/exposure.d.ts +80 -0
  347. package/dist/post/exposure.js +322 -0
  348. package/dist/post/index.d.ts +12 -0
  349. package/dist/post/index.js +11 -0
  350. package/dist/post/lens-dirt.d.ts +13 -0
  351. package/dist/post/lens-dirt.js +91 -0
  352. package/dist/post/lut.d.ts +19 -0
  353. package/dist/post/lut.js +50 -0
  354. package/dist/post/motion-blur.d.ts +9 -0
  355. package/dist/post/motion-blur.js +78 -0
  356. package/dist/post/post-stack.d.ts +302 -0
  357. package/dist/post/post-stack.js +1137 -0
  358. package/dist/post/settings.d.ts +485 -0
  359. package/dist/post/settings.js +416 -0
  360. package/dist/post/shaders.d.ts +25 -0
  361. package/dist/post/shaders.js +410 -0
  362. package/dist/post/ssr.d.ts +16 -0
  363. package/dist/post/ssr.js +155 -0
  364. package/dist/post/taa.d.ts +74 -0
  365. package/dist/post/taa.js +235 -0
  366. package/dist/post/white-balance.d.ts +25 -0
  367. package/dist/post/white-balance.js +98 -0
  368. package/dist/render/bindings.d.ts +98 -0
  369. package/dist/render/bindings.js +281 -0
  370. package/dist/render/blend.d.ts +13 -0
  371. package/dist/render/blend.js +26 -0
  372. package/dist/render/camera-2d.d.ts +72 -0
  373. package/dist/render/camera-2d.js +92 -0
  374. package/dist/render/camera.d.ts +302 -0
  375. package/dist/render/camera.js +671 -0
  376. package/dist/render/color.d.ts +16 -0
  377. package/dist/render/color.js +41 -0
  378. package/dist/render/compressed.d.ts +104 -0
  379. package/dist/render/compressed.js +248 -0
  380. package/dist/render/diagnostics.d.ts +15 -0
  381. package/dist/render/diagnostics.js +43 -0
  382. package/dist/render/dynamic-resolution.d.ts +70 -0
  383. package/dist/render/dynamic-resolution.js +126 -0
  384. package/dist/render/fake-gpu.d.ts +204 -0
  385. package/dist/render/fake-gpu.js +426 -0
  386. package/dist/render/flipbook.d.ts +56 -0
  387. package/dist/render/flipbook.js +87 -0
  388. package/dist/render/frame.d.ts +278 -0
  389. package/dist/render/frame.js +174 -0
  390. package/dist/render/fullscreen.d.ts +54 -0
  391. package/dist/render/fullscreen.js +91 -0
  392. package/dist/render/gpu-timer.d.ts +73 -0
  393. package/dist/render/gpu-timer.js +186 -0
  394. package/dist/render/index.d.ts +53 -0
  395. package/dist/render/index.js +29 -0
  396. package/dist/render/instance-list.d.ts +185 -0
  397. package/dist/render/instance-list.js +547 -0
  398. package/dist/render/lights.d.ts +181 -0
  399. package/dist/render/lights.js +233 -0
  400. package/dist/render/lod.d.ts +54 -0
  401. package/dist/render/lod.js +52 -0
  402. package/dist/render/material.d.ts +165 -0
  403. package/dist/render/material.js +292 -0
  404. package/dist/render/mesh.d.ts +284 -0
  405. package/dist/render/mesh.js +472 -0
  406. package/dist/render/mipmap.d.ts +16 -0
  407. package/dist/render/mipmap.js +121 -0
  408. package/dist/render/parallax.d.ts +50 -0
  409. package/dist/render/parallax.js +47 -0
  410. package/dist/render/pbr-wgsl.d.ts +31 -0
  411. package/dist/render/pbr-wgsl.js +119 -0
  412. package/dist/render/ping-pong.d.ts +42 -0
  413. package/dist/render/ping-pong.js +74 -0
  414. package/dist/render/renderer.d.ts +741 -0
  415. package/dist/render/renderer.js +2486 -0
  416. package/dist/render/scene-pipelines.d.ts +44 -0
  417. package/dist/render/scene-pipelines.js +92 -0
  418. package/dist/render/scene-target.d.ts +170 -0
  419. package/dist/render/scene-target.js +450 -0
  420. package/dist/render/shadow.d.ts +74 -0
  421. package/dist/render/shadow.js +208 -0
  422. package/dist/render/skin.d.ts +112 -0
  423. package/dist/render/skin.js +240 -0
  424. package/dist/render/snapshot.d.ts +36 -0
  425. package/dist/render/snapshot.js +34 -0
  426. package/dist/render/standard.d.ts +162 -0
  427. package/dist/render/standard.js +263 -0
  428. package/dist/render/texture.d.ts +142 -0
  429. package/dist/render/texture.js +228 -0
  430. package/dist/render/tile-layer.d.ts +110 -0
  431. package/dist/render/tile-layer.js +304 -0
  432. package/dist/render/transform.d.ts +115 -0
  433. package/dist/render/transform.js +318 -0
  434. package/dist/render/wgsl.d.ts +131 -0
  435. package/dist/render/wgsl.js +528 -0
  436. package/dist/save/codec.d.ts +49 -0
  437. package/dist/save/codec.js +169 -0
  438. package/dist/save/index.d.ts +8 -0
  439. package/dist/save/index.js +8 -0
  440. package/dist/save/journal.d.ts +24 -0
  441. package/dist/save/journal.js +102 -0
  442. package/dist/save/migrate.d.ts +15 -0
  443. package/dist/save/migrate.js +37 -0
  444. package/dist/save/schema.d.ts +72 -0
  445. package/dist/save/schema.js +154 -0
  446. package/dist/save/settings.d.ts +28 -0
  447. package/dist/save/settings.js +59 -0
  448. package/dist/save/slots.d.ts +56 -0
  449. package/dist/save/slots.js +200 -0
  450. package/dist/save/storage.d.ts +35 -0
  451. package/dist/save/storage.js +62 -0
  452. package/dist/save/store.d.ts +114 -0
  453. package/dist/save/store.js +269 -0
  454. package/dist/testing/fake-host.d.ts +103 -0
  455. package/dist/testing/fake-host.js +163 -0
  456. package/dist/testing/fixed-clock.d.ts +25 -0
  457. package/dist/testing/fixed-clock.js +30 -0
  458. package/dist/testing/index.d.ts +4 -0
  459. package/dist/testing/index.js +5 -0
  460. package/dist/testing/test-game.d.ts +76 -0
  461. package/dist/testing/test-game.js +92 -0
  462. package/dist/tween/ease.d.ts +77 -0
  463. package/dist/tween/ease.js +192 -0
  464. package/dist/tween/index.d.ts +8 -0
  465. package/dist/tween/index.js +7 -0
  466. package/dist/tween/timeline.d.ts +78 -0
  467. package/dist/tween/timeline.js +269 -0
  468. package/dist/tween/tween.d.ts +168 -0
  469. package/dist/tween/tween.js +320 -0
  470. package/dist/ui/anchor.d.ts +206 -0
  471. package/dist/ui/anchor.js +307 -0
  472. package/dist/ui/caption.d.ts +122 -0
  473. package/dist/ui/caption.js +286 -0
  474. package/dist/ui/cursor.d.ts +105 -0
  475. package/dist/ui/cursor.js +261 -0
  476. package/dist/ui/dom.d.ts +30 -0
  477. package/dist/ui/dom.js +92 -0
  478. package/dist/ui/floats.d.ts +86 -0
  479. package/dist/ui/floats.js +260 -0
  480. package/dist/ui/hud.d.ts +198 -0
  481. package/dist/ui/hud.js +431 -0
  482. package/dist/ui/index.d.ts +37 -0
  483. package/dist/ui/index.js +34 -0
  484. package/dist/ui/inventory-card.d.ts +52 -0
  485. package/dist/ui/inventory-card.js +173 -0
  486. package/dist/ui/inventory-context.d.ts +29 -0
  487. package/dist/ui/inventory-context.js +125 -0
  488. package/dist/ui/inventory-drag.d.ts +63 -0
  489. package/dist/ui/inventory-drag.js +394 -0
  490. package/dist/ui/inventory-grid.d.ts +66 -0
  491. package/dist/ui/inventory-grid.js +625 -0
  492. package/dist/ui/inventory-radial.d.ts +39 -0
  493. package/dist/ui/inventory-radial.js +168 -0
  494. package/dist/ui/inventory.d.ts +128 -0
  495. package/dist/ui/inventory.js +154 -0
  496. package/dist/ui/layout.d.ts +356 -0
  497. package/dist/ui/layout.js +856 -0
  498. package/dist/ui/link.d.ts +64 -0
  499. package/dist/ui/link.js +155 -0
  500. package/dist/ui/loading.d.ts +40 -0
  501. package/dist/ui/loading.js +157 -0
  502. package/dist/ui/loupe.d.ts +74 -0
  503. package/dist/ui/loupe.js +164 -0
  504. package/dist/ui/menu-nav.d.ts +33 -0
  505. package/dist/ui/menu-nav.js +28 -0
  506. package/dist/ui/minimap-canvas.d.ts +145 -0
  507. package/dist/ui/minimap-canvas.js +460 -0
  508. package/dist/ui/notify.d.ts +40 -0
  509. package/dist/ui/notify.js +141 -0
  510. package/dist/ui/orb.d.ts +53 -0
  511. package/dist/ui/orb.js +304 -0
  512. package/dist/ui/overlay.d.ts +213 -0
  513. package/dist/ui/overlay.js +394 -0
  514. package/dist/ui/padnav.d.ts +53 -0
  515. package/dist/ui/padnav.js +146 -0
  516. package/dist/ui/perf.d.ts +130 -0
  517. package/dist/ui/perf.js +329 -0
  518. package/dist/ui/rotate-card.d.ts +54 -0
  519. package/dist/ui/rotate-card.js +97 -0
  520. package/dist/ui/stack.d.ts +128 -0
  521. package/dist/ui/stack.js +312 -0
  522. package/dist/ui/styles.d.ts +13 -0
  523. package/dist/ui/styles.js +200 -0
  524. package/dist/ui/text-scale.d.ts +67 -0
  525. package/dist/ui/text-scale.js +102 -0
  526. package/dist/ui/theme.d.ts +70 -0
  527. package/dist/ui/theme.js +98 -0
  528. package/dist/ui/toast.d.ts +23 -0
  529. package/dist/ui/toast.js +46 -0
  530. package/dist/ui/tooltip.d.ts +89 -0
  531. package/dist/ui/tooltip.js +333 -0
  532. package/dist/ui/widgets-2.d.ts +134 -0
  533. package/dist/ui/widgets-2.js +399 -0
  534. package/dist/ui/widgets-3.d.ts +91 -0
  535. package/dist/ui/widgets-3.js +212 -0
  536. package/dist/ui/widgets.d.ts +34 -0
  537. package/dist/ui/widgets.js +50 -0
  538. package/dist/ui/world-panel/index.d.ts +5 -0
  539. package/dist/ui/world-panel/index.js +5 -0
  540. package/dist/ui/world-panel/layout.d.ts +38 -0
  541. package/dist/ui/world-panel/layout.js +68 -0
  542. package/dist/ui/world-panel/paint.d.ts +96 -0
  543. package/dist/ui/world-panel/paint.js +136 -0
  544. package/dist/ui/world-panel/panel.d.ts +235 -0
  545. package/dist/ui/world-panel/panel.js +762 -0
  546. package/dist/ui/world-panel/wgsl.d.ts +10 -0
  547. package/dist/ui/world-panel/wgsl.js +52 -0
  548. package/dist/ui/world-panel/widgets.d.ts +207 -0
  549. package/dist/ui/world-panel/widgets.js +569 -0
  550. package/dist/voxel/chunks.d.ts +77 -0
  551. package/dist/voxel/chunks.js +149 -0
  552. package/dist/voxel/grid.d.ts +84 -0
  553. package/dist/voxel/grid.js +223 -0
  554. package/dist/voxel/index.d.ts +22 -0
  555. package/dist/voxel/index.js +7 -0
  556. package/dist/voxel/mesher-worker.d.ts +14 -0
  557. package/dist/voxel/mesher-worker.js +47 -0
  558. package/dist/voxel/mesher.d.ts +73 -0
  559. package/dist/voxel/mesher.js +306 -0
  560. package/dist/voxel/ray.d.ts +32 -0
  561. package/dist/voxel/ray.js +73 -0
  562. package/dist/voxel/wgsl.d.ts +16 -0
  563. package/dist/voxel/wgsl.js +28 -0
  564. package/dist/voxel/worker-client.d.ts +79 -0
  565. package/dist/voxel/worker-client.js +122 -0
  566. package/package.json +177 -3
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Pure sample-level helpers: seeded noise, crackle impulses, loop folding, normalisation,
3
+ * unit conversions. No WebAudio here, so all of it runs (and is tested) in Node.
4
+ */
5
+ /** Deterministic PRNG (mulberry32). Returns floats in [0, 1). */
6
+ export declare function rng(seed: number): () => number;
7
+ export type NoiseColor = 'white' | 'pink' | 'brown';
8
+ /** Fill `out` with noise in roughly [-1, 1]. Same seed, same samples. */
9
+ export declare function fillNoise(out: Float32Array, color: NoiseColor, seed?: number): Float32Array;
10
+ /**
11
+ * Sparse random clicks, like embers or static. `density` = average clicks per second; each
12
+ * click is a short decaying noise burst of `clickDecay` seconds with random amplitude.
13
+ */
14
+ export declare function fillCrackle(out: Float32Array, sampleRate: number, density: number, seed?: number, clickDecay?: number): Float32Array;
15
+ /**
16
+ * Make a seamless loop: `data` holds `loopLen + fade` samples. The last `fade` samples are
17
+ * crossfaded (equal power) into the first `fade`, and the result is the first `loopLen` samples.
18
+ * Returns a new array of length `loopLen`.
19
+ */
20
+ export declare function foldLoop(data: Float32Array, loopLen: number, fade: number): Float32Array<ArrayBuffer>;
21
+ /** Largest absolute sample across channels. */
22
+ export declare function peakOf(channels: readonly Float32Array[]): number;
23
+ /** Scale all channels so the peak equals `target`. No-op on silence. Returns the gain applied. */
24
+ export declare function normalize(channels: readonly Float32Array[], target?: number): number;
25
+ /** Round `freq` so a whole number of cycles fits in `duration` (clean loops for tonal layers). */
26
+ export declare function loopSafeFreq(freq: number, duration: number): number;
27
+ export declare const midiToHz: (note: number) => number;
28
+ export declare const semitones: (freq: number, st: number) => number;
29
+ export declare const dbToGain: (db: number) => number;
30
+ export declare const gainToDb: (g: number) => number;
31
+ /**
32
+ * Slider position (0..1) to linear gain. Squared curve: 0.5 on the slider ≈ -12 dB, which
33
+ * feels much closer to "half as loud" than linear gain does.
34
+ */
35
+ export declare function volumeToGain(volume: number): number;
36
+ /**
37
+ * Procedural reverb impulse response: decaying seeded noise, independent per channel (stereo
38
+ * width), with a short fade-in so the onset doesn't click. `decay` shapes the tail
39
+ * (`(1 - t)^decay`; higher = shorter, darker room). Same arguments, same samples.
40
+ */
41
+ export declare function reverbImpulse(sampleRate: number, seconds?: number, decay?: number, seed?: number, channels?: number): Float32Array<ArrayBuffer>[];
42
+ /** The part of a `BaseAudioContext` that `impulseResponse` needs (a fake will do in tests). */
43
+ export interface BufferFactory {
44
+ readonly sampleRate: number;
45
+ createBuffer(channels: number, length: number, sampleRate: number): AudioBuffer;
46
+ }
47
+ export interface ImpulseResponseOptions {
48
+ /** Impulse length in seconds. Default 2. */
49
+ seconds?: number;
50
+ /** Tail shape: `(1 - t)^decay`. Higher = shorter, drier. Default 3. */
51
+ decay?: number;
52
+ /** Noise seed. Default 1. */
53
+ seed?: number;
54
+ /** Default 2 (stereo width from independent channels). */
55
+ channels?: number;
56
+ }
57
+ /**
58
+ * A procedural reverb impulse as an `AudioBuffer` on `ctx` (a `ConvolverNode.buffer`), built from
59
+ * `reverbImpulse`: no asset to load. Same options, same samples.
60
+ */
61
+ export declare function impulseResponse(ctx: BufferFactory, opts?: ImpulseResponseOptions): AudioBuffer;
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Pure sample-level helpers: seeded noise, crackle impulses, loop folding, normalisation,
3
+ * unit conversions. No WebAudio here, so all of it runs (and is tested) in Node.
4
+ */
5
+ /** Deterministic PRNG (mulberry32). Returns floats in [0, 1). */
6
+ export function rng(seed) {
7
+ let a = seed >>> 0;
8
+ return () => {
9
+ a = (a + 0x6d2b79f5) >>> 0;
10
+ let t = a;
11
+ t = Math.imul(t ^ (t >>> 15), t | 1);
12
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
13
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
14
+ };
15
+ }
16
+ /** Fill `out` with noise in roughly [-1, 1]. Same seed, same samples. */
17
+ export function fillNoise(out, color, seed = 1) {
18
+ const rand = rng(seed);
19
+ if (color === 'white') {
20
+ for (let i = 0; i < out.length; i++)
21
+ out[i] = rand() * 2 - 1;
22
+ return out;
23
+ }
24
+ if (color === 'pink') {
25
+ // Paul Kellet's economy pink filter.
26
+ let b0 = 0;
27
+ let b1 = 0;
28
+ let b2 = 0;
29
+ for (let i = 0; i < out.length; i++) {
30
+ const w = rand() * 2 - 1;
31
+ b0 = 0.99765 * b0 + w * 0.099046;
32
+ b1 = 0.963 * b1 + w * 0.2965164;
33
+ b2 = 0.57 * b2 + w * 1.0526913;
34
+ out[i] = (b0 + b1 + b2 + w * 0.1848) * 0.2;
35
+ }
36
+ return out;
37
+ }
38
+ // Brown: leaky integrated white noise.
39
+ let last = 0;
40
+ for (let i = 0; i < out.length; i++) {
41
+ const w = rand() * 2 - 1;
42
+ last = (last + 0.02 * w) / 1.02;
43
+ out[i] = last * 3.5;
44
+ }
45
+ return out;
46
+ }
47
+ /**
48
+ * Sparse random clicks, like embers or static. `density` = average clicks per second; each
49
+ * click is a short decaying noise burst of `clickDecay` seconds with random amplitude.
50
+ */
51
+ export function fillCrackle(out, sampleRate, density, seed = 1, clickDecay = 0.004) {
52
+ out.fill(0);
53
+ const rand = rng(seed);
54
+ const p = density / sampleRate;
55
+ const decayN = Math.max(1, Math.round(clickDecay * sampleRate));
56
+ const k = Math.exp(-5 / decayN);
57
+ for (let i = 0; i < out.length; i++) {
58
+ if (rand() >= p)
59
+ continue;
60
+ let amp = (0.3 + 0.7 * rand()) * (rand() < 0.5 ? -1 : 1);
61
+ const len = Math.min(decayN * 2, out.length - i);
62
+ for (let j = 0; j < len; j++) {
63
+ out[i + j] += amp * (rand() * 2 - 1);
64
+ amp *= k;
65
+ }
66
+ }
67
+ return out;
68
+ }
69
+ /**
70
+ * Make a seamless loop: `data` holds `loopLen + fade` samples. The last `fade` samples are
71
+ * crossfaded (equal power) into the first `fade`, and the result is the first `loopLen` samples.
72
+ * Returns a new array of length `loopLen`.
73
+ */
74
+ export function foldLoop(data, loopLen, fade) {
75
+ if (fade < 0 || loopLen + fade > data.length)
76
+ throw new RangeError('foldLoop: not enough samples');
77
+ const out = new Float32Array(loopLen);
78
+ out.set(data.subarray(0, loopLen));
79
+ for (let i = 0; i < fade; i++) {
80
+ const x = (i + 0.5) / fade;
81
+ const fadeIn = Math.sin((x * Math.PI) / 2);
82
+ const fadeOut = Math.cos((x * Math.PI) / 2);
83
+ out[i] = data[i] * fadeIn + data[loopLen + i] * fadeOut;
84
+ }
85
+ return out;
86
+ }
87
+ /** Largest absolute sample across channels. */
88
+ export function peakOf(channels) {
89
+ let peak = 0;
90
+ for (const ch of channels) {
91
+ for (let i = 0; i < ch.length; i++) {
92
+ const v = Math.abs(ch[i]);
93
+ if (v > peak)
94
+ peak = v;
95
+ }
96
+ }
97
+ return peak;
98
+ }
99
+ /** Scale all channels so the peak equals `target`. No-op on silence. Returns the gain applied. */
100
+ export function normalize(channels, target = 0.9) {
101
+ const peak = peakOf(channels);
102
+ if (peak < 1e-9)
103
+ return 1;
104
+ const g = target / peak;
105
+ for (const ch of channels)
106
+ for (let i = 0; i < ch.length; i++)
107
+ ch[i] *= g;
108
+ return g;
109
+ }
110
+ /** Round `freq` so a whole number of cycles fits in `duration` (clean loops for tonal layers). */
111
+ export function loopSafeFreq(freq, duration) {
112
+ if (duration <= 0)
113
+ return freq;
114
+ return Math.max(1, Math.round(freq * duration)) / duration;
115
+ }
116
+ export const midiToHz = (note) => 440 * 2 ** ((note - 69) / 12);
117
+ export const semitones = (freq, st) => freq * 2 ** (st / 12);
118
+ export const dbToGain = (db) => 10 ** (db / 20);
119
+ export const gainToDb = (g) => (g <= 0 ? -Infinity : 20 * Math.log10(g));
120
+ /**
121
+ * Slider position (0..1) to linear gain. Squared curve: 0.5 on the slider ≈ -12 dB, which
122
+ * feels much closer to "half as loud" than linear gain does.
123
+ */
124
+ export function volumeToGain(volume) {
125
+ const v = Math.min(1, Math.max(0, Number.isFinite(volume) ? volume : 0));
126
+ return v * v;
127
+ }
128
+ /**
129
+ * Procedural reverb impulse response: decaying seeded noise, independent per channel (stereo
130
+ * width), with a short fade-in so the onset doesn't click. `decay` shapes the tail
131
+ * (`(1 - t)^decay`; higher = shorter, darker room). Same arguments, same samples.
132
+ */
133
+ export function reverbImpulse(sampleRate, seconds = 2, decay = 3, seed = 1, channels = 2) {
134
+ const n = Math.max(1, Math.round(sampleRate * Math.max(0.01, seconds)));
135
+ const fadeIn = Math.min(n, Math.max(1, Math.round(sampleRate * 0.002)));
136
+ const out = [];
137
+ for (let c = 0; c < channels; c++) {
138
+ const r = rng(seed * 7919 + c * 104729);
139
+ const ch = new Float32Array(n);
140
+ for (let i = 0; i < n; i++) {
141
+ const env = (1 - i / n) ** decay * Math.min(1, (i + 1) / fadeIn);
142
+ ch[i] = (r() * 2 - 1) * env;
143
+ }
144
+ out.push(ch);
145
+ }
146
+ return out;
147
+ }
148
+ /**
149
+ * A procedural reverb impulse as an `AudioBuffer` on `ctx` (a `ConvolverNode.buffer`), built from
150
+ * `reverbImpulse`: no asset to load. Same options, same samples.
151
+ */
152
+ export function impulseResponse(ctx, opts = {}) {
153
+ const ir = reverbImpulse(ctx.sampleRate, opts.seconds ?? 2, opts.decay ?? 3, opts.seed ?? 1, opts.channels ?? 2);
154
+ const buffer = ctx.createBuffer(ir.length, ir[0].length, ctx.sampleRate);
155
+ ir.forEach((ch, i) => {
156
+ buffer.copyToChannel(ch, i);
157
+ });
158
+ return buffer;
159
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Ducking: while anything plays on a trigger bus (dialogue on `ui`), the target bus (music)
3
+ * drops by `amount`, with an attack and a release. The level is a linear envelope advanced by
4
+ * `update(dt)` and handed to the mixer as the target bus's scale under the ducker's own key
5
+ * (`setBusScale`; keys on one bus multiply, so two duckers combine), so the mixer
6
+ * owns the write: the bus keeps following `setVolume` and `setMuted`, stays silent while
7
+ * `pause` holds it, and `unpause` lands on the ducked gain.
8
+ */
9
+ import type { AudioEngine, OutputBus, Voice } from './audio.js';
10
+ export interface DuckerOptions {
11
+ /** Bus whose activity ducks the target. */
12
+ trigger: OutputBus;
13
+ /** Bus that is lowered. */
14
+ target: OutputBus;
15
+ /** Fraction of the target's gain removed when fully ducked, 0..1. Default 0.6. */
16
+ amount?: number;
17
+ /** Seconds from idle to fully ducked. Default 0.1. */
18
+ attack?: number;
19
+ /** Seconds from fully ducked back to idle. Default 0.6. */
20
+ release?: number;
21
+ /**
22
+ * Sidechain threshold in dB. When set (and the context has `createAnalyser`), an analyser on
23
+ * the trigger bus counts as active while its RMS is above this level, so voices nobody passed
24
+ * to `watch` still duck. Off by default: activity comes from `watch`.
25
+ */
26
+ threshold?: number;
27
+ }
28
+ export interface Ducker {
29
+ /** Count `voice` as activity on the trigger bus while it plays. Returns the voice. */
30
+ watch<V extends Voice>(voice: V): V;
31
+ /** Advance the envelope and write the target bus gain. Call once per frame. */
32
+ update(dt: number): void;
33
+ /** True while the trigger bus is active (a watched voice plays, or the sidechain is above threshold). */
34
+ readonly active: boolean;
35
+ /** Duck envelope 0 (idle) .. 1 (fully ducked). */
36
+ readonly level: number;
37
+ /** Multiplier applied to the target bus right now, `1 - amount × level`. */
38
+ readonly gain: number;
39
+ /** This ducker's own `setBusScale` key on the target bus (`'ducker:<n>'`). */
40
+ readonly key: string;
41
+ /** Clear this ducker's scale on the target bus (`clearBusScale`) and stop. */
42
+ dispose(): void;
43
+ }
44
+ export declare function createDucker(mixer: AudioEngine, opts: DuckerOptions): Ducker;
@@ -0,0 +1,101 @@
1
+ const ANALYSER_SIZE = 256;
2
+ /** Source of each ducker's own `setBusScale` key, so duckers on one target multiply. */
3
+ let nextDuckerId = 0;
4
+ export function createDucker(mixer, opts) {
5
+ const amount = Math.min(1, Math.max(0, opts.amount ?? 0.6));
6
+ const attack = Math.max(0.001, opts.attack ?? 0.1);
7
+ const release = Math.max(0.001, opts.release ?? 0.6);
8
+ const threshold = opts.threshold;
9
+ const watched = [];
10
+ let level = 0;
11
+ let active = false;
12
+ let analyser = null;
13
+ let analyserOn = null;
14
+ const samples = new Float32Array(ANALYSER_SIZE);
15
+ let disposed = false;
16
+ const key = `ducker:${nextDuckerId++}`;
17
+ function ensureAnalyser() {
18
+ const ctx = mixer.context;
19
+ if (threshold === undefined || !ctx || analyserOn === ctx)
20
+ return;
21
+ const bus = mixer.busNode(opts.trigger);
22
+ if (!bus || typeof ctx.createAnalyser !== 'function')
23
+ return;
24
+ const a = ctx.createAnalyser();
25
+ a.fftSize = ANALYSER_SIZE;
26
+ bus.connect(a);
27
+ analyser = a;
28
+ analyserOn = ctx;
29
+ }
30
+ function sidechainActive() {
31
+ if (!analyser || threshold === undefined)
32
+ return false;
33
+ analyser.getFloatTimeDomainData(samples);
34
+ let sum = 0;
35
+ for (let i = 0; i < samples.length; i++)
36
+ sum += samples[i] * samples[i];
37
+ const rms = Math.sqrt(sum / samples.length);
38
+ return rms > 10 ** (threshold / 20);
39
+ }
40
+ function anyWatched() {
41
+ // Drop finished voices in place (no allocation).
42
+ let w = 0;
43
+ for (let i = 0; i < watched.length; i++) {
44
+ const v = watched[i];
45
+ if (v.playing)
46
+ watched[w++] = v;
47
+ }
48
+ watched.length = w;
49
+ return w > 0;
50
+ }
51
+ function write() {
52
+ // The mixer skips an unchanged scale and holds the write while paused or in the background.
53
+ mixer.setBusScale(opts.target, 1 - amount * level, 0.005, key);
54
+ }
55
+ return {
56
+ watch(voice) {
57
+ if (!disposed)
58
+ watched.push(voice);
59
+ return voice;
60
+ },
61
+ update(dt) {
62
+ if (disposed)
63
+ return;
64
+ ensureAnalyser();
65
+ active = anyWatched() || sidechainActive();
66
+ const step = Math.max(0, dt);
67
+ let next = active ? level + step / attack : level - step / release;
68
+ // Snap at the ends so float residue never leaves a bus a hair below its volume.
69
+ if (next > 1 - 1e-6)
70
+ next = 1;
71
+ else if (next < 1e-6)
72
+ next = 0;
73
+ if (next === level)
74
+ return;
75
+ level = next;
76
+ write();
77
+ },
78
+ get active() {
79
+ return active;
80
+ },
81
+ get level() {
82
+ return level;
83
+ },
84
+ get gain() {
85
+ return 1 - amount * level;
86
+ },
87
+ key,
88
+ dispose() {
89
+ if (disposed)
90
+ return;
91
+ disposed = true;
92
+ level = 0;
93
+ active = false;
94
+ // Drop this ducker's key: the target goes back to the product of the other keys.
95
+ mixer.clearBusScale(opts.target, key, 0.005);
96
+ analyser?.disconnect();
97
+ analyser = null;
98
+ watched.length = 0;
99
+ },
100
+ };
101
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * ADSR envelopes. One source of truth: `envelopeSegments` produces breakpoints, which
3
+ * `envelopeValueAt` evaluates (pure, tested) and `scheduleEnvelope` writes to an `AudioParam`
4
+ * with the matching automation calls, so what we test is what plays.
5
+ */
6
+ export interface Envelope {
7
+ /** Seconds from 0 to peak (always linear). */
8
+ attack: number;
9
+ /** Seconds held at peak. Default 0. */
10
+ hold?: number;
11
+ /** Seconds from peak to `sustain`. */
12
+ decay: number;
13
+ /** Sustain level, 0..1 of peak. */
14
+ sustain: number;
15
+ /** Seconds from the gate-off value to silence. */
16
+ release: number;
17
+ /** Shape of decay and release. `exp` sounds natural for percussive sounds. Default `exp`. */
18
+ curve?: 'linear' | 'exp';
19
+ }
20
+ /** A breakpoint: reach `v` at time `t`, ramping from the previous point with `ramp`. */
21
+ export interface Segment {
22
+ t: number;
23
+ v: number;
24
+ ramp: 'set' | 'linear' | 'exp';
25
+ }
26
+ /** Smallest level an exponential ramp can target (WebAudio can't ramp exponentially to 0). */
27
+ export declare const EXP_FLOOR = 0.0001;
28
+ /** Shorthand for a percussive envelope: instant-ish attack, exponential fall. */
29
+ export declare function pluck(decay: number, attack?: number): Envelope;
30
+ /** Total audible length for a given gate (time from note-on to note-off). */
31
+ export declare function envelopeLength(env: Envelope, gate: number): number;
32
+ /** Evaluate a breakpoint list at time `t` (relative to its start). */
33
+ export declare function evaluateSegments(segs: readonly Segment[], t: number): number;
34
+ /**
35
+ * Breakpoints for `env` scaled by `peak`, starting at t=0, with note-off at `gate` seconds.
36
+ * If the gate closes during attack/decay, release starts from the value at that moment.
37
+ * The last point is exactly 0.
38
+ */
39
+ export declare function envelopeSegments(env: Envelope, gate: number, peak?: number): Segment[];
40
+ /** Peak-normalised value of `env` at time `t` for a note with the given gate. */
41
+ export declare function envelopeValueAt(env: Envelope, t: number, gate: number, peak?: number): number;
42
+ /** The subset of `AudioParam` used for scheduling (lets tests record calls). */
43
+ export interface ParamLike {
44
+ value: number;
45
+ setValueAtTime(v: number, t: number): unknown;
46
+ linearRampToValueAtTime(v: number, t: number): unknown;
47
+ exponentialRampToValueAtTime(v: number, t: number): unknown;
48
+ cancelScheduledValues(t: number): unknown;
49
+ }
50
+ /** Write breakpoints to an `AudioParam`, offset by `start` (context time). */
51
+ export declare function scheduleSegments(param: ParamLike, segs: readonly Segment[], start: number): void;
52
+ /** Schedule `env` on `param` (usually a GainNode's gain). Returns the end time. */
53
+ export declare function scheduleEnvelope(param: ParamLike, env: Envelope, start: number, gate: number, peak?: number): number;
@@ -0,0 +1,106 @@
1
+ /**
2
+ * ADSR envelopes. One source of truth: `envelopeSegments` produces breakpoints, which
3
+ * `envelopeValueAt` evaluates (pure, tested) and `scheduleEnvelope` writes to an `AudioParam`
4
+ * with the matching automation calls, so what we test is what plays.
5
+ */
6
+ /** Smallest level an exponential ramp can target (WebAudio can't ramp exponentially to 0). */
7
+ export const EXP_FLOOR = 1e-4;
8
+ /** Shorthand for a percussive envelope: instant-ish attack, exponential fall. */
9
+ export function pluck(decay, attack = 0.002) {
10
+ return { attack, decay, sustain: 0, release: 0.01, curve: 'exp' };
11
+ }
12
+ /** Total audible length for a given gate (time from note-on to note-off). */
13
+ export function envelopeLength(env, gate) {
14
+ return Math.max(0, gate) + env.release;
15
+ }
16
+ function interp(a, b, t) {
17
+ if (t >= b.t)
18
+ return b.v;
19
+ if (t <= a.t)
20
+ return a.v;
21
+ const p = (t - a.t) / (b.t - a.t);
22
+ if (b.ramp === 'set')
23
+ return a.v;
24
+ if (b.ramp === 'linear')
25
+ return a.v + (b.v - a.v) * p;
26
+ const v0 = Math.max(a.v, EXP_FLOOR);
27
+ const v1 = Math.max(b.v, EXP_FLOOR);
28
+ return v0 * (v1 / v0) ** p;
29
+ }
30
+ /** Evaluate a breakpoint list at time `t` (relative to its start). */
31
+ export function evaluateSegments(segs, t) {
32
+ if (segs.length === 0 || t < segs[0].t)
33
+ return 0;
34
+ for (let i = 1; i < segs.length; i++) {
35
+ if (t < segs[i].t)
36
+ return interp(segs[i - 1], segs[i], t);
37
+ }
38
+ return segs[segs.length - 1].v;
39
+ }
40
+ /**
41
+ * Breakpoints for `env` scaled by `peak`, starting at t=0, with note-off at `gate` seconds.
42
+ * If the gate closes during attack/decay, release starts from the value at that moment.
43
+ * The last point is exactly 0.
44
+ */
45
+ export function envelopeSegments(env, gate, peak = 1) {
46
+ const exp = (env.curve ?? 'exp') === 'exp';
47
+ const a = Math.max(0, env.attack);
48
+ const h = Math.max(0, env.hold ?? 0);
49
+ const d = Math.max(0, env.decay);
50
+ const r = Math.max(1e-4, env.release);
51
+ const s = Math.min(1, Math.max(0, env.sustain)) * peak;
52
+ const g = Math.max(0, gate);
53
+ const full = [{ t: 0, v: 0, ramp: 'set' }];
54
+ if (a > 0)
55
+ full.push({ t: a, v: peak, ramp: 'linear' });
56
+ else
57
+ full[0] = { t: 0, v: peak, ramp: 'set' };
58
+ if (h > 0)
59
+ full.push({ t: a + h, v: peak, ramp: 'linear' });
60
+ if (d > 0)
61
+ full.push({ t: a + h + d, v: exp ? Math.max(s, EXP_FLOOR) : s, ramp: exp ? 'exp' : 'linear' });
62
+ else if (s !== peak)
63
+ full.push({ t: a + h, v: s, ramp: 'set' });
64
+ // Keep points strictly before the gate, then a point at the gate with the curve's value there.
65
+ const out = full.filter((p) => p.t < g || (p.t === 0 && g === 0));
66
+ const atGate = evaluateSegments(full, g);
67
+ const next = full.find((p) => p.t >= g);
68
+ if (g > 0 || out.length === 0) {
69
+ out.push({ t: g, v: atGate, ramp: next ? next.ramp : 'linear' });
70
+ }
71
+ // A 'set' point at the gate would jump; at the gate we already hold atGate, so mark it linear.
72
+ const last = out[out.length - 1];
73
+ if (last.ramp === 'set' && last.t > 0)
74
+ last.ramp = 'linear';
75
+ if (exp && atGate > EXP_FLOOR) {
76
+ out.push({ t: g + r, v: EXP_FLOOR, ramp: 'exp' });
77
+ out.push({ t: g + r, v: 0, ramp: 'set' });
78
+ }
79
+ else {
80
+ out.push({ t: g + r, v: 0, ramp: 'linear' });
81
+ }
82
+ return out;
83
+ }
84
+ /** Peak-normalised value of `env` at time `t` for a note with the given gate. */
85
+ export function envelopeValueAt(env, t, gate, peak = 1) {
86
+ return evaluateSegments(envelopeSegments(env, gate, peak), t);
87
+ }
88
+ /** Write breakpoints to an `AudioParam`, offset by `start` (context time). */
89
+ export function scheduleSegments(param, segs, start) {
90
+ param.cancelScheduledValues(start);
91
+ for (const p of segs) {
92
+ const t = start + p.t;
93
+ if (p.ramp === 'set')
94
+ param.setValueAtTime(p.v, t);
95
+ else if (p.ramp === 'linear')
96
+ param.linearRampToValueAtTime(p.v, t);
97
+ else
98
+ param.exponentialRampToValueAtTime(Math.max(EXP_FLOOR, p.v), t);
99
+ }
100
+ }
101
+ /** Schedule `env` on `param` (usually a GainNode's gain). Returns the end time. */
102
+ export function scheduleEnvelope(param, env, start, gate, peak = 1) {
103
+ const segs = envelopeSegments(env, gate, peak);
104
+ scheduleSegments(param, segs, start);
105
+ return start + segs[segs.length - 1].t;
106
+ }
@@ -0,0 +1,13 @@
1
+ export { type AttachAudioOptions, AudioEngine, type AudioEngineOptions, type AudioFollowTarget, type AudioPlayOptions, type AudioState, BUS_NAMES, type BusName, type BusSettings, busGain, type CameraLike, type ConeOptions, createVoiceAdapter, DEFAULT_LIMITER, DEFAULT_MAX_DOPPLER_SPEED, DEFAULT_MIXER, DEFAULT_REVERB_SENDS, type LimiterOptions, MAX_VOICE_SENDS, type MixerSettings, type OutputBus, type PauseOptions, type ReverbOptions, type Rolloff, SPEED_OF_SOUND, SpatialListener, type SpatialOptions, type StopAllOptions, sanitizeMixer, type Voice, type VoiceAdapter, type VoiceAdapterOptions, type VoiceFilterKind, type VoiceFilterOptions, } from './audio.js';
2
+ export { type BufferFactory, dbToGain, fillCrackle, fillNoise, foldLoop, gainToDb, type ImpulseResponseOptions, impulseResponse, loopSafeFreq, midiToHz, type NoiseColor, normalize, peakOf, reverbImpulse, rng, semitones, volumeToGain, } from './dsp.js';
3
+ export { createDucker, type Ducker, type DuckerOptions } from './duck.js';
4
+ export { type Envelope, envelopeLength, envelopeSegments, envelopeValueAt, pluck, type Segment, scheduleEnvelope, } from './envelope.js';
5
+ export { createMusic, intensityGains, type Music, type MusicOptions, type MusicPlayOptions, type MusicStart, type Stem, type StreamElement, } from './music.js';
6
+ export { DEFAULT_OCCLUSION, type OccludableVoice, type OcclusionOptions, type OcclusionResult, OcclusionScheduler, occlusionCutoff, occlusionGain, } from './occlusion.js';
7
+ export { type PersistMixerOptions, persistMixer } from './persist.js';
8
+ export { chime, click, crackleLoop, dronePad, type RecipeName, type RecipeOptions, recipes, sparkle, thud, whoosh, zap, } from './recipes.js';
9
+ export { createSend, type Send, type SendKind, type SendOptions } from './send.js';
10
+ export { parseSpriteRef, type SpriteRange, type SpriteSchedule, type SpriteSheet, type SpriteSidecar, spriteSchedule, spriteSheetFrom, } from './sprites.js';
11
+ export { type CrackleSource, type FilterSpec, type Layer, type NoiseSource, type OfflineContextFactory, type OscSource, renderSound, type SoundSpec, type Source, validateSpec, type Wave, } from './synth.js';
12
+ export { createVariations, type Variations, type VariationsOptions, } from './variations.js';
13
+ export { type AudioZone, type AudioZoneDesc, type AudioZoneHost, AudioZones, type ZoneShape, zoneWeight, } from './zones.js';
@@ -0,0 +1,14 @@
1
+ // ws/audio-4
2
+ export { AudioEngine, BUS_NAMES, busGain, createVoiceAdapter, DEFAULT_LIMITER, DEFAULT_MAX_DOPPLER_SPEED, DEFAULT_MIXER, DEFAULT_REVERB_SENDS, MAX_VOICE_SENDS, SPEED_OF_SOUND, SpatialListener, sanitizeMixer, } from './audio.js';
3
+ export { dbToGain, fillCrackle, fillNoise, foldLoop, gainToDb, impulseResponse, loopSafeFreq, midiToHz, normalize, peakOf, reverbImpulse, rng, semitones, volumeToGain, } from './dsp.js';
4
+ export { createDucker } from './duck.js';
5
+ export { envelopeLength, envelopeSegments, envelopeValueAt, pluck, scheduleEnvelope, } from './envelope.js';
6
+ export { createMusic, intensityGains, } from './music.js';
7
+ export { DEFAULT_OCCLUSION, OcclusionScheduler, occlusionCutoff, occlusionGain, } from './occlusion.js';
8
+ export { persistMixer } from './persist.js';
9
+ export { chime, click, crackleLoop, dronePad, recipes, sparkle, thud, whoosh, zap, } from './recipes.js';
10
+ export { createSend } from './send.js';
11
+ export { parseSpriteRef, spriteSchedule, spriteSheetFrom, } from './sprites.js';
12
+ export { renderSound, validateSpec, } from './synth.js';
13
+ export { createVariations, } from './variations.js';
14
+ export { AudioZones, zoneWeight, } from './zones.js';
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Music on a beat grid: looping stems started sample-accurately on the next beat or bar from the
3
+ * context clock, grouped in layers, crossfaded by `fadeBeats` and by an intensity level. Sits on
4
+ * the mixer's buses; it owns its own `AudioBufferSourceNode`s so they can start at an offset
5
+ * that keeps every stem in phase with the grid. Each playing stem is registered with the mixer
6
+ * through a `Voice` adapter, so `audio.voices('music')`, `stopAll` and `pause` see it.
7
+ *
8
+ * A stem with a `url` can also be streamed (`play(name, { stream: true })`, ws/audio-4): a
9
+ * `MediaElementAudioSourceNode` over an `<audio>` element the page decodes as it goes, so a long
10
+ * track costs no decoded buffer. The grid then follows the element's `currentTime`.
11
+ */
12
+ import { type AudioEngine, type OutputBus } from './audio.js';
13
+ export interface MusicOptions {
14
+ bpm: number;
15
+ /** Beats in a bar. Default 4. */
16
+ beatsPerBar?: number;
17
+ /** Bus the stems play on. Default `music`. */
18
+ bus?: OutputBus;
19
+ /** Beats a `setIntensity` crossfade takes. Default 1. */
20
+ intensityFadeBeats?: number;
21
+ /**
22
+ * Makes the media element a streamed stem plays through. Default `document.createElement('audio')`
23
+ * where a document exists; without one (and without `createMediaElementSource` on the
24
+ * context) streamed stems fall back to their buffer.
25
+ */
26
+ createElement?: () => StreamElement;
27
+ }
28
+ /** What music needs from an `<audio>` element (a fake in tests). */
29
+ export interface StreamElement {
30
+ src: string;
31
+ loop: boolean;
32
+ currentTime: number;
33
+ /** NaN until the metadata loaded. */
34
+ readonly duration: number;
35
+ readonly paused: boolean;
36
+ playbackRate: number;
37
+ play(): Promise<void> | void;
38
+ pause(): void;
39
+ }
40
+ /** A looping piece of music: a buffer (or a streamable URL) whose length is a whole number of bars. */
41
+ export interface Stem {
42
+ name: string;
43
+ /** The decoded loop. Optional when `url` is set and the stem is only ever streamed. */
44
+ buffer?: AudioBuffer;
45
+ /** A URL to stream the stem from (`play(name, { stream: true })`). */
46
+ url?: string;
47
+ /**
48
+ * Layer the stem belongs to (default: its own name). Within a layer, stems with an `intensity`
49
+ * range are mixed by `setIntensity`; a stem without one replaces the layer's other rangeless
50
+ * stem when played (a crossfade).
51
+ */
52
+ layer?: string;
53
+ /**
54
+ * Intensity range `[lo, hi]` (0..1) over which the stem is at full gain. Between the `hi` of one
55
+ * stem and the `lo` of the next in the same layer (sorted by `lo`) the two crossfade linearly,
56
+ * so their gains sum to 1 at every level. Below the first stem's `lo` the first stem plays,
57
+ * above the last stem's `hi` the last one does.
58
+ */
59
+ intensity?: readonly [number, number];
60
+ }
61
+ export type MusicStart = 'now' | 'beat' | 'bar';
62
+ export interface MusicPlayOptions {
63
+ /** Overrides the stem's layer. */
64
+ layer?: string;
65
+ /** Fade-in length in beats (and fade-out of a stem this one replaces). Default 0 for a stem joining a layer, 1 for a replacement. */
66
+ fadeBeats?: number;
67
+ /** When to start: immediately, on the next beat, or on the next bar. Default `bar`. */
68
+ at?: MusicStart;
69
+ /** Linear gain on top of the intensity gain. Default 1. */
70
+ gain?: number;
71
+ /**
72
+ * Stream the stem from its `url` through a media element instead of a decoded buffer
73
+ * (ws/audio-4). A streamed stem starts now, in phase with the grid (`at` is ignored); if the
74
+ * grid has not started it starts from the element's clock. Falls back to the buffer path
75
+ * where the context has no `createMediaElementSource` or no element can be made.
76
+ */
77
+ stream?: boolean;
78
+ }
79
+ export interface Music {
80
+ /** Register a stem so `play(name)` finds it. Replaces a stem of the same name. */
81
+ add(stem: Stem): void;
82
+ /** Start a stem (by name, or a stem registered on the fly) aligned to the grid. */
83
+ play(stem: string | Stem, opts?: MusicPlayOptions): void;
84
+ /** Stop one layer (or every stem) with a fade in beats (default 1). */
85
+ stop(layer?: string, fadeBeats?: number): void;
86
+ /** Crossfade stems that declare an `intensity` range. 0..1. */
87
+ setIntensity(level: number): void;
88
+ readonly intensity: number;
89
+ /** Beat within the current bar, 0-based. */
90
+ readonly beat: number;
91
+ /** Bars since the grid started, 0-based. */
92
+ readonly bar: number;
93
+ /** Beats since the grid started. */
94
+ readonly totalBeats: number;
95
+ /** Seconds per beat. */
96
+ readonly beatDuration: number;
97
+ /** Context time of the next beat / bar boundary (0 before the context exists). */
98
+ nextBeatTime(): number;
99
+ nextBarTime(): number;
100
+ /** Called once per beat crossed by `update`, with the beat in the bar and the bar. Returns unsubscribe. */
101
+ onBeat(cb: (beat: number, bar: number) => void): () => void;
102
+ /** Current target gain of a stem (intensity × play gain; 0 when not playing). */
103
+ gain(name: string): number;
104
+ /** Names of the stems playing now. */
105
+ readonly playing: readonly string[];
106
+ /** Advance `beat`/`bar` from the context clock and fire `onBeat`. Call once per frame. */
107
+ update(dt: number): void;
108
+ /** Stop everything at once and drop the grid. */
109
+ dispose(): void;
110
+ }
111
+ /** Gains of a sorted run of stems (by `lo`) at `level`, linear crossfades, summing to 1. */
112
+ export declare function intensityGains(ranges: readonly (readonly [number, number])[], level: number, out: number[]): number[];
113
+ export declare function createMusic(mixer: AudioEngine, opts: MusicOptions): Music;