@woosh/meep-engine 3.60.4 → 3.60.6

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 (419) hide show
  1. package/CHANGELOG.md +2621 -0
  2. package/CONTRIBUTING.md +7 -0
  3. package/MIGRATION_2_TO_3.md +136 -0
  4. package/SECURITY.md +4 -0
  5. package/editor/EditorCameraNavigation.d.ts +2 -1
  6. package/editor/EditorCameraNavigation.d.ts.map +1 -1
  7. package/editor/EditorCameraNavigation.js +51 -57
  8. package/package.json +6 -2
  9. package/src/engine/EngineHarness.d.ts +3 -9
  10. package/src/engine/EngineHarness.d.ts.map +1 -1
  11. package/src/engine/EngineHarness.js +6 -8
  12. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
  13. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
  14. package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
  15. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
  16. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
  17. package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
  18. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
  19. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
  20. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
  21. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
  22. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
  23. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
  24. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
  25. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  26. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
  27. package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
  28. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  29. package/src/engine/graphics/ecs/water/Water.js +8 -0
  30. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  31. package/src/engine/graphics3/WaterSystem.js +731 -734
  32. package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
  33. package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
  34. package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
  35. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
  36. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  37. package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
  38. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
  39. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
  40. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
  41. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
  42. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
  43. package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
  44. package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
  45. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  46. package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
  47. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
  48. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
  49. package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
  50. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
  51. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
  52. package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
  53. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
  54. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
  55. package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
  56. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
  57. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
  58. package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
  59. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  60. package/src/shade/playground/water_refraction/README.md +5 -6
  61. package/src/shade/playground/water_refraction/index.html +1 -1
  62. package/src/shade/renderer/Renderer.d.ts +11 -0
  63. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  64. package/src/shade/renderer/Renderer.js +50 -6
  65. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  66. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  67. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  68. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  69. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  70. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  71. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  72. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  73. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +28 -45
  74. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  75. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +111 -119
  76. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +22 -3
  77. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  78. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +84 -3
  79. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts +19 -0
  80. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/proxy/vgeo_proxy_triangle_limit.js +25 -0
  82. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  83. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  84. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  85. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +15 -0
  86. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -1
  87. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +38 -0
  88. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  89. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  90. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  91. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  92. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  93. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts +28 -59
  94. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.d.ts.map +1 -1
  95. package/src/shade/renderer/geometry/virtual/sdf/VGeoSDFProxies.js +84 -789
  96. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
  98. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +7 -6
  99. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  100. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +12 -11
  101. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.d.ts +2 -2
  102. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_instance.js +2 -2
  103. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
  104. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  105. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
  106. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  107. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  108. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  109. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  110. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  111. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  112. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  113. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  114. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  115. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  116. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  117. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  118. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  119. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  120. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  121. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  122. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  123. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  124. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  125. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  126. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  127. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  128. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  129. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  130. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  131. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  132. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  133. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  134. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  135. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  136. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  137. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  138. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  139. package/src/shade/renderer/material/GPUMaterialManager.d.ts +11 -1
  140. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  141. package/src/shade/renderer/material/GPUMaterialManager.js +16 -8
  142. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  143. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  144. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  145. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +7 -3
  146. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  147. package/src/shade/renderer/postprocess/gtao/GTAO.js +26 -13
  148. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts +14 -0
  149. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  150. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +196 -68
  151. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  152. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +42 -28
  153. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.d.ts.map +1 -1
  154. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +429 -414
  155. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts +16 -0
  156. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.d.ts.map +1 -0
  157. package/src/shade/renderer/postprocess/gtao/shader_gtao_spatial_denoise.js +156 -0
  158. package/src/shade/renderer/scene/GPUSceneContext.d.ts +2 -2
  159. package/src/shade/renderer/scene/GPUSceneContext.js +5 -5
  160. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  161. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  162. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  163. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  164. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  165. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  166. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  167. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  168. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  169. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  170. package/src/shade/renderer/water/shader_water.js +22 -13
  171. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  172. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  173. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  174. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  175. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  176. package/editor/REWORK_PLAN_2026_08.md +0 -692
  177. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  178. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  179. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  180. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  181. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  182. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  183. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  184. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  185. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  186. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  187. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  188. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  189. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  190. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  191. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  192. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  193. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  194. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  195. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  196. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  197. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  198. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  199. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  200. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  201. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  202. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  203. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  204. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  205. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  206. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  207. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  208. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  209. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  210. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  211. package/src/REVIEW_2026_08_06/generation.md +0 -176
  212. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  213. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  214. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  215. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  216. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  217. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  218. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  219. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  220. package/src/REVIEW_2026_08_06/view.md +0 -667
  221. package/src/REVIEW_2026_08_06.md +0 -610
  222. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  223. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  224. package/src/core/bvh8/NOTES.md +0 -63
  225. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  226. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  227. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  228. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  229. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  230. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  231. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  232. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  233. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  234. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  235. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  236. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  237. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  238. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  239. package/src/engine/REVIEW_2026_07_03.md +0 -211
  240. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  241. package/src/engine/control/first-person/DESIGN.md +0 -658
  242. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  243. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  244. package/src/engine/control/first-person/TODO.md +0 -160
  245. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  246. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  247. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  248. package/src/engine/ecs/ik/PLAN.md +0 -179
  249. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  250. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  251. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  252. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  253. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  254. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  255. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  256. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  257. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  258. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  259. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  260. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  261. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  262. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  263. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  264. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  265. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  266. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  267. package/src/engine/physics/cloth/PLAN.md +0 -3056
  268. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  269. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  270. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  271. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  272. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  273. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  274. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  275. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  276. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  277. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  278. package/src/format/image/avif/DECISIONS.md +0 -545
  279. package/src/format/image/jpeg/NOTES.md +0 -3
  280. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  281. package/src/generation/COORDINATES.md +0 -54
  282. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  283. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  284. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  285. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  286. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  287. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  288. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  289. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  290. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  291. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  292. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  293. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  294. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  295. package/src/shade/RENDERER_CONTRACT.md +0 -697
  296. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  297. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  298. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  299. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  300. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  301. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  302. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  303. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  304. package/src/shade/renderer/NOTES.md +0 -78
  305. package/src/shade/renderer/PLAN.md +0 -8
  306. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  307. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  308. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  309. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  310. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  311. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  312. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  313. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  314. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  315. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  316. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  317. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  318. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  319. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  320. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  321. package/src/shade/renderer/camera/orbital/README.md +0 -81
  322. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  323. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  324. package/src/shade/renderer/camera/orbital/index.js +0 -9
  325. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  326. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  327. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  328. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  329. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  330. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  331. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  332. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  333. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  334. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
  335. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  336. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  337. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  338. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  339. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  340. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  341. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  342. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
  343. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts +0 -47
  344. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.d.ts.map +0 -1
  345. package/src/shade/renderer/geometry/virtual/sdf/VGEO_SDF.js +0 -80
  346. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts +0 -16
  347. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.d.ts.map +0 -1
  348. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_collect.js +0 -67
  349. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts +0 -17
  350. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.d.ts.map +0 -1
  351. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_decode.js +0 -145
  352. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts +0 -12
  353. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.d.ts.map +0 -1
  354. package/src/shade/renderer/geometry/virtual/sdf/shader_vgeo_sdf_traverse.js +0 -105
  355. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  356. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
  357. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  358. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  359. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  360. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  361. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  362. package/src/shade/renderer/hiz/NOTES.md +0 -3
  363. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  364. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  365. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  366. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  367. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  368. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  369. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  370. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  371. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  372. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
  373. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  374. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  375. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  376. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  377. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  378. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
  379. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  380. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  381. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  382. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  383. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  384. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  385. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  386. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  387. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  388. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  389. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  390. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  391. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
  392. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
  393. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  394. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  395. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  396. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  397. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  398. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  399. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  400. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  401. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  402. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
  403. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  404. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  405. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  406. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  407. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  408. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  409. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  410. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  411. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  412. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  413. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
  414. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  415. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  416. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  417. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  418. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  419. package/src/shade/wgsl/NOTES.md +0 -4
