@descix/frqtl-node 0.0.0-stage → 0.6.0-p0.2

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 (218) hide show
  1. package/CLAUDE.md +87 -0
  2. package/LICENSE.md +664 -0
  3. package/README.md +84 -2
  4. package/dist/dist-manifest.json +218 -0
  5. package/dist/eyes/index.js +109 -0
  6. package/dist/fat/pkg-node/egpt_wasm.js +1277 -0
  7. package/dist/fat/pkg-node/egpt_wasm_bg.wasm +0 -0
  8. package/dist/fat/pkg-node/package.json +12 -0
  9. package/dist/frqtl/assets/vendor/chroma.mjs +64 -0
  10. package/dist/frqtl/compat/boot-node.js +34 -0
  11. package/dist/frqtl/compat/engine.js +127 -0
  12. package/dist/frqtl/compat/package-root.js +24 -0
  13. package/dist/frqtl/compat/providers/wasm-engine.js +2499 -0
  14. package/dist/frqtl/compat/providers/wasm-load-browser.js +134 -0
  15. package/dist/frqtl/compat/providers/wasm-load-node.js +59 -0
  16. package/dist/frqtl/compat/renderer/FieldBuilder.js +145 -0
  17. package/dist/frqtl/compat/renderer/Filters.js +163 -0
  18. package/dist/frqtl/compat/renderer/RenderFamily.js +84 -0
  19. package/dist/frqtl/compat/renderer/RenderMesh.js +99 -0
  20. package/dist/frqtl/compat/renderer/RenderShape.js +154 -0
  21. package/dist/frqtl/compat/renderer/camera2d-geometry.js +183 -0
  22. package/dist/frqtl/compat/renderer/captureStill-common.js +258 -0
  23. package/dist/frqtl/compat/renderer/display-slot.js +96 -0
  24. package/dist/frqtl/compat/renderer/lo-drawn-center.js +231 -0
  25. package/dist/frqtl/compat/renderer/orbit-bag.js +216 -0
  26. package/dist/frqtl/compat/renderer/providers/godrays-layer.js +213 -0
  27. package/dist/frqtl/compat/renderer/providers/index.js +22 -0
  28. package/dist/frqtl/compat/renderer/providers/p5-provider.js +861 -0
  29. package/dist/frqtl/compat/renderer/providers/three-camera-contract.js +160 -0
  30. package/dist/frqtl/compat/renderer/providers/three-camera-geometry.js +571 -0
  31. package/dist/frqtl/compat/renderer/providers/three-provider.js +2470 -0
  32. package/dist/frqtl/compat/renderer/renderCapabilities-common.js +186 -0
  33. package/dist/frqtl/compat/renderer/renderer-provider-contract.js +238 -0
  34. package/dist/frqtl/compat/renderer/unprojectStill.js +172 -0
  35. package/dist/frqtl/compat/vendor.js +196 -0
  36. package/dist/frqtl/node-entry.js +17 -0
  37. package/dist/frqtl/objects/FrqtlApp.js +2920 -0
  38. package/dist/frqtl/objects/ObjectFactory.js +296 -0
  39. package/dist/frqtl/objects/RecipeContext.js +322 -0
  40. package/dist/frqtl/objects/collectors/DataCollector.js +116 -0
  41. package/dist/frqtl/objects/constants/WallBehaviors.js +178 -0
  42. package/dist/frqtl/objects/geometry/SlabFace.js +258 -0
  43. package/dist/frqtl/objects/instruments/capture-host.js +453 -0
  44. package/dist/frqtl/objects/instruments/capture.js +710 -0
  45. package/dist/frqtl/objects/instruments/hosted-eyes.js +984 -0
  46. package/dist/frqtl/objects/kinds-entry.js +14 -0
  47. package/dist/frqtl/objects/kinds.js +190 -0
  48. package/dist/frqtl/objects/objects/Box.js +273 -0
  49. package/dist/frqtl/objects/objects/Bullet.js +155 -0
  50. package/dist/frqtl/objects/objects/CircuitGate.js +380 -0
  51. package/dist/frqtl/objects/objects/CircuitProbe.js +214 -0
  52. package/dist/frqtl/objects/objects/DirectedTunnel.js +411 -0
  53. package/dist/frqtl/objects/objects/EgptFractal.js +164 -0
  54. package/dist/frqtl/objects/objects/LightSource.js +351 -0
  55. package/dist/frqtl/objects/objects/QuantumEmitter.js +760 -0
  56. package/dist/frqtl/objects/objects/Tube.js +471 -0
  57. package/dist/frqtl/objects/objects/absorbRadiation.js +309 -0
  58. package/dist/frqtl/objects/objects/actionCompiler.js +1747 -0
  59. package/dist/frqtl/objects/objects/controlAction.js +89 -0
  60. package/dist/frqtl/objects/objects/controlBind.js +278 -0
  61. package/dist/frqtl/objects/objects/directedRedirectProgram.js +120 -0
  62. package/dist/frqtl/objects/objects/emitterProgram.js +209 -0
  63. package/dist/frqtl/objects/objects/frqtl-control-slider.js +197 -0
  64. package/dist/frqtl/objects/objects/frqtl-control-sync.js +272 -0
  65. package/dist/frqtl/objects/objects/frqtl-control-widgets.js +219 -0
  66. package/dist/frqtl/objects/objects/frqtl-control-write-queue.js +174 -0
  67. package/dist/frqtl/objects/objects/makeControlsForObject.js +820 -0
  68. package/dist/frqtl/objects/objects/makeControlsForRenderSurface.js +161 -0
  69. package/dist/frqtl/objects/objects/slitLinerScatter.js +261 -0
  70. package/dist/frqtl/objects/objects/sustainedSource.js +298 -0
  71. package/dist/frqtl/objects/objects/tagger.js +228 -0
  72. package/dist/frqtl/objects/schema/engine-scopes.js +468 -0
  73. package/dist/frqtl/objects/schema/index.js +256 -0
  74. package/dist/frqtl/objects/schema/scopes.js +457 -0
  75. package/dist/frqtl/objects/schema/validate-descriptor.js +49 -0
  76. package/dist/frqtl/objects/schema/valueNormalizer.js +366 -0
  77. package/dist/frqtl/objects/setup/_collisionMatrixLanes.js +66 -0
  78. package/dist/frqtl/objects/setup/_setupOptions.js +118 -0
  79. package/dist/frqtl/objects/setup/_worldInit.js +162 -0
  80. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +242 -0
  81. package/dist/frqtl/objects/setup/setupCircuitBasic.js +447 -0
  82. package/dist/frqtl/objects/setup/setupCollisionMatrix.js +188 -0
  83. package/dist/frqtl/objects/setup/setupConeEmitter3D.js +250 -0
  84. package/dist/frqtl/objects/setup/setupDoubleSlit.js +326 -0
  85. package/dist/frqtl/objects/setup/setupDoubleSlit_Laser.js +229 -0
  86. package/dist/frqtl/objects/setup/setupEmpty.js +59 -0
  87. package/dist/frqtl/objects/setup/setupFxSmoke.js +209 -0
  88. package/dist/frqtl/objects/setup/setupG1Race.js +80 -0
  89. package/dist/frqtl/objects/setup/setupG2Sweep.js +89 -0
  90. package/dist/frqtl/objects/setup/setupG3Collide.js +177 -0
  91. package/dist/frqtl/objects/setup/setupLightSourceLab.js +277 -0
  92. package/dist/frqtl/objects/setup/setupOscillator.js +68 -0
  93. package/dist/frqtl/objects/setup/setupParticleWalk.js +93 -0
  94. package/dist/frqtl/objects/setup/setupParticleWalk3D.js +133 -0
  95. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +1258 -0
  96. package/dist/frqtl/objects/setup/setupTraceCircuit.js +239 -0
  97. package/dist/frqtl/objects/setup/setupTunnelLab.js +240 -0
  98. package/dist/frqtl/objects/setup/setupTunnelPointExit.js +306 -0
  99. package/dist/frqtl/objects/setup/setupWaveInterference.js +177 -0
  100. package/dist/frqtl/objects/v1-replacements.js +29 -0
  101. package/dist/frqtl/package-core.js +23 -0
  102. package/dist/frqtl/parts/CameraControls.js +643 -0
  103. package/dist/frqtl/parts/CameraPath.js +575 -0
  104. package/dist/frqtl/parts/CollisionActions.js +34 -0
  105. package/dist/frqtl/parts/CollisionTypes.js +30 -0
  106. package/dist/frqtl/parts/DetectorWall.js +496 -0
  107. package/dist/frqtl/parts/FrqtlControlOverlay.js +227 -0
  108. package/dist/frqtl/parts/FrqtlControlsPanel.js +415 -0
  109. package/dist/frqtl/parts/FrqtlVision.js +818 -0
  110. package/dist/frqtl/parts/GraphingDetectorWall.js +489 -0
  111. package/dist/frqtl/parts/LargeObject.js +405 -0
  112. package/dist/frqtl/parts/LightColor.js +255 -0
  113. package/dist/frqtl/parts/LiveChart.js +285 -0
  114. package/dist/frqtl/parts/LogicGate.js +717 -0
  115. package/dist/frqtl/parts/OvenBox.js +611 -0
  116. package/dist/frqtl/parts/PointSource.js +762 -0
  117. package/dist/frqtl/parts/Rectangle.js +200 -0
  118. package/dist/frqtl/parts/SlitLightSource.js +234 -0
  119. package/dist/frqtl/parts/SlitScreen.js +628 -0
  120. package/dist/frqtl/parts/SphereEmitter.js +946 -0
  121. package/dist/frqtl/parts/Trace.js +508 -0
  122. package/dist/frqtl/parts/Tunnel.js +1057 -0
  123. package/dist/frqtl/parts/Wall.js +1211 -0
  124. package/dist/frqtl/parts/index.js +33 -0
  125. package/dist/frqtl/scenes/absorbing-wall-3d.js +33 -0
  126. package/dist/frqtl/scenes/absorbing-wall.js +32 -0
  127. package/dist/frqtl/scenes/atomic-model.js +88 -0
  128. package/dist/frqtl/scenes/big-bang.js +46 -0
  129. package/dist/frqtl/scenes/blackbody.js +81 -0
  130. package/dist/frqtl/scenes/circuit-sat.js +127 -0
  131. package/dist/frqtl/scenes/collision-matrix-3d.js +107 -0
  132. package/dist/frqtl/scenes/counting-wall-3d.js +36 -0
  133. package/dist/frqtl/scenes/counting-wall.js +38 -0
  134. package/dist/frqtl/scenes/double-slit.js +31 -0
  135. package/dist/frqtl/scenes/dse-one-emitter.js +196 -0
  136. package/dist/frqtl/scenes/dse-one-slit.js +114 -0
  137. package/dist/frqtl/scenes/dse-photoelectric.js +70 -0
  138. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +243 -0
  139. package/dist/frqtl/scenes/dse-two-emitters.js +196 -0
  140. package/dist/frqtl/scenes/dse-two-slits.js +132 -0
  141. package/dist/frqtl/scenes/empty.js +14 -0
  142. package/dist/frqtl/scenes/laser-buildup.js +49 -0
  143. package/dist/frqtl/scenes/mirror-3d.js +56 -0
  144. package/dist/frqtl/scenes/mirror-route.js +140 -0
  145. package/dist/frqtl/scenes/mirror.js +38 -0
  146. package/dist/frqtl/scenes/omni-3d.js +27 -0
  147. package/dist/frqtl/scenes/omni-source.js +22 -0
  148. package/dist/frqtl/scenes/self-interference.js +39 -0
  149. package/dist/frqtl/scenes/smoking-gun.js +122 -0
  150. package/dist/frqtl/scenes/straight-walk.js +22 -0
  151. package/dist/frqtl/scenes/sun.js +39 -0
  152. package/dist/frqtl/scenes/two-source.js +37 -0
  153. package/dist/frqtl/scenes/wave.js +26 -0
  154. package/dist/shim/bundle/frqnb.js +112 -0
  155. package/dist/shim/bundle/index.js +121 -0
  156. package/dist/shim/bundle/indexHtml.js +107 -0
  157. package/dist/shim/bundle/manifest.js +95 -0
  158. package/dist/shim/bundle/zip.js +243 -0
  159. package/dist/shim/bus.js +67 -0
  160. package/dist/shim/controls/index.js +627 -0
  161. package/dist/shim/controls/registry.js +274 -0
  162. package/dist/shim/engine/artifact-shell.js +211 -0
  163. package/dist/shim/engine/boot-phases.js +44 -0
  164. package/dist/shim/engine/engine-stack-boot.js +59 -0
  165. package/dist/shim/engine/router.js +690 -0
  166. package/dist/shim/experiment-frame/index.js +296 -0
  167. package/dist/shim/expr/ast-schema.js +369 -0
  168. package/dist/shim/field-descriptor/index.js +325 -0
  169. package/dist/shim/frame-data/index.js +224 -0
  170. package/dist/shim/index.js +67 -0
  171. package/dist/shim/notebook/canvas-guard.js +82 -0
  172. package/dist/shim/notebook/catalog.js +35 -0
  173. package/dist/shim/notebook/format.js +736 -0
  174. package/dist/shim/notebook/frqtl-setups-importmap.js +81 -0
  175. package/dist/shim/notebook/kernel.js +671 -0
  176. package/dist/shim/notebook/knob.js +150 -0
  177. package/dist/shim/notebook/module-resolver.js +260 -0
  178. package/dist/shim/notebook/notebook-knobs.js +65 -0
  179. package/dist/shim/notebook/produced-bindings.js +67 -0
  180. package/dist/shim/notebook/realm-registry.js +121 -0
  181. package/dist/shim/notebook/realm-transport.js +73 -0
  182. package/dist/shim/notebook/setup-builder.js +507 -0
  183. package/dist/shim/notebook/store.js +159 -0
  184. package/dist/shim/notebook/vendor-importmap.js +134 -0
  185. package/dist/shim/notebook/view-builder.js +189 -0
  186. package/dist/shim/notebook/view-ctx.js +100 -0
  187. package/dist/shim/notebook/view-handle.js +202 -0
  188. package/dist/shim/notebook/warmup.js +102 -0
  189. package/dist/shim/package.json +4 -0
  190. package/dist/shim/page-params/index.js +213 -0
  191. package/dist/shim/project/identity.js +112 -0
  192. package/dist/shim/project/index.js +19 -0
  193. package/dist/shim/project/link.js +402 -0
  194. package/dist/shim/project/node-specifier-hook.mjs +35 -0
  195. package/dist/shim/project/register-node.mjs +17 -0
  196. package/dist/shim/project/specifiers.js +106 -0
  197. package/dist/shim/registry/copy-gate.js +110 -0
  198. package/dist/shim/registry/exhibits.js +455 -0
  199. package/dist/shim/registry/experiments.js +215 -0
  200. package/dist/shim/registry/index.js +19 -0
  201. package/dist/shim/registry/scene-source.js +157 -0
  202. package/dist/shim/registry/walkthrough.js +804 -0
  203. package/dist/shim/render/capture-envelope.js +279 -0
  204. package/dist/shim/render/heat-colormap.js +92 -0
  205. package/dist/shim/render/render-stages.js +855 -0
  206. package/dist/shim/render/sun-look.js +33 -0
  207. package/dist/shim/render-state/apply.js +262 -0
  208. package/dist/shim/render-state/index.js +74 -0
  209. package/dist/shim/rpc/index.js +207 -0
  210. package/dist/shim/runtime/index.js +1023 -0
  211. package/dist/shim/storage/boot.js +69 -0
  212. package/dist/shim/storage/index.js +169 -0
  213. package/dist/shim/storage/local-storage-provider.js +230 -0
  214. package/dist/shim/storage/make-namespace-store.js +132 -0
  215. package/dist/shim/storage/modules.js +249 -0
  216. package/dist/shim/storage/projects.js +150 -0
  217. package/llms.txt +12 -0
  218. package/package.json +63 -4
