@opsydyn/effect-three 0.0.0-stage → 0.2.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 (166) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/LICENSE +21 -0
  3. package/README.md +189 -2
  4. package/dist/index-01kwfekm.js +5118 -0
  5. package/dist/index-01kwfekm.js.map +40 -0
  6. package/dist/index-4045wme0.js +129 -0
  7. package/dist/index-4045wme0.js.map +10 -0
  8. package/dist/index.d.ts +12 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +1361 -0
  11. package/dist/index.js.map +14 -0
  12. package/dist/kernel/animation.d.ts +14 -0
  13. package/dist/kernel/animation.d.ts.map +1 -0
  14. package/dist/kernel/behaviours.d.ts +16 -0
  15. package/dist/kernel/behaviours.d.ts.map +1 -0
  16. package/dist/kernel/bodies.d.ts +60 -0
  17. package/dist/kernel/bodies.d.ts.map +1 -0
  18. package/dist/kernel/bodySources.d.ts +47 -0
  19. package/dist/kernel/bodySources.d.ts.map +1 -0
  20. package/dist/kernel/build.d.ts +564 -0
  21. package/dist/kernel/build.d.ts.map +1 -0
  22. package/dist/kernel/cameraMotion.d.ts +39 -0
  23. package/dist/kernel/cameraMotion.d.ts.map +1 -0
  24. package/dist/kernel/clock.d.ts +17 -0
  25. package/dist/kernel/clock.d.ts.map +1 -0
  26. package/dist/kernel/contactShadowPasses.d.ts +47 -0
  27. package/dist/kernel/contactShadowPasses.d.ts.map +1 -0
  28. package/dist/kernel/description.d.ts +1120 -0
  29. package/dist/kernel/description.d.ts.map +1 -0
  30. package/dist/kernel/diff.d.ts +90 -0
  31. package/dist/kernel/diff.d.ts.map +1 -0
  32. package/dist/kernel/environmentPresets.d.ts +30 -0
  33. package/dist/kernel/environmentPresets.d.ts.map +1 -0
  34. package/dist/kernel/equality.d.ts +64 -0
  35. package/dist/kernel/equality.d.ts.map +1 -0
  36. package/dist/kernel/frames.d.ts +55 -0
  37. package/dist/kernel/frames.d.ts.map +1 -0
  38. package/dist/kernel/index.d.ts +37 -0
  39. package/dist/kernel/index.d.ts.map +1 -0
  40. package/dist/kernel/index.js +335 -0
  41. package/dist/kernel/index.js.map +9 -0
  42. package/dist/kernel/instances.d.ts +26 -0
  43. package/dist/kernel/instances.d.ts.map +1 -0
  44. package/dist/kernel/labels.d.ts +51 -0
  45. package/dist/kernel/labels.d.ts.map +1 -0
  46. package/dist/kernel/loop.d.ts +49 -0
  47. package/dist/kernel/loop.d.ts.map +1 -0
  48. package/dist/kernel/materials.d.ts +24 -0
  49. package/dist/kernel/materials.d.ts.map +1 -0
  50. package/dist/kernel/motion.d.ts +36 -0
  51. package/dist/kernel/motion.d.ts.map +1 -0
  52. package/dist/kernel/orbit.d.ts +127 -0
  53. package/dist/kernel/orbit.d.ts.map +1 -0
  54. package/dist/kernel/overlays.d.ts +16 -0
  55. package/dist/kernel/overlays.d.ts.map +1 -0
  56. package/dist/kernel/performance.d.ts +41 -0
  57. package/dist/kernel/performance.d.ts.map +1 -0
  58. package/dist/kernel/physics.d.ts +166 -0
  59. package/dist/kernel/physics.d.ts.map +1 -0
  60. package/dist/kernel/physicsDebug.d.ts +18 -0
  61. package/dist/kernel/physicsDebug.d.ts.map +1 -0
  62. package/dist/kernel/pointer.d.ts +357 -0
  63. package/dist/kernel/pointer.d.ts.map +1 -0
  64. package/dist/kernel/renderKit.d.ts +49 -0
  65. package/dist/kernel/renderKit.d.ts.map +1 -0
  66. package/dist/kernel/renderer.d.ts +83 -0
  67. package/dist/kernel/renderer.d.ts.map +1 -0
  68. package/dist/kernel/resources.d.ts +112 -0
  69. package/dist/kernel/resources.d.ts.map +1 -0
  70. package/dist/kernel/sceneGraph.d.ts +155 -0
  71. package/dist/kernel/sceneGraph.d.ts.map +1 -0
  72. package/dist/kernel/sceneIndex.d.ts +90 -0
  73. package/dist/kernel/sceneIndex.d.ts.map +1 -0
  74. package/dist/kernel/stage.d.ts +87 -0
  75. package/dist/kernel/stage.d.ts.map +1 -0
  76. package/dist/kernel/textLayout.d.ts +29 -0
  77. package/dist/kernel/textLayout.d.ts.map +1 -0
  78. package/dist/kernel/tween.d.ts +35 -0
  79. package/dist/kernel/tween.d.ts.map +1 -0
  80. package/dist/kernel/xrInput.d.ts +107 -0
  81. package/dist/kernel/xrInput.d.ts.map +1 -0
  82. package/dist/kernel/xrLocomotion.d.ts +38 -0
  83. package/dist/kernel/xrLocomotion.d.ts.map +1 -0
  84. package/dist/runtime/FrameSource.d.ts +29 -0
  85. package/dist/runtime/FrameSource.d.ts.map +1 -0
  86. package/dist/runtime/ResourceRegistry.d.ts +18 -0
  87. package/dist/runtime/ResourceRegistry.d.ts.map +1 -0
  88. package/dist/runtime/SceneRuntime.d.ts +68 -0
  89. package/dist/runtime/SceneRuntime.d.ts.map +1 -0
  90. package/dist/runtime/SceneSchema.d.ts +2536 -0
  91. package/dist/runtime/SceneSchema.d.ts.map +1 -0
  92. package/dist/runtime/errors.d.ts +19 -0
  93. package/dist/runtime/errors.d.ts.map +1 -0
  94. package/dist/runtime/index.d.ts +11 -0
  95. package/dist/runtime/index.d.ts.map +1 -0
  96. package/dist/webgl/contactShadows.d.ts +5 -0
  97. package/dist/webgl/contactShadows.d.ts.map +1 -0
  98. package/dist/webgl/index.d.ts +11 -0
  99. package/dist/webgl/index.d.ts.map +1 -0
  100. package/dist/webgl/index.js +239 -0
  101. package/dist/webgl/index.js.map +13 -0
  102. package/dist/webgl/shaderGrid.d.ts +27 -0
  103. package/dist/webgl/shaderGrid.d.ts.map +1 -0
  104. package/dist/webgl/sky.d.ts +4 -0
  105. package/dist/webgl/sky.d.ts.map +1 -0
  106. package/dist/webgpu/contactShadows.d.ts +5 -0
  107. package/dist/webgpu/contactShadows.d.ts.map +1 -0
  108. package/dist/webgpu/index.d.ts +10 -0
  109. package/dist/webgpu/index.d.ts.map +1 -0
  110. package/dist/webgpu/index.js +216 -0
  111. package/dist/webgpu/index.js.map +13 -0
  112. package/dist/webgpu/shaderGrid.d.ts +5 -0
  113. package/dist/webgpu/shaderGrid.d.ts.map +1 -0
  114. package/dist/webgpu/sky.d.ts +4 -0
  115. package/dist/webgpu/sky.d.ts.map +1 -0
  116. package/package.json +82 -4
  117. package/src/index.ts +11 -0
  118. package/src/kernel/animation.ts +24 -0
  119. package/src/kernel/behaviours.ts +36 -0
  120. package/src/kernel/bodies.ts +338 -0
  121. package/src/kernel/bodySources.ts +203 -0
  122. package/src/kernel/build.ts +1325 -0
  123. package/src/kernel/cameraMotion.ts +202 -0
  124. package/src/kernel/clock.ts +35 -0
  125. package/src/kernel/contactShadowPasses.ts +207 -0
  126. package/src/kernel/description.ts +1197 -0
  127. package/src/kernel/diff.ts +383 -0
  128. package/src/kernel/environmentPresets.ts +38 -0
  129. package/src/kernel/equality.ts +604 -0
  130. package/src/kernel/frames.ts +124 -0
  131. package/src/kernel/index.ts +274 -0
  132. package/src/kernel/instances.ts +97 -0
  133. package/src/kernel/labels.ts +54 -0
  134. package/src/kernel/loop.ts +248 -0
  135. package/src/kernel/materials.ts +258 -0
  136. package/src/kernel/motion.ts +75 -0
  137. package/src/kernel/orbit.ts +554 -0
  138. package/src/kernel/overlays.ts +130 -0
  139. package/src/kernel/performance.ts +101 -0
  140. package/src/kernel/physics.ts +361 -0
  141. package/src/kernel/physicsDebug.ts +68 -0
  142. package/src/kernel/pointer.ts +785 -0
  143. package/src/kernel/renderKit.ts +54 -0
  144. package/src/kernel/renderer.ts +136 -0
  145. package/src/kernel/resources.ts +504 -0
  146. package/src/kernel/sceneGraph.ts +1442 -0
  147. package/src/kernel/sceneIndex.ts +219 -0
  148. package/src/kernel/stage.ts +157 -0
  149. package/src/kernel/textLayout.ts +102 -0
  150. package/src/kernel/tween.ts +80 -0
  151. package/src/kernel/xrInput.ts +230 -0
  152. package/src/kernel/xrLocomotion.ts +72 -0
  153. package/src/runtime/FrameSource.ts +41 -0
  154. package/src/runtime/ResourceRegistry.ts +24 -0
  155. package/src/runtime/SceneRuntime.ts +229 -0
  156. package/src/runtime/SceneSchema.ts +983 -0
  157. package/src/runtime/errors.ts +18 -0
  158. package/src/runtime/index.ts +10 -0
  159. package/src/webgl/contactShadows.ts +67 -0
  160. package/src/webgl/index.ts +19 -0
  161. package/src/webgl/shaderGrid.ts +196 -0
  162. package/src/webgl/sky.ts +42 -0
  163. package/src/webgpu/contactShadows.ts +83 -0
  164. package/src/webgpu/index.ts +19 -0
  165. package/src/webgpu/shaderGrid.ts +158 -0
  166. package/src/webgpu/sky.ts +35 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ ## [0.2.0](https://github.com/opsydyn/fold-kit-experiments/compare/effect-three-v0.1.0...effect-three-v0.2.0) (2026-10-19)