@@ -1,400 +0,0 @@
1
- # Perceptual smoothness — design and plan
2
-
3
- **Status:** implemented on `feature/network-smoothness` (phases 1–7 below). §6 records what the
4
- implementation found that this plan did not foresee, §7 the measured result, §8 what is left.
5
- **Goal:** a game that wires `NetworkSession` the obvious way renders smoothly out of the box — its
6
- own predicted entities, other players' entities, and the host's view of everybody — at any display
7
- rate, without the game knowing what `Interpolated`, `InterpolationLog` or `SmoothingState` are.
8
-
9
- Three projects built on this package ended up with jerky motion by default, each time with an
10
- agent that had been pointed at the interpolation package. Each piece is correct and tested on its
11
- own; nothing tests or documents what happens when physics, the network and interpolation all drive
12
- one entity. This note records what goes wrong there, the design that fixes it, and the order the
13
- work lands in.
14
-
15
- ---
16
-
17
- ## 1. What is wrong today
18
-
19
- Verified means there is a failing spec or a trace; read means it follows from the code and is
20
- covered by a spec written in the phase that fixes it.
21
-
22
- | # | Defect | Evidence |
23
- |---|---|---|
24
- | D1 | **Network writes to `Transform64` are never announced.** `ReplaceComponentAction.apply`, AUTH_STATE apply, rewind restore, the session's render blend and `normalize_if_dirty` all write through `adapter.deserialize` and nobody calls `t64_announce_change`. Mesh placement (`ShadedGeometrySystem`, `MeshSystem`) and the attachment sync only react to `TRANSFORM64_EVENT_CHANGE`, so a networked pose can change without the mesh moving. | read (Transform64 migration landed after the network code) |
25
- | D2 | **An interpolated body throws away every correction.** `PhysicsSystem.__interp_restore` opens each step by restoring interpolated bodies from the log's previous tick. The log holds the client's own prediction; the correction went to the live `Transform64` only. | **verified** — `predicted_body_correction.spec.js`: the drawn ball stays 0.5 m off through 30 ticks of corrections; the same scenario without `Interpolated` converges |
26
- | D3 | **Two clocks.** `NetworkSession.tick` runs its own accumulator; `EntityManager` runs another. Network frame N and `fixedStepTick` are unrelated counters, so the interpolation alpha of one is meaningless to the other. | read |
27
- | D4 | **Time dilation drops or doubles whole ticks.** `#local_frame_accum += 1/dilation` quantises a ≤5% rate change into a skipped or doubled tick every ~20 steps. | read |
28
- | D5 | **Predicted entities are never interpolated.** The session skips owned entities ("predicted, not interpolated"); the physics producer only covers physics bodies that the game opted in by hand, and runs on the other clock. | read |
29
- | D6 | **Remote entities are blended on a private path.** The session's own `#render_interpolated_entities` runs inside `tick()`, not per render frame; it does not announce (D1); it is not ordered before the camera. `InterpolationSystem.registerSource` exists for exactly this and is never called. | read |
30
- | D7 | **`InterpolationSystem` runs after `CameraSystem`.** It declares only `Interpolated`, so its derived order puts it after every system that reads `Transform64` — a camera that follows an interpolated entity copies last frame's pose. | read (ordering arithmetic) |
31
- | D8 | **Every AUTH_STATE rewinds, and corrections are stale and unsmoothed.** The no-op short-circuit needs `onComputeExpected`/`onMeasureCurrent`, which the session never wires. Replay re-executes input actions only, never the fixed-step systems, so a physics-driven entity is set to the server's *past* pose and left there. `SmoothingState` exists but is not wired. | verified (trace: control case lands one step behind the host, not ahead) |
32
- | D9 | **Physics-driven state reaches remote peers only if the game publishes it by hand**, every tick, from the right hook. Publishing through `net_mutate_component` also writes the quantised wire value back into the host's own live state, so the host's physics continues from a 10-bit-per-component rotation every tick. | read |
33
- | D10 | **A frame with no actions is a hole in the remote log.** The playout blends `(floor, floor+1)`; a missing endpoint snaps to the other, and a window with both missing leaves the live value — the *newest* truth — so an entity jumps ahead and then holds. | read |
34
- | D11 | `setPose`'s `snap` is cleared by the render pass before the next step reads it (known, documented at `PhysicsSystem.js` setPose). | read |
35
- | D12 | A body spawned after the physics step has no record for its birth tick (GAP-036). | documented |
36
-
37
- Adjacent, **not** in scope here (affects every game, networked or not, and belongs to the engine
38
- loop): `Ticker` samples the simulation dt from `setTimeout(0)` after `requestAnimationFrame`, and
39
- render runs before simulate, so the interpolation alpha carries scheduling jitter. Tracked as a
40
- follow-up.
41
-
42
- ---
43
-
44
- ## 2. Target model
45
-
46
- **One clock.** The `EntityManager` fixed step is the network tick. Network frame N is simulated by
47
- exactly one EM fixed step; nothing else advances simulation time.
48
-
49
- **Every entity is in exactly one of three roles on each peer**, and each role has one smoothing path:
50
-
51
- | Role | Where | Simulated by | Rendered by |
52
- |---|---|---|---|
53
- | **Authoritative** | everything on the host | host fixed step (inputs + systems) | local fixed-step interpolation |
54
- | **Predicted** | entities the client owns | client fixed step (sampled input + systems) | local fixed-step interpolation, plus a decaying visual offset after a correction |
55
- | **Interpolated** | everything else on a client | nobody locally — the network writes it | the network playout (render-delayed), via the same `InterpolationSystem` |
56
-
57
- **One interpolation pipeline.** `InterpolationSystem` owns render-time interpolation end to end:
58
-
59
- - **Record** (end of every fixed step): every `Interpolated` entity on the local timeline is
60
- recorded into the local log. Physics no longer records; neither does anything else. This also
61
- closes D12 — the record runs after every system, so a body spawned during the step is recorded
62
- on its birth tick.
63
- - **Blend** (every render frame, before any reader of `Transform64`): local entities between the
64
- last two ticks at the fixed-step alpha; network entities on the registered playout; the write is
65
- announced.
66
- - **Restore — guarded** (start of every fixed step, and on demand before network applies): the
67
- system remembers, per entity it wrote, the value it displaced and the value it wrote. It puts the
68
- displaced value back **only if the live value is still the one it wrote**. Anything else that
69
- wrote in between — a network correction, `setPose`, game code — is authoritative and stays. This
70
- is what fixes D2 and D11 without either side knowing about the other.
71
-
72
- **Corrections are applied to history, not just to the present.** When a predicted entity is
73
- corrected by Δ, the local log's recent records for it are shifted by Δ too (so the blend has no
74
- discontinuity) and the rendered pose gets a visual offset of −Δ that decays over ~0.1–0.3 s.
75
-
76
- **Reconciliation compares before it corrects.** The client records its own predicted replicated
77
- state per frame. AUTH_STATE for frame S is compared with the client's prediction *for S*:
78
-
79
- - equal within tolerance → nothing happens (the common case; today it is a full rewind);
80
- - different → the present is corrected by the error observed at S (`present += server(S) −
81
- predicted(S)` — position additive, rotation as a relative quaternion), the prediction history
82
- after S is shifted by the same error, and the visual offset hides the step.
83
-
84
- This is the correction used for physics-predicted objects in shipped engines (Mirror's
85
- `PredictedRigidbody`, Fiedler's state-sync smoothing). It needs no re-simulation and no physics
86
- rollback — which does not exist in the engine (restoring physics in place would need manifold,
87
- sleep-group and broadphase restore; see the inventory in the review notes) and would cost
88
- `RTT/step` physics steps per correction. It is exact for motion that does not depend on position,
89
- and converges within a round trip for motion that does (a collision the correction would have
90
- avoided is corrected again by the next AUTH_STATE).
91
-
92
- The action-log rewind + replay path stays, as an opt-in (`reconcile: 'replay'`), for games whose
93
- predicted state is entirely produced by deterministic `SimAction`s and that want exactness.
94
-
95
- **Superseded 2026-10-15: the host no longer looks for changes.** What follows describes the
96
- end-of-step comparison as built. It cost every host tick a pass over every networked entity — 7–8 ms
97
- for 20,000 entities with nothing changing — and the stack is for worlds of millions. It is gone: what
98
- travels is what goes through the executor, actions and direct writes reported with
99
- `net_mutate_component`, which the host records (`executor.record`, no write-back, D9's second half
100
- kept fixed). A direct write nobody reports stays on the host — D9's first half is the contract now,
101
- stated in the README's "What the session assumes".
102
-
103
- **The host publishes what changed** (as built, superseded above). At the end of every host step, each replicated component of
104
- each networked entity is encoded with its replication adapter and compared with what was last
105
- published; changed ones go out as `ReplaceComponentAction` records. They are recorded without
106
- applying (the host's live state is not overwritten with its quantised wire image — D9) and
107
- without prior state (they are not rolled back — the physics that produced them is not rolled back
108
- either).
109
-
110
- **Late input is applied when it arrives, on the host, in turnkey mode.** The input buffer
111
- (`simulation_delay_ticks`) and time dilation keep inputs on time; an input that is late anyway is
112
- applied at the current simulated frame rather than by rewinding. Rewinding the host replays
113
- actions but not physics, so for a physics game it bought nothing and cost a replay. The rollback
114
- path stays for `ServerAuthoritativeServer` users and for `reconcile: 'replay'` sessions.
115
-
116
- ---
117
-
118
- ## 3. Design changes by module
119
-
120
- ### `EntityManager` and `System`
121
- - Two system phases, `openFixedStep()` and `closeFixedStep()`, bracketing each fixed step: every
122
- system that has them is opened, in execution order, before any `fixedUpdate`, and closed in
123
- reverse after the last; a system whose open throws is not closed. They are what a step's framing
124
- needs and no more — the interpolation system settles in one, the network opens and closes its
125
- frame in the pair — and they are part of the system contract, ordered like everything else.
126
- (The first cut added `onFixedStepBegin` / `onFixedStepEnd` signals on the `EntityManager`: an
127
- open subscription point on the core loop that anything could attach to, in no defined order.
128
- Replaced; see §9.)
129
- - `fixedStepTimeScale` (default 1): wall time is scaled by it on its way into the fixed-step
130
- accumulator only. Systems still receive `fixedUpdateStepSize`; the `update` pass still receives
131
- the raw dt. Time dilation sets it to `1 / dilation_factor`.
132
-
133
- ### `InterpolationSystem`
134
- - Restores when a step opens (`settle()`, also callable by a system that applies state as the step
135
- opens) and on `restore()`: every value a render blend displaced is put back, and so is anything
136
- written over the blend since — unless the entity declared its live value with `snap`. The truth
137
- is held losslessly, through the component's `copy` where it has one.
138
- - Local recording of every completed step, for every `Interpolated` entity on the local timeline,
139
- at the next step's open or the next render update, whichever comes first. A record made before an
140
- entity was linked is not blended from (entity ids are reused).
141
- - Declares `Write` on `Transform64`, so derived order puts the blend before readers (D7).
142
- - `snap` rewrites the entity's latest records with its live pose instead of skipping one frame, so
143
- the blend continues from the teleported pose (D11).
144
- - Visual offset per entity (position + rotation in the body's frame), decayed by frame time, applied
145
- after the blend of a `Transform64` pose; `correct(entity, Δp, e)` shifts recent history and adds
146
- the inverse to the offset.
147
- - `allocateSourceId()`; `registerSource` refuses a second registration of a live id.
148
- - `InterpolationLog.rewrite(tick, key, type_id, fn)` — in-place, same-length record rewrite —
149
- `latest_tick_at_or_before(tick)` and `ensure_ticks(n)`.
150
-
151
- ### `PhysicsSystem`
152
- - No longer restores or records. `interpolationLog` is kept as a deprecated field that warns once;
153
- the `InterpolationSystem` produces the same (better) result with no wiring.
154
-
155
- ### `NetworkSession`
156
- - Driven by the EM through its `NetworkSystem`, given a driver: the network frame opens in
157
- `openFixedStep` and closes in `closeFixedStep`; frame-paced duties (reconnect ladder, liveness,
158
- grace, the playout's clock) run in `update`, on the engine's frame time. `tick(dt)` is deprecated:
159
- it warns once and does nothing. `tick_rate_hz`, when given, sets the EM step.
160
- - Inbound traffic is held (`DeferredTransport`) and applied as the step opens, after
161
- `interpolation.settle()` — never between a frame's blend and its draw (§9, finding 1).
162
- - Requires an `InterpolationSystem` and adds one if the EM has none. Attaches (or adapts) an
163
- `Interpolated` on every networked entity, by role; re-evaluates on ownership change.
164
- - Replication says how each component is drawn, with no optional parameters:
165
- `replicate_interpolated(C, interpoland)` blends it, `replicate_discrete(C)` shows each state as it
166
- lands. The session names no component type in its API; `replicate_transform64(session)`, in
167
- `network/adapters` beside the Transform64 wire adapter, is `replicate_interpolated(Transform64,
168
- POSE_INTERPOLAND)` — the pose blended at full precision (7 doubles), not re-quantised through the
169
- wire layout.
170
- - Remote entities are blended by `InterpolationSystem` on a registered network source whose window
171
- holds across frames that carried no change (D10). `normalize_if_dirty` delegates to `restore()`.
172
- - Every write it makes to a replicated component in place is announced (D1), through the
173
- component's replication adapter (`announce(dataset, entity)`); the Transform64 adapter announces
174
- a Transform64. The network itself knows no component that needs it.
175
- - Client: AUTH_STATE is queued (newest per entity, none older than the last reconciled) and
176
- reconciled as the next step opens, using the comparison + correction above. The session compares
177
- wire images and hands a component that differs to its `Correction` — `same`, `carry` (into each
178
- prediction since S), `apply` (to the live component, and through whatever owns it) — so it knows
179
- no component type, and no physics. `POSE_CORRECTION` (Transform64, through physics for a body,
180
- hidden by `InterpolationSystem.correct`), `VELOCITY_CORRECTION` and `HOST_VALUE_CORRECTION` are
181
- given. `reconcile: 'correct' | 'replay'`, default `'correct'`.
182
- - Host: ~~publishes changed replicated state at the end of each step~~ (removed 2026-10-15: what
183
- travels is what goes through the executor; a reported direct write is recorded, not executed);
184
- `late_input: 'apply' | 'rollback'`, default `'apply'`.
185
-
186
- ### Orchestrators
187
- - `ServerAuthoritativeServer`: `begin_tick(frame)` / `end_tick()` split, so the EM's systems run
188
- inside the open frame; `tick(frame)` remains as begin+end. `late_input` option.
189
- - `ServerAuthoritativeClient`: `begin_tick`/`end_tick` split; `auto_reconcile` option so the session
190
- can take AUTH_STATE over.
191
- - `SimActionExecutor.record(action)`: log a record without applying it and without prior state (for
192
- host publishing), stamped `SENDER_RECORDED`; a rollback replay carries it over unchanged.
193
- - `SimActionExecutor.execute` is transactional: an action whose `apply` throws is undone from the
194
- prior state it captured and leaves no record; announcements follow the completed record.
195
-
196
- ---
197
-
198
- ## 4. Compatibility
199
-
200
- - `NetworkSession.tick(dt)` → no-op + one-time warning. Call `entity_manager.update(dt)` (the engine
201
- loop already does). **Breaking** for code that drove the session without an EM loop.
202
- - Default reconcile changes from rewind+replay to compare+correct; `reconcile: 'replay'` restores
203
- the old behaviour. Default host late-input handling changes from rollback to apply;
204
- `late_input: 'rollback'` restores it.
205
- - `replicate()` is deprecated in favour of `replicate_interpolated(C, interpoland)` and
206
- `replicate_discrete(C)`. It keeps its meaning — `replicate(C)` is discrete, `replicate(C, adapter)`
207
- blended over the component's replication layout, `Transform64` included — and warns once. **Breaking** only where it blends a component
208
- without `copy(other)`, which is now required of any component blended between ticks.
209
- - Inbound traffic takes effect at the receiver's next fixed step; the session's timers follow the
210
- engine's frame time. Code that delivered packets by hand and read the result at once must step.
211
- - An interpolated entity written between steps without `snap` or a prior `restore()` is put back at
212
- the next step. This was the rule for interpolated physics bodies already; it now holds for every
213
- `Interpolated` entity. **Behaviour change** for code that relied on the old absence of any restore
214
- for non-physics entities.
215
- - Peer ids are `[0, 251]`; 252 is `SENDER_CORRECTED`, 253 is `SENDER_RECORDED`.
216
- - `PhysicsSystem.interpolationLog` is deprecated (ignored, warns once).
217
- - Wire format: a new `FRAME_MARKER` packet; nothing else.
218
- - `scene-editor`'s `RemotePresence` (the only in-repo `registerSource` user) needed one change: a
219
- drag's end — a committed record, the canonical bytes written back — now declares the entity's live
220
- value with `snap` before taking its `Interpolated` away, where it used to rely on nothing restoring.
221
- Its spec caught it.
222
-
223
- ---
224
-
225
- ## 5. Phases
226
-
227
- Each phase lands as its own commit(s) with its specs, and leaves the tree green.
228
-
229
- 1. **EM hooks** — signals + time scale. Specs: order of begin/fixedUpdate/end; time scale changes step
230
- cadence, not step size. (The signals became system phases after review — §9.)
231
- 2. **InterpolationSystem owns interpolation** — guarded restore (an unconditional one after
232
- review — §9), local record, write-ordering,
233
- snap-by-rewrite, visual offset + `correct()`, log rewrite; physics producer retired; physics
234
- interpolation specs ported. `predicted_body_correction.spec.js` goes green here.
235
- 3. **One clock for the session** — orchestrator begin/end split, EM-driven session, dilation through
236
- the time scale, remote entities through a registered source with hold-across-gaps, auto
237
- `Interpolated`, announcements. Existing session specs migrate from `session.tick` to
238
- `em.update`.
239
- 4. **Host publishes** — `executor.record`, change detection, no write-back; late input applied.
240
- 5. **Client reconciles by comparison** — prediction history, queued AUTH_STATE, correct + shift +
241
- visual offset; `'replay'` kept and tested.
242
- 6. **Render-cadence spec** — host + clients over a jittery simulated link, rendered at 144 Hz on a
243
- 60 Hz step: per-frame displacement of a constant-velocity entity is near-constant for the
244
- predicted, remote and host views alike. This is the "turnkey" acceptance test.
245
- 7. **Docs** — README section "Smooth by default", CHANGELOG, migration notes.
246
-
247
- Deferred (named, not started): predicted non-owned entities (a Rocket League–style shared ball
248
- predicted by every client), automatic kinematic proxies for remote physics bodies, and the engine-loop dt jitter above.
249
-
250
- ---
251
-
252
- ## 6. Found during implementation
253
-
254
- Each of these surfaced as a failing or wrongly-passing spec, and each is fixed on the branch.
255
-
256
- - **The repro first passed for the wrong reason.** Sampled right after packet delivery, it read
257
- the correction in the one window where it existed. Sampled where the renderer reads — after the
258
- update, before the next packets — it showed the ball 0.5 m off for good. Specs here measure what
259
- is drawn.
260
- - **A client echoed the host's state back to the host every tick.** Arrived records are logged, and
261
- a peer's outbound stream repacks its log for every peer. For host-owned entities ownership rejects
262
- the echo; for client-owned ones the host applied it as input and overwrote its own simulation —
263
- the host's copy of a client-owned ball dropped a step every fifth frame. The replicator now never
264
- sends a record back to its sender.
265
- - **A record with no prior state is out of scope-filter reach.** The filter reads a record's
266
- prior-state entries to find its entities; the host's published records had none and went to
267
- everyone, owner included. They now carry empty entries that name the entity; rewind skips them.
268
- - **Time dilation ran away for clients without input.** Buffer depth was measured from received
269
- input only, so a client sending none read as depth 0 and was told to run 5% fast forever. The
270
- per-tick keep-alive is now a `FRAME_MARKER` carrying the client's frame.
271
- - **The playout started one frame behind the stream.** With less than a delay's worth of frames at
272
- the first sample the playhead was clamped to 0, and the deliberately slow pull took seconds to
273
- open the gap. It now waits for a whole delay before it starts.
274
- - **Publishing had to follow the local sim.** Recording at step end, before `onLocalSim`, published
275
- what an `onLocalSim` action was about to overwrite. The server gained `onBeforeTickClose`.
276
- - **The local log could not hold two ticks of a large world.** Recording every interpolated entity
277
- every tick outgrew 64 KB at ~1000 entities, and a blend missing its older tick snaps.
278
- `InterpolationLog.ensure_ticks` grows it in place.
279
-
280
- ## 7. Result
281
-
282
- `NetworkSession.smoothness.spec.js`: a host and a client, 144 Hz render, 60 Hz step, each frame run
283
- in a browser's order — the engine updates, then the network delivers, then the frame is drawn. A
284
- *hitch* is a drawn frame that moves less than half or more than one and a half of the typical
285
- per-frame travel. Measured on this branch and, with the same scenario wired as the old docs led
286
- (session `tick` next to `em.update`, the Transform interpolator, the NPC published from
287
- `onLocalSim`, `Interpolated` on the entities), on the tree before it:
288
-
289
- | view (719 drawn frames) | before, clean link | after, clean link | after, 45±20 ms, 5% loss |
290
- |---|---|---|---|
291
- | client's predicted player | 719 hitches, 420 frames frozen | 0 (worst 1.2%) | 0 (worst 6%) |
292
- | host NPC, drawn on the client | 240 hitches | 0 (worst 0.9%) | 0 (worst 2.5%) |
293
- | host's view of the client's player | 719 hitches | 0 | 0 on this link's seed; input later than the buffer shows as a hitch (§8) |
294
- | host's own NPC | 719 hitches | 0 | 0 |
295
-
296
- With inbound traffic applied on arrival instead of at the step (the order a browser delivers it),
297
- the same spec fails on the clean link — its worst frame moves 83% off the typical one — and shows
298
- 319 hitches on the lossy one.
299
-
300
- ## 8. What is left
301
-
302
- - **The host's view of late input.** The host draws its authoritative simulation; input later than
303
- the buffer shows as a stall then a double step. Remedies, in rising cost: a larger
304
- `simulation_delay_ticks`; an input buffer that adapts to observed lateness; repeating a client's
305
- last input when one is missing and reconciling when it lands.
306
- - **Predicted non-owned entities** — a shared ball every client predicts — need AUTH_STATE for
307
- entities a peer does not own, and a per-entity choice between predicted and played out.
308
- - **Ownership handoff.** An entity that changes hands moves between the local timeline and the
309
- playout, a render delay apart, and jumps when it does.
310
- - **Automatic kinematic proxies** for remote physics bodies. A client that gives a remote entity a
311
- dynamic body has two writers fighting over its pose, and nothing says so: the session knows no
312
- physics to check with.
313
- - **Engine-loop timing.** `Ticker` samples the frame delta in a `setTimeout(0)` after the animation
314
- frame, and draws before it simulates; the interpolation alpha carries that scheduling jitter. It
315
- affects every game, networked or not, and belongs to its own change.
316
- - **Correction across components.** A strategy corrects its own component. A velocity corrected at
317
- S is not carried into the positions predicted since S; the pose's own correction takes that up, a
318
- frame at a time, as the host's states arrive. And `HOST_VALUE_CORRECTION` drops what a client
319
- predicted since S, so it never settles for a component the client predicts from its input — such
320
- a component needs a strategy that carries its difference.
321
- - **A write between steps that forgets to say so** is put back without a word. The engine cannot
322
- tell it from a render-phase writer reacting to the blend, which must be put back; a debug-build
323
- warning, or a small `teleport()` helper, would make the rule harder to miss.
324
- - **A paused engine holds its session's traffic.** Packets wait for the next step; an engine
325
- stopped for long with its session still connected holds them all, and the peer reaps it anyway.
326
-
327
- ## 9. Independent review, and three corrections
328
-
329
- The branch was reviewed independently once the first cut was green. Twelve findings, all real:
330
-
331
- | # | Finding | Fix |
332
- |---|---|---|
333
- | 1 | Packets are handled between a frame's blend and its draw; applying them restores the truth for everything blended, so most client frames drew unblended | Inbound traffic is held (`DeferredTransport`) and applied as the next step opens, after the interpolation system settles. The acceptance spec now runs frames in browser order |
334
- | 2 | An AUTH_STATE arriving after a newer one corrected the present a second time | The newest reconciled frame per network id; older states are dropped |
335
- | 3 | Skipping components an action wrote lost a change the simulation made to them later in the step | The publish baseline is "what the peers hold", updated by every executed action; the step's end publishes any difference |
336
- | 4 | A resync compared new states against predictions for the world it replaced | INITIAL_SYNC clears the prediction history, pending states and sampled input |
337
- | 5 | Render-phase writers (`ClingToTerrainSystem`) leaked the blend into the simulation through the guarded restore | Restore always, as physics already did; a deliberate write between steps says so with `snap` or `restore()` first |
338
- | 6 | Restoring through a lossy wire adapter rounded the truth | The truth is a copy of the component, through its `copy()`; `replicate_interpolated` refuses a component without one |
339
- | 7 | `ensure_ticks` dropped bytes past the logical capacity | It copies the whole buffer |
340
- | 8 | A reused entity id blended from the previous owner's records | Records from before an entity's link are not blended from |
341
- | 9 | A rollback replay dropped the host's published records | Records carry `SENDER_RECORDED`; a replay writes them back unchanged |
342
- | 10 | Scale was never compared or corrected | Compared, and corrected by ratio |
343
- | 11 | `rewrite` before the first step mistook −1 for the open tick | Guarded |
344
- | 12 | Exceptions left frames open or records half-written | Transactional `execute`; announcements after the record; a failing host input is reported and skipped; a `begin_tick` that fails part-way closes the frame it opened, and the session moves on to the next frame; one entity's failed correction does not stop the others |
345
-
346
- Of the lesser notes, four were right and are fixed: a turn observed at S must be carried in the
347
- body's frame (`q' = q · e`), since physics integrates rotation in world space; the position
348
- tolerance must grow with distance from the origin, as Float32's step does; `restore()` threw away
349
- its per-entity slots every step (now pooled); and `PredictionHistory.get` allocated per read. Two
350
- are left on purpose: networked entities that carry no interpolated component are looked at again
351
- each step until one arrives — correct, and a handful of lookups — and queueing an AUTH_STATE
352
- allocates a few small objects, a few hundred a second.
353
-
354
- Finding 5 changed a design decision. The guard existed because network writes landed between
355
- frames; once they land in the step (finding 1), the engine's older contract — always restore, a
356
- deliberate write declares itself — covers everything, and the guard's leak goes with it. Its cost
357
- is that an undeclared write between steps is put back: the scene editor's remote-drag presence
358
- relied on nothing restoring, and its spec caught it.
359
-
360
- Three corrections came from the author while the findings were being fixed:
361
-
362
- - **The `EntityManager` seam was too wide.** `onFixedStepBegin` / `onFixedStepEnd` were open
363
- signals on the core loop: anything could subscribe, in no defined order, and it invited bolting
364
- on work that belongs in a system. They became two system phases, `openFixedStep` /
365
- `closeFixedStep`, with the same ordering as every other phase and the smallest contract that
366
- frames a step; `NetworkSystem` takes a driver for them.
367
- - **`replicate()`'s second argument meant too many things** — omitted meant "interpolate" for one
368
- class and "snap" for the rest, `null` meant snap, an adapter meant "interpolate" but was ignored
369
- for `Transform64`. A first pass made it an options object, which still left optional fields and a
370
- default to fall into. It is now two methods with no optional parameters,
371
- `replicate_interpolated(C, interpoland)` and `replicate_discrete(C)`: every call says how its
372
- component is drawn. A `replicate_transform()` on the session came and went — a component type
373
- named in the session's own API, coupling it to one of the things it carries. The shortcut is a
374
- free function beside the Transform64 wire adapter, `replicate_transform64(session)`, and it is the
375
- general call, nothing more. The same went for reconciliation: the session special-cased
376
- `Transform64` and duck-typed physics to correct poses and velocities. Correction is now a strategy
377
- each component is registered with, as its interpolation is; the pose and velocity strategies sit
378
- beside the Transform64 wire adapter, and the physics lookup with them. Last to go: the network's
379
- announcement of in-place writes named Transform64 — it now asks the component's replication
380
- adapter, and the Transform64 adapter answers — and a warning for a remote entity with a dynamic
381
- body needed the session to know physics. The warning was dropped: a missing message is better than
382
- the session knowing what a rigid body is. The network's core — session, replication, simulation,
383
- transport — names no component type and imports nothing from physics; what is specific to a
384
- component lives in `network/adapters`.
385
- - **Specs reached behind the curtain.** Patching `performance.now` tied specs to how the engine
386
- reads time — something the engine may change — and let the session run on a clock of its own. The
387
- session now runs on the engine's frame time, so a spec drives time by driving the engine. Other
388
- mocks went too: monkeypatched time dilation became a client that falls behind and is told to
389
- catch up; spied executor calls became bytes on the wire, compared with a session that does not
390
- publish; an injected retransmission became a real one; reads of the interpolation log became the
391
- pose that is drawn. A spec that asserted a warning's text was dropped. No mock or spy remains in
392
- the specs the branch added; the log, history and executor are unit-specced through their own
393
- interfaces.
394
-
395
- Every finding above has a spec, and each spec was run against the code as it was before its fix:
396
- it fails there and passes here. Two first drafts passed on the broken code as well, and were
397
- rewritten until they did not.
398
-
399
- @author Alex Goldring
400
- @copyright Company Named Limited (c) 2026