@@ -0,0 +1,33 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * parts/index.js — THE parts barrel (@descix/frqtl-web/parts): one export per file in this
5
+ * directory, named by the file. The directory IS the roster — a part joins by being a file here,
6
+ * and the barrel lists exactly the directory (lib/frqtl/parts/test/barrel-equals-directory.test.mjs).
7
+ *
8
+ * Importing it registers every published kind in this realm (each part module registers its own
9
+ * kind with objects/kinds.js when it evaluates).
10
+ */
11
+
12
+ export { CameraControls } from './CameraControls.js';
13
+ export { CameraPath } from './CameraPath.js';
14
+ export { CollisionActions } from './CollisionActions.js';
15
+ export { CollisionTypes } from './CollisionTypes.js';
16
+ export { DetectorWall } from './DetectorWall.js';
17
+ export { FrqtlControlOverlay } from './FrqtlControlOverlay.js';
18
+ export { FrqtlControlsPanel } from './FrqtlControlsPanel.js';
19
+ export { FrqtlVision } from './FrqtlVision.js';
20
+ export { GraphingDetectorWall } from './GraphingDetectorWall.js';
21
+ export { LargeObject } from './LargeObject.js';
22
+ export { LightColor } from './LightColor.js';
23
+ export { LiveChart } from './LiveChart.js';
24
+ export { LogicGate } from './LogicGate.js';
25
+ export { OvenBox } from './OvenBox.js';
26
+ export { PointSource } from './PointSource.js';
27
+ export { Rectangle } from './Rectangle.js';
28
+ export { SlitLightSource } from './SlitLightSource.js';
29
+ export { SlitScreen } from './SlitScreen.js';
30
+ export { SphereEmitter } from './SphereEmitter.js';
31
+ export { Trace } from './Trace.js';
32
+ export { Tunnel } from './Tunnel.js';
33
+ export { Wall } from './Wall.js';
@@ -0,0 +1,33 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — 1B · Absorbing Wall (3D)
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
7
+ * (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
8
+ * the parts (`exp.addObject`) and the render intent are all here; the sugar
9
+ * objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
10
+ * (rung cl1b-absorbing-wall-3d).
11
+ */
12
+
13
+ import { SphereEmitter } from '@descix/frqtl-web/parts';
14
+
15
+ export default function buildExperiment(exp) {
16
+ // Collision Lab 1B · 3D — a volumetric (spherical) source into a 3D ABSORB wall
17
+ // slab (z-slab centered on 0 so the 3D spray collides); orbit the camera to view.
18
+ var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
19
+ var cy = Math.round(H / 2);
20
+ var SRC = {
21
+ preset: 'spherical', is2D: false, center: [Math.round(W * 0.15), cy],
22
+ chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
23
+ burstSize: 24, emitEveryTicks: 1
24
+ };
25
+ exp.createScene(W, H, 600, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 3D scene + universe, 600x400x600 — first, then parts
26
+ exp.addObject('SphereEmitter', SRC);
27
+ exp.addObject('Wall', {
28
+ rect: [Math.round(W * 0.6), Math.round(H * 0.2), 20, Math.round(H * 0.6)],
29
+ z: -80, depth: 160,
30
+ collisionBehavior: 'absorb', capacity: 64,
31
+ name: 'absorber', color: [150, 90, 210]
32
+ });
33
+ }
@@ -0,0 +1,32 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — 1B · Absorbing Wall
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
7
+ * (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
8
+ * the parts (`exp.addObject`) and the render intent are all here; the sugar
9
+ * objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
10
+ * (rung cl1b-absorbing-wall).
11
+ */
12
+
13
+ import { SphereEmitter } from '@descix/frqtl-web/parts';
14
+
15
+ export default function buildExperiment(exp) {
16
+ // Collision Lab 1B (Absorbing Wall) — ABSORB v2 (Track A): the wall LO is backed by
17
+ // a REAL leaf frame; a host tick-fn tallies the killed masses into it + radiates.
18
+ var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
19
+ var cy = Math.round(H / 2);
20
+ var SRC = {
21
+ preset: 'spherical', is2D: true, center: [Math.round(W * 0.15), cy],
22
+ chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
23
+ burstSize: 24, emitEveryTicks: 1
24
+ };
25
+ exp.createCanvas(W, H, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 2D canvas + universe — first, then parts
26
+ exp.addObject('SphereEmitter', SRC);
27
+ exp.addObject('Wall', {
28
+ rect: [Math.round(W * 0.6), Math.round(H * 0.2), 20, Math.round(H * 0.6)],
29
+ collisionBehavior: 'absorb', capacity: 64,
30
+ name: 'absorber', color: [150, 90, 210]
31
+ });
32
+ }
@@ -0,0 +1,88 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — Atomic model
5
+ *
6
+ * A sketch file: the WHOLE scene as one `export default function buildExperiment(exp)` module. The
7
+ * world and the three-level frame hierarchy are built here from the engine's own primitives (leaf
8
+ * frames — `engine().Frame`, read when the scene builds — and parent frames from a rect). Registered by
9
+ * lib/shim/registry/walkthrough.js (rung atomic-model).
10
+ *
11
+ * Physics (zero new — bench H1, A1, A2, M1, M4): 48 one-pixel quarks fill an 8x8 proton frame; the
12
+ * proton and one electron sit in a 64x64 atom frame. There is no force equation. Each parent's
13
+ * compress() pulls its children one pixel toward its centre each tick (A1: a stationary parent
14
+ * CONFINES a child), a parent's motion is carried by its children (A2), a parent's mass is its
15
+ * child count (H1), and its wavelength is capacity/mass (M4) — 64/48 → the proton's oscillation.
16
+ * The atom is stationary: the lab frame. `bonding` (noEscape) traps each quark in its parent's
17
+ * live rect, so the nucleus holds together: binding emerges from compress, not from a law.
18
+ *
19
+ * Order (it seeds the run): world → the three layers → quarks (grid-filled, registered) → proton
20
+ * from its rect → electron (registered) → atom from its rect → electron into the atom.
21
+ */
22
+
23
+ import { Rectangle } from '@descix/frqtl-web/parts';
24
+ import * as OrbitBag from '@descix/frqtl-web/compat/renderer/orbit-bag.js';
25
+ import { engine } from '@descix/frqtl-web/compat/engine.js';
26
+
27
+ export default function buildExperiment(exp) {
28
+ var W = 600, H = 600; // THE WORLD this scene states (its co-init call below)
29
+
30
+ var QUARK_LAYER = 0; // 1x1 leaves
31
+ var SUBATOMIC_LAYER = 3; // the proton: 8x8, capacity 64
32
+ var ATOM_LAYER = 6; // the atom: 64x64, capacity 4096
33
+ var QUARKS_PER_PROTON = 48; // the proton's mass at t=0 → wavelength 64/48
34
+
35
+ // ── WORLD — a 2D canvas + universe: quark layer fundamental, λ multiplier 1, quarks confined ──
36
+ exp.createCanvas(W, H, { dimension: QUARK_LAYER, wavelengthScale: 1, bonding: true });
37
+ var u = exp.universe;
38
+
39
+ var quarkDim = u.addDimension(QUARK_LAYER);
40
+ var subatomicDim = u.addDimension(SUBATOMIC_LAYER);
41
+ var atomDim = u.addDimension(ATOM_LAYER);
42
+ var subatomicW = subatomicDim.mb_w, atomW = atomDim.mb_w;
43
+
44
+ var Frame = engine().Frame, Charge = engine().Charge; // the engine's own frame primitive + charge ABI
45
+ var cx = Math.round(W / 2), cy = Math.round(H / 2);
46
+ var atomRect = new Rectangle(cx, cy, atomW, atomW, null, true);
47
+ var protonRect = new Rectangle(cx, cy, subatomicW, subatomicW, null, true);
48
+ var rect = exp.runner.canvas_rect;
49
+
50
+ // ── QUARKS — fill the proton rect column by column ─────────────────────
51
+ var quarkW = Math.max(1, Math.pow(2, QUARK_LAYER));
52
+ var quarks = [];
53
+ for (var x = protonRect.left; x < protonRect.left + protonRect.w && quarks.length < QUARKS_PER_PROTON; x += quarkW) {
54
+ for (var y = protonRect.top; y < protonRect.top + protonRect.h && quarks.length < QUARKS_PER_PROTON; y += quarkW) {
55
+ var q = Frame.makeLeafFrame(x, y, QUARK_LAYER, 1, 0, 0, false, Charge.POSITIVE, rect);
56
+ q.name = 'quark_' + quarks.length;
57
+ quarks.push(q);
58
+ }
59
+ }
60
+ for (var i = 0; i < quarks.length; i++) { quarkDim.addFrame(quarks[i]); }
61
+
62
+ // ── PROTON — the parent frame of the quarks ────────────────────────────
63
+ var proton = subatomicDim.createFrameFromRect(protonRect, quarks, 0, 0);
64
+ proton.name = 'proton';
65
+
66
+ // ── ELECTRON — one negative leaf, upper-right of the atom ──────────────
67
+ var electron = Frame.makeLeafFrame(atomRect.left + Math.round(atomW * 0.75), atomRect.top + Math.round(atomW * 0.25),
68
+ QUARK_LAYER, 1, 0, 0, false, Charge.NEGATIVE, rect);
69
+ electron.name = 'electron';
70
+ quarkDim.addFrame(electron);
71
+
72
+ // ── ATOM — stationary parent of the proton; the electron joins directly ─
73
+ var atom = atomDim.createFrameFromRect(atomRect, [proton], 0, 0, true, true);
74
+ atom.name = 'atom';
75
+ atom.insert(electron);
76
+
77
+ u.leafQuantaWidth = quarkW;
78
+ u.brownianMotion = true;
79
+
80
+ // ── VIEW — frame the atom (render only: the 2D camera's `distance` is the visible physics-px
81
+ // height). The atom is 64 px in the world; at 1.5x its width it fills two thirds of the view,
82
+ // so the proton, its quarks and the electron are individually legible. ─────────────────────
83
+ var cam = exp.runner.cameraControl;
84
+ var flat = !OrbitBag.hasAngularPose(cam);
85
+ if (cam && typeof cam.setOrbit === 'function' && flat) {
86
+ cam.setOrbit({ target: { x: cx, y: cy }, distance: Math.round(atomW * 1.5) });
87
+ }
88
+ }
@@ -0,0 +1,46 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — Big Bang
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world and the
7
+ * point source are built here; the sugar (PointSource — deprecated V1 sugar, byte-identical), the
8
+ * renderer and the engine are imports. Registered by lib/shim/registry/walkthrough.js (rung big-bang).
9
+ *
10
+ * Physics (zero new — bench M1, H1, H2a-c): one radial burst at the canvas centre — 72 rays (5°) at
11
+ * radii 32 → 2 — of leaves with NO initial momentum (emergentPhysics). The world generates parent
12
+ * frames top-down (by how many frames an area can parent, brownian on), so a uniform field clumps into
13
+ * filaments and voids with no gravity law and no cosmological constant. The source shuts off after
14
+ * 20 s of ticks (1200).
15
+ *
16
+ * Order (it seeds the run): world (with its parent-generation interval) → source → the shut-off tick-fn.
17
+ */
18
+
19
+ import { PointSource } from '@descix/frqtl-web/parts';
20
+
21
+ export default function buildExperiment(exp) {
22
+ var W = 600, H = 600; // THE WORLD this scene states (its co-init call below)
23
+
24
+ var SOURCE_DIAMETER = 64; // emission radii 32 → 2 (step 2)
25
+ var ANGLE_STEP = 5; // degrees between rays → 72 rays
26
+ var EMIT_TICKS = 20 * 60; // the source fires for 1200 ticks
27
+
28
+ // ── WORLD — a 2D canvas + universe: fundamental layer 0, λ multiplier 1, parents top-down ──
29
+ // iframeIntervalSeconds 1/60: parents are generated EVERY tick (the engine freezes the interval at
30
+ // init, so it rides the world cfg — at the default 1 s, Phase B ran every 60 ticks).
31
+ exp.createCanvas(W, H, { dimension: 0, wavelengthScale: 1, frames: true, iframeIntervalSeconds: 1 / 60 });
32
+ var u = exp.universe;
33
+
34
+ // ── THE SOURCE — at the centre, no initial momentum, random charge ──────
35
+ var source = new PointSource(u, Math.floor(W / 2), Math.floor(H / 2), SOURCE_DIAMETER,
36
+ 0, null, true, Infinity, 0, ANGLE_STEP,
37
+ { emergentPhysics: true, minRadius: 2 });
38
+
39
+ // ── SHUT-OFF — the bang is one burst era, then the field evolves on its own ──
40
+ var t = 0;
41
+ exp.addTickFunction(function () {
42
+ t++;
43
+ if (t >= EMIT_TICKS) { source.setActive(false); }
44
+ return [];
45
+ });
46
+ }
@@ -0,0 +1,81 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — Blackbody
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world, the
7
+ * oven, its thermostat and the radiation sink are all constructed here; the sugar objects
8
+ * (OvenBox, LargeObject), the renderer and the engine are imports. Registered by
9
+ * lib/shim/registry/walkthrough.js (rung blackbody).
10
+ *
11
+ * Physics (zero new — composition of goldened behaviours): a cavity of absorbing walls holds a
12
+ * population of leaves; the OvenBox thermostat keeps the cavity's quanta near the TEMPERATURE
13
+ * target by emitting, and colliding leaves merge into parent frames (bench H2a–c,
14
+ * collide → merge → promote; `withInterQuantumCollisions`). A parent's fullness IS its frequency
15
+ * (wavelength = capacity/mass, bench M4), so what leaks out of the slit in the right wall is a
16
+ * spectrum of frame sizes — the emergent Planck-like curve — with no energy quanta assumed.
17
+ * Everything that escapes is absorbed by the sink at the right edge (the experiment boundary).
18
+ *
19
+ * Order matters (it seeds the run): the world, the brownian/collision flags, the quantum
20
+ * layer, the thermostat SLOT (reserved before the oven so the oven's own tick-fns run after it),
21
+ * the oven, the thermostat, the sink. Same calls, same order, same seed ⇒ the same RNG stream.
22
+ */
23
+
24
+ import { CollisionActions, CollisionTypes, FrqtlControlOverlay, FrqtlControlsPanel, Rectangle } from '@descix/frqtl-web/parts';
25
+ import * as ControlRegistry from '@descix/frqtl-web/shim/controls/registry.js';
26
+
27
+ export default function buildExperiment(exp) {
28
+ var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
29
+
30
+ var TEMPERATURE = 1500; // target quanta inside the cavity (the knob below)
31
+ var QUANTUM_DIM = 3; // the layer leaves promote into: an 8 px box edge, 64 cells of capacity in 2D
32
+ var BOX_FRACTION = 1 / 3; // the cavity's outer box, as a fraction of the canvas
33
+ var WALL = 10; // wall thickness (px)
34
+ var SLIT_HEIGHT = 60; // the radiation slit in the right wall (px)
35
+ var SINK_DEPTH = 30; // the absorbing strip at the right canvas edge (px)
36
+
37
+ // ── WORLD — a 2D canvas + universe, fundamental layer 0 (1x1 leaves), λ multiplier 1 ──
38
+ exp.createCanvas(W, H, { dimension: 0, wavelengthScale: 1 });
39
+ var u = exp.universe;
40
+ u.brownianMotion = true;
41
+ u.withInterQuantumCollisions = true;
42
+ u.addDimension(QUANTUM_DIM);
43
+
44
+ // ── THE OVEN — the cavity, centred on the canvas ────────────────────────
45
+ var outer = new Rectangle(Math.round(W / 2), Math.round(H / 2),
46
+ Math.round(W * BOX_FRACTION), Math.round(H * BOX_FRACTION), null, true);
47
+ // The thermostat runs FIRST each tick: reserve its slot before the oven registers its own.
48
+ var thermostatSlot = u.experimentTickFunctions.length;
49
+ u.experimentTickFunctions.push(null);
50
+ var oven = exp.addObject('OvenBox', {
51
+ name: 'oven',
52
+ left: outer.left + WALL, top: outer.top + WALL,
53
+ width: outer.w - 2 * WALL, height: outer.h - 2 * WALL,
54
+ wallThickness: WALL, temperature: TEMPERATURE,
55
+ slitHeight: SLIT_HEIGHT, // the slit is in the RIGHT wall (OvenBox's own slit side)
56
+ color: [120, 120, 120]
57
+ });
58
+ u.experimentTickFunctions[thermostatSlot] = oven.getTemperatureTickFunction();
59
+
60
+ // ── THE SINK — the experiment boundary on the slit side ─────────────────
61
+ exp.addObject('LargeObject', {
62
+ name: 'radiation_sink',
63
+ rect: { left: W - SINK_DEPTH, top: 0, w: SINK_DEPTH, h: H },
64
+ collision_type: CollisionTypes.NONE, actions: [CollisionActions.ABSORB],
65
+ reposition: 'closest', color: [0, 0, 0]
66
+ });
67
+
68
+ // ── THE KNOB — temperature, live (the oven's own thermostat target) ─────
69
+ var registry = ControlRegistry.makeControlRegistry();
70
+ registry.registerControls('blackbody', [
71
+ { kind: 'scalar', id: 'temperature', label: 'temperature (quanta)', min: 50, max: 8000, step: 50,
72
+ value: TEMPERATURE, fmt: function (v) { return String(v); },
73
+ onChange: function (v) { oven.setTemperature(v); },
74
+ place: { x: 0.02, y: 0.03 } }
75
+ ]);
76
+ // Chrome (a page only): expose the knobs to a host frame, and mount the overlay through the
77
+ // runner's ONE door (runner.controlHost) — before a canvas is mounted (a notebook pane) there is
78
+ // none, so the knobs stay in the exposed registry only.
79
+ FrqtlControlsPanel.exposeRegistry(registry, { runner: exp.runner });
80
+ if (exp.runner.controlHost) { FrqtlControlOverlay.mount(exp.runner, { registry: registry, name: 'blackbody' }); }
81
+ }
@@ -0,0 +1,127 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — Circuit SAT (a half-adder)
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world, the two
7
+ * logic gates, the four rail traces with their input emitters, the two grounded readouts, the live
8
+ * input knobs and the framing are all here; the sugar (LogicGate, Trace, SphereEmitter), the
9
+ * renderer and the engine are imports. Registered by lib/shim/registry/walkthrough.js (rung
10
+ * circuit-sat).
11
+ *
12
+ * Physics (zero new — bench LO1/LO6/LO7 + the Trace wiring): input A and input B are laser
13
+ * emitters that drive quanta down rail traces into an XOR gate (SUM) and an AND gate (CARRY). A
14
+ * gate's valve opens only when its two input ammeters read the logic it embodies, so the circuit
15
+ * SETTLES onto the half-adder's answer: SUM = A xor B, CARRY = A and B, read as the current on each
16
+ * grounded readout. No search algorithm runs.
17
+ *
18
+ * Order (it seeds the run): world → SUM gate → CARRY gate → per lane plan row,
19
+ * the rail trace then its emitter (switched to its input) → the two readouts.
20
+ */
21
+
22
+ import { FrqtlControlOverlay, FrqtlControlsPanel, LogicGate, SphereEmitter, Trace } from '@descix/frqtl-web/parts';
23
+ import * as ControlRegistry from '@descix/frqtl-web/shim/controls/registry.js';
24
+
25
+ export default function buildExperiment(exp) {
26
+ var W = 760, H = 520; // THE WORLD this scene states (its co-init call below)
27
+ var LEFT = 60, TOP = 15; // the circuit's top-left
28
+ var RAIL = 6; // rail voltage: quanta per tick per input emitter
29
+ var LEAD = 24, READOUT = 24; // input-rail and readout trace lengths
30
+ var INSET = 4; // each input emitter sits this far inside its rail's bore mouth
31
+ var STATE = { a: true, b: false }; // input A HIGH at start: SUM reads 1, CARRY reads 0
32
+
33
+ // The input emitter: a narrow 2D laser on the fundamental layer, random ± charge.
34
+ function emitterBag(center) {
35
+ var bag = { preset: 'laser', is2D: true, R: 24, orientationDeg: 0, emissionBoxSide: 0,
36
+ fundamentalDimension: 0, burstSize: RAIL, emitEveryTicks: 1, chargeMode: 'random' };
37
+ if (center) { bag.center = center; }
38
+ return bag;
39
+ }
40
+
41
+ // ── WORLD — a 2D canvas + universe; the emitter's own world (its worldCfg) ──
42
+ exp.createCanvas(W, H, SphereEmitter.worldCfg(emitterBag(null)));
43
+ var u = exp.universe;
44
+
45
+ var MIN = LogicGate.MINIMAL; // bore 3 / wall 3 / length 20
46
+ var cell = LogicGate.cellSize(MIN.bore, MIN.wallThickness, MIN.length);
47
+ var gateOpts = { bore: MIN.bore, wallThickness: MIN.wallThickness, length: MIN.length, supplyVoltage: RAIL };
48
+
49
+ // ── THE GATES — SUM (XOR) above CARRY (AND) ─────────────────────────────
50
+ var sum = exp.addObject('LogicGate', Object.assign({ logic: 'XOR', left: LEFT, top: TOP, name: 'SUM', color: [200, 170, 70] }, gateOpts));
51
+ var carry = exp.addObject('LogicGate', Object.assign({ logic: 'AND', left: LEFT, top: TOP + cell.h, name: 'CARRY', color: [170, 200, 90] }, gateOpts));
52
+ var gates = { SUM: sum, CARRY: carry };
53
+
54
+ // ── THE INPUT RAILS — the lane plan: B and A are SWAPPED on CARRY (AND commutes; the swap buys
55
+ // a lane pitch of separation between the two inputs) ──────────────────────────────────────
56
+ var LANES = [
57
+ { input: 'A', gate: 'SUM', port: 'inPortA' }, { input: 'B', gate: 'SUM', port: 'inPortB' },
58
+ { input: 'B', gate: 'CARRY', port: 'inPortA' }, { input: 'A', gate: 'CARRY', port: 'inPortB' }
59
+ ];
60
+ var rails = { A: [], B: [] }, parts = [];
61
+ LANES.forEach(function (row, i) {
62
+ var port = gates[row.gate][row.port], y = port[1];
63
+ var t = new Trace(u, { path: [[port[0] - LEAD, y], [port[0], y]], bore: MIN.bore,
64
+ wallThickness: MIN.wallThickness, name: 'rail' + row.input + i, color: [90, 120, 200] });
65
+ var em = exp.addObject('SphereEmitter', Object.assign(emitterBag([t.inPort[0] + INSET, y]),
66
+ { name: 'input' + row.input + i })); // parked inside the bore mouth
67
+ em.setActive(row.input === 'A' ? STATE.a : STATE.b);
68
+ rails[row.input].push(em);
69
+ parts.push(t);
70
+ });
71
+
72
+ // ── THE READOUTS — grounded traces off each gate's outlet: their current IS the answer ──
73
+ function readout(gate, name) {
74
+ return new Trace(u, { path: [[gate.outPort[0], gate.outPort[1]], [gate.outPort[0] + READOUT, gate.outPort[1]]],
75
+ bore: MIN.bore, wallThickness: MIN.wallThickness, ground: true, name: name, color: [60, 160, 220] });
76
+ }
77
+ var readouts = { SUM: readout(sum, 'SUM_out'), CARRY: readout(carry, 'CARRY_out') };
78
+ parts.push(readouts.SUM, readouts.CARRY);
79
+ parts.forEach(function (p) { p.attachToRunner(exp.runner); }); // every trace reads the tick stream (a declared gate subscribed at addObject)
80
+
81
+ function setInput(which, on) {
82
+ STATE[which] = !!on;
83
+ rails[which === 'a' ? 'A' : 'B'].forEach(function (em) { em.setActive(STATE[which]); });
84
+ }
85
+
86
+ // ── THE CIRCUIT, PUBLISHED — the parts and the geometry a harness reads (the half-adder
87
+ // acceptance rig): each gate's own getState(), each readout's calibrated getDelivered(), the
88
+ // input switch, and the layout values its settle budget is computed from. ───────────────
89
+ exp.circuit = {
90
+ gates: gates, readouts: readouts,
91
+ rails: rails, lanes: LANES, setInput: setInput,
92
+ lead: LEAD, readout: READOUT, inset: INSET
93
+ };
94
+
95
+ // ── THE VIEW — frame the 72x54 px circuit (render only: the 2D camera's distance IS the visible
96
+ // physics-px height) ─────────────────────────────────────────────────────────────────────
97
+ var fp = [LEFT - LEAD, TOP, LEAD + cell.w + READOUT + 4, cell.h * 2];
98
+ var cam = exp.runner.cameraControl;
99
+ if (cam && typeof cam.setOrbit === 'function') {
100
+ cam.setOrbit({ target: { x: fp[0] + fp[2] / 2, y: fp[1] + fp[3] / 2 },
101
+ distance: Math.max(fp[3] * 1.35, fp[2] * 1.35 * H / W) });
102
+ }
103
+
104
+ // ── THE KNOBS (on the canvas): the two inputs and the rail ──────────────
105
+ var registry = ControlRegistry.makeControlRegistry();
106
+ registry.registerControls('circuit-sat', [
107
+ { kind: 'scalar', id: 'inputA', label: 'input A', min: 0, max: 1, step: 1, value: STATE.a ? 1 : 0, widget: 'toggle',
108
+ fmt: function (v) { return v ? '1 (HIGH)' : '0 (LOW)'; }, onChange: function (v) { setInput('a', v); },
109
+ place: { x: 0.02, y: 0.80 } },
110
+ { kind: 'scalar', id: 'inputB', label: 'input B', min: 0, max: 1, step: 1, value: STATE.b ? 1 : 0, widget: 'toggle',
111
+ fmt: function (v) { return v ? '1 (HIGH)' : '0 (LOW)'; }, onChange: function (v) { setInput('b', v); },
112
+ place: { x: 0.02, y: 0.88 } },
113
+ { kind: 'scalar', id: 'rail', label: 'rail (burst/tick)', min: 1, max: 16, step: 1, value: RAIL,
114
+ fmt: function (v) { return String(v) + ' q/tick'; },
115
+ onChange: function (v) {
116
+ RAIL = Math.max(0, Math.round(v));
117
+ sum.setSupplyVoltage(RAIL); carry.setSupplyVoltage(RAIL);
118
+ rails.A.concat(rails.B).forEach(function (em) { em.setBurstSize(RAIL); });
119
+ },
120
+ place: { x: 0.30, y: 0.88 } }
121
+ ]);
122
+ // Chrome (a page only): expose the knobs to a host frame, and mount the overlay through the
123
+ // runner's ONE door (runner.controlHost) — before a canvas is mounted (a notebook pane) there is
124
+ // none, so the knobs stay in the exposed registry only.
125
+ FrqtlControlsPanel.exposeRegistry(registry, { runner: exp.runner });
126
+ if (exp.runner.controlHost) { FrqtlControlOverlay.mount(exp.runner, { registry: registry, name: 'circuit-sat' }); }
127
+ }
@@ -0,0 +1,107 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — 3D Collisions Lab
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The 3D world, the
7
+ * seven head-on lanes of equal-mass voxel pairs, their live knobs and the look are all here; the
8
+ * lane density table (`collisionMatrixLanes`), the renderer and the engine are imports.
9
+ * Registered by lib/shim/registry/walkthrough.js (rung collision-matrix-3d).
10
+ *
11
+ * Physics (zero new — bench C2, C3, C5, SU1): each lane fires two equal bodies at each other at
12
+ * speed_prob 1 along x; a lane's density (mass / capacity) sets how hard the bodies are — rigid
13
+ * bodies bounce cleanly, soft ones press into each other before parting. The lanes sit on a 3x3
14
+ * y/z grid, so the same contact happens at seven depths: collision is identical on any axis
15
+ * (2D ≡ 3D). The same engine that makes light makes mechanics — no second physics.
16
+ *
17
+ * Order (it seeds the run): world → collisions on → the voxel layer →
18
+ * per lane, body A then body B (engine-built, capacity read back once) → seat every body.
19
+ */
20
+
21
+ import { CameraControls, FrqtlControlOverlay, FrqtlControlsPanel } from '@descix/frqtl-web/parts';
22
+ import * as ControlRegistry from '@descix/frqtl-web/shim/controls/registry.js';
23
+ import { collisionMatrixLanes } from '@descix/frqtl-web/objects/setup/_collisionMatrixLanes.js';
24
+
25
+ export default function buildExperiment(exp) {
26
+ var W = 700, H = 700, D = 700; // THE WORLD this scene states (its co-init call below)
27
+ var LEVEL = 6; // the voxel layer: 64 px bodies, capacity 4096
28
+
29
+ // ── WORLD — a 3D scene + universe W×H×D, fundamental layer 0, λ multiplier 1 ──
30
+ exp.createScene(W, H, D, { dimension: 0, wavelengthScale: 1 });
31
+ var u = exp.universe;
32
+ u.withInterQuantumCollisions = true;
33
+ u.addDimension(LEVEL);
34
+ exp.runner.setRateLimits({ tpsLimit: 240 }); // pacing only: wall-clock, never the ticks
35
+
36
+ var lanesTable = collisionMatrixLanes; // the ONE lane density table (imported)
37
+ var voxel = Math.pow(2, LEVEL), half = voxel / 2;
38
+ var midX = Math.floor(W * 0.5);
39
+ var ax0 = midX - Math.floor(1.5 * voxel); // body A, left of centre
40
+ var bx0 = midX + Math.floor(1.5 * voxel); // body B, right of centre
41
+ var N = lanesTable.LANE_DENSITIES.length; // 7 lanes
42
+ var spacing = Math.round(H / 4);
43
+ var laneYs = [], laneZs = [];
44
+ for (var k = 0; k < N; k++) {
45
+ laneYs.push(Math.round(H / 2 + (Math.floor(k / 3) - 1) * spacing)); // rows 0,0,0,1,1,1,2
46
+ laneZs.push(((k % 3) - 1) * spacing); // cols 0,1,2,…
47
+ }
48
+
49
+ // The live knobs: an offset (the bodies miss by that much) and an angle (vy against vx).
50
+ var offsetPx = 0, angleVy = 0;
51
+ function seat(qid, cx, cy, cz, dirX, mass) {
52
+ u.setFrameProperty(qid, 'x', cx);
53
+ u.setFrameProperty(qid, 'y', cy + dirX * offsetPx);
54
+ u.setFrameProperty(qid, 'z', cz);
55
+ u.setFrameProperty(qid, 'vx', dirX);
56
+ u.setFrameProperty(qid, 'vy', -dirX * angleVy);
57
+ u.setFrameProperty(qid, 'vz', 0);
58
+ u.setFrameProperty(qid, 'mass', mass);
59
+ u.setFrameProperty(qid, 'speed_prob', 1.0);
60
+ }
61
+
62
+ // ── THE BODIES — A then B per lane; the capacity is read back from the engine, never assumed ──
63
+ var built = [], capacity = null;
64
+ for (var i = 0; i < N; i++) {
65
+ var a = u.makeLeafFrame(ax0 - half, laneYs[i] - half, LEVEL, null, 1, 0, true, 0, null);
66
+ u.injectFrame(a);
67
+ var aQid = u.getFrameQuantumId(a);
68
+ var b = u.makeLeafFrame(bx0 - half, laneYs[i] - half, LEVEL, null, -1, 0, true, 0, null);
69
+ u.injectFrame(b);
70
+ var bQid = u.getFrameQuantumId(b);
71
+ if (capacity === null) { capacity = u.getFrameProperty(aQid, 'mass'); }
72
+ built.push({ aQid: aQid, bQid: bQid });
73
+ }
74
+ var masses = lanesTable.laneMassesForCapacity(capacity);
75
+ var lanes = built.map(function (q, i) {
76
+ return { aQid: q.aQid, bQid: q.bQid, mass: masses[i], laneY: laneYs[i], laneZ: laneZs[i] };
77
+ });
78
+ function reseatAll() {
79
+ lanes.forEach(function (l) {
80
+ seat(l.aQid, ax0, l.laneY, l.laneZ, 1, l.mass);
81
+ seat(l.bQid, bx0, l.laneY, l.laneZ, -1, l.mass);
82
+ });
83
+ }
84
+ reseatAll();
85
+
86
+ // ── THE LOOK — lit spheres, coloured by fullness (sparse red → dense violet) ──
87
+ exp.render({ shape: 'sphere', colorScheme: 'roygbiv' });
88
+
89
+ // ── THE KNOBS (on the canvas) + MOVE/LOOK pads ──
90
+ var registry = ControlRegistry.makeControlRegistry();
91
+ registry.registerControls('collision-matrix-3d', [
92
+ { kind: 'scalar', id: 'offset', label: 'offset', min: 0, max: 48, step: 4, value: 0,
93
+ fmt: function (v) { return String(v); }, onChange: function (v) { offsetPx = v; reseatAll(); },
94
+ place: { x: 0.02, y: 0.03 } },
95
+ { kind: 'scalar', id: 'angle', label: 'angle', min: 0, max: 1, step: 0.1, value: 0,
96
+ fmt: function (v) { return v.toFixed(1); }, onChange: function (v) { angleVy = v; reseatAll(); },
97
+ place: { x: 0.02, y: 0.12 } }
98
+ ]);
99
+ // Chrome (a page only): expose the knobs to a host frame, and mount the overlay through the
100
+ // runner's ONE door (runner.controlHost) — before a canvas is mounted (a notebook pane) there is
101
+ // none, so the knobs stay in the exposed registry only.
102
+ FrqtlControlsPanel.exposeRegistry(registry, { runner: exp.runner });
103
+ if (exp.runner.controlHost) { FrqtlControlOverlay.mount(exp.runner, { registry: registry, name: 'collision-matrix-3d' }); }
104
+ if (typeof document !== 'undefined') {
105
+ if (exp.runner.controlHost) { CameraControls.attach(exp.runner, exp.runner.controlHost); }
106
+ }
107
+ }
@@ -0,0 +1,36 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — 1C · Counting Wall (3D)
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
7
+ * (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
8
+ * the parts (`exp.addObject`) and the render intent are all here; the sugar
9
+ * objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
10
+ * (rung cl1c-counting-wall-3d).
11
+ */
12
+
13
+ import { SphereEmitter } from '@descix/frqtl-web/parts';
14
+
15
+ export default function buildExperiment(exp) {
16
+ // Collision Lab 1C · 3D — a volumetric source into a 3D Detector Screen slab
17
+ // (destroy+count) + a LIVE in-scene chart, textured in three; orbit to view.
18
+ var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
19
+ var cy = Math.round(H / 2);
20
+ var SRC = {
21
+ preset: 'spherical', is2D: false, center: [Math.round(W * 0.15), cy],
22
+ chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
23
+ burstSize: 24, emitEveryTicks: 1
24
+ };
25
+ exp.createScene(W, H, 600, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 3D scene + universe, 600x400x600 — first, then parts
26
+ exp.addObject('SphereEmitter', SRC);
27
+ var screen = exp.addObject('Wall', {
28
+ rect: [Math.round(W * 0.6), 0, 20, H], z: -80, depth: 160,
29
+ preset: 'detector-screen', name: 'screen', color: [120, 200, 120]
30
+ });
31
+ exp.addObject('LiveChart', {
32
+ source: screen,
33
+ rect: [Math.round(W * 0.66), Math.round(H * 0.08), Math.round(W * 0.28), Math.round(H * 0.5)],
34
+ series: 'bar', label: 'arrivals by y', refreshMs: 100, color: [24, 32, 46]
35
+ });
36
+ }
@@ -0,0 +1,38 @@
1
+ /* Copyright 2023-2026 by Essam Abadir */
2
+
3
+ /**
4
+ * SCENE — 1C · Counting Wall
5
+ *
6
+ * A sketch file: the WHOLE scene as one ES module, `export default function buildExperiment(exp)`. The world
7
+ * (its co-init call, `exp.createCanvas` / `exp.createScene` — the one place its size is written),
8
+ * the parts (`exp.addObject`) and the render intent are all here; the sugar
9
+ * objects, renderer and engine are imports. Registered by lib/shim/registry/walkthrough.js
10
+ * (rung cl1c-counting-wall).
11
+ */
12
+
13
+ import { SphereEmitter } from '@descix/frqtl-web/parts';
14
+
15
+ export default function buildExperiment(exp) {
16
+ // Collision Lab 1C (Counting Wall) — the Detector Screen preset (destroy + count),
17
+ // with a LIVE in-scene LiveChart of the running y-histogram placed beside it.
18
+ var W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
19
+ var cy = Math.round(H / 2);
20
+ var SRC = {
21
+ preset: 'spherical', is2D: true, center: [Math.round(W * 0.15), cy],
22
+ chargeMode: 'random', fundamentalDimension: 2, wavelength: 32,
23
+ burstSize: 24, emitEveryTicks: 1
24
+ };
25
+ exp.createCanvas(W, H, SphereEmitter.worldCfg(SRC)); // THE WORLD: a 2D canvas + universe — first, then parts
26
+ exp.addObject('SphereEmitter', SRC);
27
+ var screen = exp.addObject('Wall', {
28
+ rect: [Math.round(W * 0.6), 0, 20, H],
29
+ preset: 'detector-screen', name: 'screen', color: [120, 200, 120]
30
+ });
31
+ // The interactive counting demo: a live chart of the detector's histogram,
32
+ // placed to the RIGHT of the screen (past where the quanta are absorbed).
33
+ exp.addObject('LiveChart', {
34
+ source: screen,
35
+ rect: [Math.round(W * 0.66), Math.round(H * 0.08), Math.round(W * 0.28), Math.round(H * 0.5)],
36
+ series: 'bar', label: 'arrivals by y', refreshMs: 100, color: [24, 32, 46]
37
+ });
38
+ }