4
+
5
+ ### ⚠ BREAKING CHANGES
6
+
7
+ - **effect-three:** `RenderKit.drawsLabels` is gone; a render kit names its `shading` instead. Code that spreads a renderer factory to instrument it is unaffected.
8
+
9
+ ### Features
10
+
11
+ - **effect-three:** render kits name their kind of material; label engines may draw with TSL ([8059fde](https://github.com/opsydyn/fold-kit-experiments/commit/8059fde41349297dea0c7c3383361333dc6a25ad))
12
+ - **samples:** agent-driven XR room, WebGPU label fallback, Drei audit, WebGPU labels spike ([6f3c1a8](https://github.com/opsydyn/fold-kit-experiments/commit/6f3c1a88aee302f5a62c94a75419ded6f572f4d3))
13
+
14
+ ## 0.1.0 (2026-10-18)
15
+
16
+ ### Features
17
+
18
+ - **effect-three-foldkit:** Foldkit adapter with WebGL renderer and browser tests ([5e2816d](https://github.com/opsydyn/fold-kit-experiments/commit/5e2816d4a46138777db0cb656a9c3436328c8be4))
19
+ - **effect-three:** a rigid body per instance ([cc39435](https://github.com/opsydyn/fold-kit-experiments/commit/cc39435b878c781c7bf35e00e4b50ddc6d92b035))
20
+ - **effect-three:** a WebGPU renderer ([94a0964](https://github.com/opsydyn/fold-kit-experiments/commit/94a0964c723c9c8bea555a0054d52c2224fcc00c))
21
+ - **effect-three:** after R5 — WebGPU, render kits, glide, physics groups, XR spaces ([e5bde71](https://github.com/opsydyn/fold-kit-experiments/commit/e5bde71212353879d74aa32567c3eb9cbbeb38ac))
22
+ - **effect-three:** animation clips on borrowed objects ([719e73e](https://github.com/opsydyn/fold-kit-experiments/commit/719e73ed8053ba60ff1ecf7902d89735c1d74e31))
23
+ - **effect-three:** basic, lambert, phong, standard, physical and normal materials ([5baa690](https://github.com/opsydyn/fold-kit-experiments/commit/5baa690ef51dce7c1ae9ea53cfc0b6c70b9565c3))
24
+ - **effect-three:** benchmarks, API reports, package checks and CI browser tests ([5640b2d](https://github.com/opsydyn/fold-kit-experiments/commit/5640b2dc2a078642f15f157fa77b491e1443305d))
25
+ - **effect-three:** bodies on groups and loaded models ([b06179c](https://github.com/opsydyn/fold-kit-experiments/commit/b06179c4baa878fb65dc2f15a43a3e275b7bf682))
26
+ - **effect-three:** borrowed objects and lent textures ([6e4c010](https://github.com/opsydyn/fold-kit-experiments/commit/6e4c010bc21da24aadea61bee76b3a39963cda4a))
27
+ - **effect-three:** camera auto-rotate, and damping for view cameras ([c563b69](https://github.com/opsydyn/fold-kit-experiments/commit/c563b697753d8d2c0eed05ecfc935ad0d48f693a))
28
+ - **effect-three:** close the parity gaps and prepare the packages for npm ([994fe5d](https://github.com/opsydyn/fold-kit-experiments/commit/994fe5dd640583c787746a95e8465d8f051d78df))
29
+ - **effect-three:** colliders of a body's own, motors, contact forces and debug lines ([2a16c9e](https://github.com/opsydyn/fold-kit-experiments/commit/2a16c9e0f8432872e008bbd747eede1472c6ba44))
30
+ - **effect-three:** collision groups and colliders named in collisions ([9531a01](https://github.com/opsydyn/fold-kit-experiments/commit/9531a01f54e9e19b8ccc301152167d0ac52c3229))
31
+ - **effect-three:** declarative, diffed instancing ([26f70b3](https://github.com/opsydyn/fold-kit-experiments/commit/26f70b3ea5f20d5d4c33e36d3056bb15d2053334))
32
+ - **effect-three:** demo app, package builds and visual tests ([1649d49](https://github.com/opsydyn/fold-kit-experiments/commit/1649d496c3387eb63233665da4ccc6e1def2aa64))
33
+ - **effect-three:** Drei's shader grid, centring and environment presets ([a9467c7](https://github.com/opsydyn/fold-kit-experiments/commit/a9467c7580e24c0e93ee6742bb71ebd1da08a5e5))
34
+ - **effect-three:** eased transitions and sphere, plane and cylinder geometry ([26187f1](https://github.com/opsydyn/fold-kit-experiments/commit/26187f12e8ffed91993802c0128e2be03ea3ba41))
35
+ - **effect-three:** Effect runtime with scoped scenes, frame source and pointer events ([e7dec61](https://github.com/opsydyn/fold-kit-experiments/commit/e7dec61ce9fa57594fe606f9453a3115566a4cf8))
36
+ - **effect-three:** environment lighting and a shadow catcher ([023d2de](https://github.com/opsydyn/fold-kit-experiments/commit/023d2dedc8ab7b6a46a6ef23a1dc0f364af80816))
37
+ - **effect-three:** event bubbling paths, interactive groups and drag capture ([546a946](https://github.com/opsydyn/fold-kit-experiments/commit/546a94631923532bf5c11ff5b4ad7b19be64195e))
38
+ - **effect-three:** every hit carries the pointer's world-space ray ([a7b3a46](https://github.com/opsydyn/fold-kit-experiments/commit/a7b3a46aa09eecaa04d7c73158b5fbf388a12cb6))
39
+ - **effect-three:** frame-rate monitoring and render resolution ([64f9027](https://github.com/opsydyn/fold-kit-experiments/commit/64f902794d58bc5ef7bfbb19c0e2cbfa753e6f8b))
40
+ - **effect-three:** full pointer event set with R3F semantics ([d9c6366](https://github.com/opsydyn/fold-kit-experiments/commit/d9c6366931c5e6c7414ce3a4a04bd1ef8da6b019))
41
+ - **effect-three:** headless core kernel with property-tested keyed reconciliation ([a579695](https://github.com/opsydyn/fold-kit-experiments/commit/a5796951fc9545236a360ad62dc50c523c017b8c))
42
+ - **effect-three:** HTML overlays at points in the scene ([d0d016f](https://github.com/opsydyn/fold-kit-experiments/commit/d0d016f1896eb05927f154b0941662b7afc6112e))
43
+ - **effect-three:** immersive sessions (WebXR) ([9e003d1](https://github.com/opsydyn/fold-kit-experiments/commit/9e003d125813e2ce72477fd544242bc5b03cac73))
44
+ - **effect-three:** joints, and bodies no longer remade by rounding ([66ebf4d](https://github.com/opsydyn/fold-kit-experiments/commit/66ebf4d0a4bcd580699e9a7f371919397e95585c))
45
+ - **effect-three:** label colour ranges, curves, materials and layout ([ad9942e](https://github.com/opsydyn/fold-kit-experiments/commit/ad9942e49a540ab346a62fa00481c8c4d40ede11))
46
+ - **effect-three:** lent geometries and XR detected meshes ([832388b](https://github.com/opsydyn/fold-kit-experiments/commit/832388bbe6833d248d1d437f01c6e1a31aba08ce))
47
+ - **effect-three:** multiple views into one canvas ([b732a7c](https://github.com/opsydyn/fold-kit-experiments/commit/b732a7c4b6ddb50f846f71d4fa4401786a76d2a3))
48
+ - **effect-three:** orbit controls as data ([067af99](https://github.com/opsydyn/fold-kit-experiments/commit/067af99a443c1c3000bfb3e424ae43e5995366d0))
49
+ - **effect-three:** orthographic camera ([ac05a7e](https://github.com/opsydyn/fold-kit-experiments/commit/ac05a7e60307150e7707533b9ee14457ec5110ab))
50
+ - **effect-three:** physics as data, simulated by Rapier ([dd99e1c](https://github.com/opsydyn/fold-kit-experiments/commit/dd99e1cbde789e5b578ec6b28278893e5725b326))
51
+ - **effect-three:** post-processing effects ([1e2aa60](https://github.com/opsydyn/fold-kit-experiments/commit/1e2aa60533696b4db9158fd3de5d978bc601a063))
52
+ - **effect-three:** registered behaviours as data ([68eef53](https://github.com/opsydyn/fold-kit-experiments/commit/68eef530b7f43cb59ac45822064b0d1518887cc4))
53
+ - **effect-three:** render frames on demand ([9df4a06](https://github.com/opsydyn/fold-kit-experiments/commit/9df4a06adc9493a64fd0ef183e562c06568285fd))
54
+ - **effect-three:** render textures as data ([cbc5f3d](https://github.com/opsydyn/fold-kit-experiments/commit/cbc5f3d54a844ea623c28b9223a0561586771737))
55
+ - **effect-three:** scaffold Effect-native Three.js packages with Phase 0 ADRs ([02fa947](https://github.com/opsydyn/fold-kit-experiments/commit/02fa9476801e8609e311174023016b262e108427))
56
+ - **effect-three:** shadows and scene render settings ([4140595](https://github.com/opsydyn/fold-kit-experiments/commit/41405952fbe709ab6453f51b9cfd53b7e1adb40e))
57
+ - **effect-three:** sky and grid staging ([03e9f4f](https://github.com/opsydyn/fold-kit-experiments/commit/03e9f4f5c81a6dbe2a0e29720f768380ff86e737))
58
+ - **effect-three:** stage lighting as scene parts ([8686f13](https://github.com/opsydyn/fold-kit-experiments/commit/8686f13da31425c49e8d9f73e0baf78b0ce82eb2))
59
+ - **effect-three:** text geometry from lent typeface fonts ([c573453](https://github.com/opsydyn/fold-kit-experiments/commit/c573453ae3acef5356a184eb3030495ed08389af))
60
+ - **effect-three:** torus, cone, capsule, ring and circle; point, spot and hemisphere lights ([3d695fe](https://github.com/opsydyn/fold-kit-experiments/commit/3d695fe6f9d85ffd70e3762dcdf586ce7cbb94de))
61
+ - **effect-three:** update telemetry, shared samples package and apps/web samples ([f7cd64d](https://github.com/opsydyn/fold-kit-experiments/commit/f7cd64da19496e6137d8daa669ada99cae01d546))
62
+ - **effect-three:** XR controllers, and VR in the toybox ([861f008](https://github.com/opsydyn/fold-kit-experiments/commit/861f0082819ad3b1898be9e86dd9c537de637cee))
63
+ - **effect-three:** XR hover and drag, hands, sticks, AR surfaces ([35597b9](https://github.com/opsydyn/fold-kit-experiments/commit/35597b937b2721b823040b2c67468d6ab9224fd0))
64
+ - **effect-three:** XR spaces, anchors, planes and DOM overlay ([0041c7c](https://github.com/opsydyn/fold-kit-experiments/commit/0041c7c9ebbe066f2c2300ae23283dfa68cc7451))
65
+ - **effect-three:** zoom to cursor, arrow keys and camera fitting ([e14224b](https://github.com/opsydyn/fold-kit-experiments/commit/e14224b5dc11ac1d371ce1061ad41ca73879fc6f))
66
+
67
+ ### Performance Improvements
68
+
69
+ - **effect-three:** reconcile 1,000 unchanged meshes in about 95 µs ([1069d8a](https://github.com/opsydyn/fold-kit-experiments/commit/1069d8a1a9cbd344eb6d59fd6dc290d5c436f121))
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alan P Currie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,190 @@
1
- # Temporary Holding Version
1
+ # @opsydyn/effect-three
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Declarative Three.js scenes with Effect owning every lifetime: GPU resources, renderers, frame subscriptions and event queues are acquired in a `Scope` and released when it closes. Scenes are plain data, so they can be derived from an application model, diffed and tested without a browser.
4
+
5
+ The core is independent of any UI framework and of the DOM. For Foldkit, use [`@opsydyn/effect-three-foldkit`](../effect-three-foldkit/README.md).
6
+
7
+ > Status: `0.x` (see [Stability](#stability)). The API is documented in [`etc/effect-three.api.md`](etc/effect-three.api.md).
8
+
9
+ ```sh
10
+ npm install @opsydyn/effect-three effect three
11
+ ```
12
+
13
+ ## Describe a scene
14
+
15
+ ```ts
16
+ import {
17
+ ambientLight,
18
+ box,
19
+ directionalLight,
20
+ mesh,
21
+ scene,
22
+ sharedGeometry,
23
+ sharedMaterial,
24
+ spin,
25
+ standard,
26
+ } from '@opsydyn/effect-three';
27
+
28
+ const description = scene({
29
+ resources: {
30
+ geometries: [{ id: 'cube', geometry: box() }],
31
+ materials: [{ id: 'paint', material: standard({ color: 0x14b8a6 }) }],
32
+ },
33
+ children: [
34
+ ambientLight({ key: 'ambient', intensity: 0.4 }),
35
+ directionalLight({ key: 'sun', position: [3, 4, 5] }),
36
+ mesh({
37
+ key: 'cube',
38
+ geometry: sharedGeometry('cube'),
39
+ material: sharedMaterial('paint'),
40
+ rotation: spin({ axis: 'y', radiansPerSecond: 1 }),
41
+ interactive: true,
42
+ }),
43
+ ],
44
+ });
45
+ ```
46
+
47
+ - **Keys are unique across the whole scene.** A node keeps its three.js object while its key and kind stay the same, including when it moves to another group. A different kind under the same key replaces the object in place.
48
+ - **Descriptions are fully specified data.** The constructors fill in defaults, and nothing in a description is a closure or a three.js object.
49
+ - **Shapes:** `box`, `sphere`, `plane`, `cylinder`, `torus`, `cone`, `capsule`, `ring` and `circle`, owned by one mesh or shared through `resources`. Parameter names and defaults are three.js's own.
50
+ - **Materials:** `basic`, `lambert`, `phong`, `standard`, `physical` and `normalMaterial`, each with opacity, transparency, `side`, wireframe and (lit kinds) flat shading and emission. Defaults are three.js's own. Changing a material within its kind updates it in place; changing its kind replaces it.
51
+ - **Shadows and render settings:** `scene({ render: renderSettings({ shadows: 'soft', toneMapping: 'aces' }) })`, then `castShadow`/`receiveShadow` on meshes and `castShadow` on directional, point and spot lights. Tone mapping defaults to `none` (React Three Fiber defaults to ACES) so colours render exactly as given.
52
+ - **Instancing:** `instances({ key, geometry, material, instances: [instance({ key, position, color })] })` draws many copies in one call. Updates rewrite only the instances that changed, and hits name the instance (`instance` on the event).
53
+ - **Views:** `scene({ views: [view({ key: 'front', camera, rect: { width: 0.5 } }), ...] })` draws several cameras into parts of one canvas; picks use the view under the pointer and report it as `view`.
54
+ - **Post-processing:** `renderSettings({ effects: [bloom({ strength: 1.5 }), vignette()] })` applies three.js's composer passes in order (`bloom`, `vignette`, `film`); with no effects the scene renders straight to the screen.
55
+ - **Render textures:** `scene({ renderTextures: [renderTexture({ id: 'screen', camera, children })] })` draws a little scene of its own into a texture that any material uses with `map: 'screen'`; it redraws only when its scene changes.
56
+ - **Orbit controls:** `scene({ gestures: 'scene', camera: model.camera })` claims drags, wheel and pinches from the page and reports each as a `Gesture`; `orbit(model.camera, gesture)` returns the next camera, as three.js's `OrbitControls` would move it. A camera's `damping` eases the rendered camera there (a view's too), and `autoRotate` turns it round its target until a gesture, which an `AutoRotated` event precedes: keep it with `autoRotated(model.camera, detail)`. See [ADR-006](../../docs/effect-three/adr/006-controls-as-data.md).
57
+ - **Fitting:** `scene({ measure: ['model'] })` reports the node's world box as a `Measured` event whenever it changes; `fitCamera(camera, box, area)` frames it as Drei's `Bounds` does, and `orbitKey` moves the camera for arrow keys.
58
+ - **HTML overlays:** `overlay({ key: 'tag', node: 'cone', position: [0, 1, 0], center: true })` is a point the scene places after every drawn frame, as Drei's `Html`; the Foldkit view's `overlays: [{ overlay, content }]` moves Model-rendered HTML there.
59
+ - **Text:** `text({ text: 'Hello', font: 'inter', depth: 0.2 })` is extruded (or, at depth 0, flat) text from a typeface JSON font lent with `store.lendFont` (`assets.loadFont` in Foldkit), as Drei's `Text3D`, with `letterSpacing`, `lineHeight` and `align` applied (three ignores them).
60
+ - **Labels:** `label({ key: 'title', text: 'Hello', font: '/inter.woff', fontSize: 0.5 })` is crisp distance-field text at any size, as Drei's `Text`: wrapping (`maxWidth`), anchors, alignment, spacing, outlines, coloured runs (`colorRanges`), curving (`curveRadius`) and a scene `material` to light it; measured (`scene({ measure })`), it reports its laid-out size as `LabelMeasured`. Pass an engine (`troikaLabels()` from `@opsydyn/effect-three-troika`); fonts are TTF, OTF or WOFF (troika 0.52 cannot read WOFF2).
61
+ - **Staging:** `scene({ environment: environment(), sky: sky() })` adds image-based lighting (three's `RoomEnvironment`, or a lent panorama with `map`) and a daylight sky, as Drei's `Environment` and `Sky`; `grid({ key })` draws three's `GridHelper`, `contactShadows()` draws Drei's soft ground shadows, and `shadowMaterial()` shows only the shadows cast onto it.
62
+ - **Stage:** `const { lights, contactShadows, environment } = stage({ radius: 1.2, height: 2 })` is Drei's `Stage` lighting (presets `rembrandt`, `portrait`, `upfront`, `soft`) for content centred on the origin; spread the parts into `scene`. Measure the content (`scene({ measure })`), place it at `centerOffset(box)` (Drei's `Center`), light it with `stage(stageBounds(box))` and frame it with `fitCamera`.
63
+ - **Grids and presets:** `shaderGrid({ key, infinite: true })` is Drei's fading shader `Grid`; `environmentPresetUrl('sunset')` is one of Drei's ten environment HDRs, for `assets.loadEnvironment`.
64
+ - **Physics:** `mesh({ body: body({ collider: 'ball', restitution: 0.6 }) })` under `scene({ physics: physics() })` makes a rigid body the simulation moves; kicks are `impulse: { id, impulse }`, applied once per id, and collisions arrive as `Collision` events. Bodies sit on groups, models and instances too, take colliders of their own (`colliders: [capsuleCollider(…)]`) and report `ContactForce` over a threshold; joints (with motors) go in `physics({ joints })`, and `physics({ debug: true })` draws the colliders. Pass an engine (`loadRapier()` from `@opsydyn/effect-three-rapier`) to the scene graph or the Foldkit environment. See [ADR-007](../../docs/effect-three/adr/007-physics-as-a-pose-source.md).
65
+ - **XR:** `scene({ xr: xr({ origin: [0, 0, 4] }) })` lets a scene be presented in a WebXR session; in Foldkit, run `environment.xr.enter('immersive-vr')` from a click's Command. The session drives frames, the player stands at `origin`, and controllers and hands hover, click and drag along their rays. Thumbsticks arrive as `XrStick` events (`locomote(model.xr, stick)` snap-turns and steps), buttons as `XrButton`, and in AR with `xr({ hitTest: true })` a select reports the real surface it met (`XrSurface`). `xr({ glide })` glides with the sticks and reports `XrMoved`. `group({ follow: jointSpace('right', 'index-finger-tip') })` rides a tracked space (also `headSpace()`, `controllerSpace(hand)`, `anchorSpace(id)`, `planeSpace(id)`), hidden while it is not tracked; `xr({ anchors: [xrAnchor({ id, position })] })` anchors places in the room, `xr({ planes: true })` reports detected planes as `XrPlanes`, `xr({ meshes: true })` detected meshes as `XrMeshes` (draw one with `mesh({ geometry: xrMesh(id) })` in a group following `meshSpace(id)`), and `xr({ domOverlay: true })` overlays the scene's element. See [ADR-008](../../docs/effect-three/adr/008-immersive-sessions.md).
66
+ - **Cameras:** `perspectiveCamera({ fov })` or `orthographicCamera({ zoom })`. As in React Three Fiber, an orthographic frustum follows the viewport in CSS pixels, with `zoom` in pixels per world unit.
67
+ - **Lights:** `ambientLight`, `directionalLight`, `pointLight`, `spotLight` (aimed at a world-space `target`) and `hemisphereLight`, with three.js defaults.
68
+
69
+ ## Run it
70
+
71
+ ```ts
72
+ import { Effect, Layer } from 'effect';
73
+ import { FrameSource, ResourceRegistry, SceneRuntime } from '@opsydyn/effect-three';
74
+
75
+ const program = Effect.gen(function* () {
76
+ const handle = yield* SceneRuntime.make({ scene: description, renderer, viewport });
77
+ yield* handle.update(nextDescription); // diff and apply; equal scenes cost no host changes
78
+ handle.pointer({ _tag: 'PointerDown', x: 0, y: 0, button: 0 }); // normalised device coords
79
+ // handle.events: Stream of PointerEnter | PointerMove | PointerLeave | PointerDown | PointerUp
80
+ // | Click | DoubleClick | ContextMenu | Wheel | PointerMissed | Drag | DragEnd | Gesture
81
+ }).pipe(Effect.scoped);
82
+
83
+ const environment = Layer.mergeAll(ResourceRegistry.layer, FrameSource.layer(scheduler));
84
+ ```
85
+
86
+ - `renderer` is a `RendererFactory`: a plain object whose `create()` returns a renderer bound to one drawing surface. The WebGL2 implementation lives in the browser adapter. A WebGPU one can be added without touching the core.
87
+ - `FrameSource.layer(scheduler)` takes any `FrameScheduler`: `requestAnimationFrame`, three's `setAnimationLoop`, or `createManualFrames()` in tests.
88
+ - Pointer events follow React Three Fiber's semantics: click, double-click and context menu go only to the mesh that was pressed; a click that hits nothing and moves at most 2 CSS pixels is a `PointerMissed`. Hits carry the key, world-space point, distance, face normal and uv.
89
+ - `make` is atomic. If the description is invalid (`InvalidScene`), the renderer can't be created (`RendererUnavailable`), or startup is interrupted, everything already acquired is released before it returns.
90
+
91
+ ## Resource ownership
92
+
93
+ | Mode | Declared as | Lifetime |
94
+ | ------ | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
95
+ | Owned | `geometry: box(...)`, `material: standard(...)` | Created for one mesh, disposed with it. Owned material changes are applied in place. |
96
+ | Shared | `sharedGeometry(id)`, `sharedMaterial(id)` plus a `resources` definition | Created on first use, reference-counted across every scene using the same `ResourceRegistry`, disposed when the last user releases it. Changing a shared material updates it in place; changing a shared geometry swaps a new one onto every user and disposes the old one. |
97
+
98
+ Borrowed objects, textures and geometries are the application's own: lend them to the store and the library shows them but never disposes them.
99
+
100
+ ```ts
101
+ const withdraw = store.lend('car', gltf.scene); // then: borrowed({ key: 'car', id: 'car' })
102
+ store.lendTexture('bricks', texture); // then: standard({ map: 'bricks' })
103
+ store.lendGeometry('terrain', geometry); // then: mesh({ geometry: lentGeometry('terrain'), ... })
104
+ ```
105
+
106
+ A `borrowed` node may name an id before it is lent, so a description can refer to a model that is still loading; it appears when lent and disappears when withdrawn. Hits inside a borrowed object carry the `part` name of the mesh hit.
107
+
108
+ ## Animation
109
+
110
+ Behaviours own animated properties: a mesh's rotation is either model-owned (`fixed`) or behaviour-owned (`spin`), never both. A behaviour's output is a pure function of scene time, so frames are deterministic and frame-rate independent.
111
+
112
+ Behaviours replace React Three Fiber's `useFrame` with data: a node names one, `behaviour: behave('float', { speed: 2 })`, and the behaviour (a pure function of scene time registered with the scene) adds a position offset, a rotation and a scale factor on top of the node's transform each frame. `float` (Drei's `Float`) is built in; register your own through `SceneRuntime.make({ behaviours })` or `makeEnvironment(window, { behaviours })`:
113
+
114
+ ```ts
115
+ const orbit: Behaviour = (pose, seconds, params) =>
116
+ pose.offset.set(Math.cos(seconds) * (params['radius'] ?? 1), 0, Math.sin(seconds));
117
+ ```
118
+
119
+ Transitions decide how `position` and `scale` reach new values. The Model always owns the target; a transition only shapes the journey:
120
+
121
+ ```ts
122
+ mesh({
123
+ key: 'cube',
124
+ position: [model.column, 0, 0],
125
+ transition: ease({ seconds: 0.35, easing: 'easeOutBack' }), // default: instant()
126
+ geometry: box(),
127
+ material: standard({ color: 0x14b8a6 }),
128
+ });
129
+ ```
130
+
131
+ - A new target mid-flight restarts from the pose on screen, so nothing jumps. A node moving to another group keeps its world position and travels to its new place.
132
+ - Easings: `linear`, `easeInOutCubic` (default), `easeOutCubic`, `easeOutBack`. Tweens run in the frame loop without allocating and never reach `update`.
133
+
134
+ Frames render on demand by default: a scene draws after any change and while anything animates, then stops requesting frames until the next change, so nothing ever calls `invalidate`. `render: renderSettings({ frames: 'always' })` draws every display frame, as React Three Fiber's default does; `frames: 'never'` draws only on `handle.advance`.
135
+
136
+ When frames run persistently slow, the scene emits `PerformanceChanged { fps, factor }` (Drei's `PerformanceMonitor` algorithm). Store the factor in the Model and pass it back as `renderSettings({ resolution })` to lower the pixel ratio until frames recover.
137
+
138
+ `handle.setAnimating(false)` freezes scene time (for `prefers-reduced-motion`) while frames keep rendering; transitions then land at once, including any in flight.
139
+
140
+ ## Testing
141
+
142
+ Everything needed for deterministic headless tests ships with the package:
143
+
144
+ - `recordingRendererFactory()`: counts renders and records viewport and disposal.
145
+ - `createManualFrames()`: frames run when you call `step()`.
146
+ - Effect's `TestClock`: scene time comes from the `Clock` service's monotonic reading.
147
+ - **Renderers:** `webglRendererFactory` (the default) or `webgpuRendererFactory` (three's `WebGPURenderer`: WebGPU where available, its WebGL 2 backend otherwise), each carrying its render kit (`@opsydyn/effect-three/webgl` or `/webgpu`). Under WebGPU, labels draw with `troikaLabels` from `@opsydyn/effect-three-troika/webgpu`. See [ADR-010](../../docs/effect-three/adr/010-webgpu-renderer.md).
148
+ - `createSceneGraph()`: the synchronous kernel on its own, with `object(key)` and resource `stats()` for assertions. Pass `kit: webglKit` (from `@opsydyn/effect-three/webgl`) to draw skies, shader grids and contact shadows; `webglRendererFactory` carries it for you.
149
+
150
+ ## Stability
151
+
152
+ `0.x`. The four packages (`@opsydyn/effect-three`, `-foldkit`, `-rapier`, `-troika`) are
153
+ released together at one version, and each adapter needs the core at exactly its own
154
+ version. The public surface is what the API reports in `etc/` record, checked in CI: a
155
+ change that breaks it bumps the minor version (0.1 to 0.2); additions and fixes bump the
156
+ patch.
157
+
158
+ ## Limits
159
+
160
+ - **WebXR on `WebGPURenderer`** is wired but untested: the headset emulator the tests use
161
+ (IWER) cannot render into it. WebXR on the WebGL renderer is tested.
162
+ - **DOM overlay** (`xr({ domOverlay: true })`) is checked only in the session request; the
163
+ emulator has no overlay.
164
+
165
+ ## Architecture
166
+
167
+ - `src/kernel`: plain TypeScript (descriptions, keyed diff, applier, resource store, behaviours, pointer hit testing). It must not import `effect` ([ADR-003](../../docs/effect-three/adr/003-kernel-runtime-layering.md)).
168
+ - `src/runtime`: Effect services, scopes and typed errors.
169
+
170
+ Decisions and their evidence are in [`docs/effect-three`](../../docs/effect-three/).
171
+
172
+ ## Development
173
+
174
+ ```sh
175
+ bun run test # unit and property tests (effect/Arbitrary)
176
+ bun run check # strict lint, layering and single-effect checks
177
+ bun run typecheck # TypeScript 7
178
+ bun run bench:check # benchmarks against the plan's budgets
179
+ bun run build # dist JS (bun build) and declarations (tsc)
180
+ bun run api:check # fail if the public API changed without updating etc/
181
+ ```
182
+
183
+ Current benchmark results (Apple Silicon, 1,000 meshes):
184
+
185
+ | Case | Median | Budget |
186
+ | ---------------------------- | ---------------------- | ------ |
187
+ | No-op reconcile | ~95 µs | 200 µs |
188
+ | One changed property | ~95 µs, 1 op | 500 µs |
189
+ | Runtime mount + unmount | ~22 µs | 1 ms |
190
+ | Frame with one animated mesh | ~55 ns, no heap growth | 50 µs |