@descix/frqtl-node 0.6.1-p3.3 → 0.7.0-p0

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 (64) hide show
  1. package/CLAUDE.md +3 -3
  2. package/README.md +4 -3
  3. package/dist/dist-manifest.json +26 -56
  4. package/dist/frqtl/experiments/_shared/empty-sketch.js +17 -0
  5. package/dist/frqtl/objects/setup/setupBlackbodySubstrate.js +6 -6
  6. package/dist/frqtl/objects/setup/setupFxSmoke.js +2 -2
  7. package/dist/frqtl/objects/setup/setupG3Collide.js +1 -1
  8. package/dist/frqtl/objects/setup/setupSphereEmitter3D.js +2 -2
  9. package/dist/frqtl/objects/v1-replacements.js +1 -1
  10. package/dist/frqtl/parts/OvenBox.js +2 -2
  11. package/dist/frqtl/parts/SphereEmitter.js +1 -1
  12. package/dist/shim/engine/router.js +18 -6
  13. package/dist/shim/engine/site-assemble.mjs +268 -0
  14. package/dist/shim/engine/site-graph.js +42 -14
  15. package/dist/shim/host/device.js +1 -1
  16. package/dist/shim/index.js +3 -7
  17. package/dist/shim/notebook/catalog.js +2 -2
  18. package/dist/shim/notebook/format.js +1 -1
  19. package/dist/shim/notebook/knob.js +1 -1
  20. package/dist/shim/notebook/notebook-knobs.js +1 -1
  21. package/dist/shim/project/specifiers.js +8 -8
  22. package/dist/shim/registry/copy-gate.js +3 -3
  23. package/dist/shim/registry/index.js +9 -12
  24. package/dist/shim/registry/roster-link.js +142 -0
  25. package/dist/shim/registry/roster.js +595 -0
  26. package/dist/shim/registry/scene-source.js +80 -105
  27. package/dist/shim/registry/studio-landing.js +15 -7
  28. package/dist/shim/render/sun-look.js +1 -2
  29. package/llms.txt +4 -2
  30. package/package.json +1 -1
  31. package/dist/frqtl/scenes/absorbing-wall-3d.js +0 -37
  32. package/dist/frqtl/scenes/absorbing-wall.js +0 -36
  33. package/dist/frqtl/scenes/atomic-model.js +0 -91
  34. package/dist/frqtl/scenes/big-bang.js +0 -49
  35. package/dist/frqtl/scenes/blackbody.js +0 -84
  36. package/dist/frqtl/scenes/circuit-sat.js +0 -131
  37. package/dist/frqtl/scenes/collision-matrix-3d.js +0 -110
  38. package/dist/frqtl/scenes/counting-wall-3d.js +0 -40
  39. package/dist/frqtl/scenes/counting-wall.js +0 -42
  40. package/dist/frqtl/scenes/double-slit.js +0 -34
  41. package/dist/frqtl/scenes/dse-one-emitter.js +0 -200
  42. package/dist/frqtl/scenes/dse-one-slit.js +0 -118
  43. package/dist/frqtl/scenes/dse-photoelectric.js +0 -74
  44. package/dist/frqtl/scenes/dse-tunnel-rebuild.js +0 -279
  45. package/dist/frqtl/scenes/dse-two-emitters.js +0 -201
  46. package/dist/frqtl/scenes/dse-two-slits.js +0 -136
  47. package/dist/frqtl/scenes/empty.js +0 -17
  48. package/dist/frqtl/scenes/laser-buildup.js +0 -56
  49. package/dist/frqtl/scenes/mirror-3d.js +0 -60
  50. package/dist/frqtl/scenes/mirror-route.js +0 -164
  51. package/dist/frqtl/scenes/mirror.js +0 -42
  52. package/dist/frqtl/scenes/omni-3d.js +0 -30
  53. package/dist/frqtl/scenes/omni-source.js +0 -25
  54. package/dist/frqtl/scenes/self-interference.js +0 -42
  55. package/dist/frqtl/scenes/smoking-gun.js +0 -126
  56. package/dist/frqtl/scenes/straight-walk.js +0 -25
  57. package/dist/frqtl/scenes/sun.js +0 -42
  58. package/dist/frqtl/scenes/two-source.js +0 -42
  59. package/dist/frqtl/scenes/wave.js +0 -29
  60. package/dist/shim/engine/artifact-shell.js +0 -211
  61. package/dist/shim/registry/exhibits.js +0 -436
  62. package/dist/shim/registry/scene-roster.js +0 -35
  63. package/dist/shim/registry/showcase-link.js +0 -61
  64. package/dist/shim/registry/walkthrough.js +0 -752
@@ -1,201 +0,0 @@
1
- /* Copyright 2023-2026 by Essam Abadir */
2
-
3
- /**
4
- * SCENE — Two Emitters at the Canonical Leg
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 (`export const parts` + `exp.declare`, or `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 dse-two-emitters).
11
- */
12
-
13
- // ── H1 AT THE CANONICAL DETECTOR LEG ─────────────────────────────────────────
14
- //
15
- // The competence baseline: the most basic thing, done right, seen with eyes. One
16
- // emitter, then two, with the plate PRE-COMPUTED at the leg the ring formula was
17
- // written for and the count-by-y chart registered to that plate. No barrier, no
18
- // slits, no tunnels — nothing between the source and the plate, so a band on the
19
- // chart can have come from nothing but the walk.
20
- //
21
- // WHY THESE EXIST BESIDE `dse-tunnel-rebuild`. That rung places its plate at
22
- // D = 155 px against λ_eff = 128 px — 4.84× the reference leg — which pushes the
23
- // low orders inside the leg where they never reach the plate at all, so only high-m
24
- // rings can land. These two rungs put the plate ON the reference leg instead.
25
- //
26
- // THE GEOMETRY, and it is not re-derived here. λ is the ONLY length; every other
27
- // length below is a dimensionless constant × λ (the `published/dse/scene.js`
28
- // discipline). The ring ladder quoted in CLAUDE.md,
29
- //
30
- // r_m = (λ/4)·√((2m+1)² + 1) [slant, from the emission point]
31
- //
32
- // is the hypotenuse of a right triangle whose TRANSVERSE leg is ρ_m = (2m+1)·λ/4
33
- // and whose AXIAL leg is λ/4 — the "+1" under the root IS the (λ/4)² axial term.
34
- // Solve for the axial leg order by order and every order returns λ/4, so the leg is
35
- // not fitted per-order: it is the one leg the formula was written for. At that leg
36
- // the on-plate bands are ρ_m = (2m+1)·λ/4 from the source's own y, spacing λ/2.
37
- //
38
- // MEASURED, not assumed. `published/dse/h1/` ran exactly this apparatus (bare
39
- // source, plate at D = λ/4 from the emitter, seed 1, detector 'hits') at λ = 160 /
40
- // 320 / 640 and found a symmetric feature on the plate at |Δy| = λ/4 − 4 px — 36 /
41
- // 76 / 156 px — holding that offset to the pixel across a 4× sweep while moving 120
42
- // px in absolute terms. The raw histogram's shape is: a flat PLATEAU across the
43
- // centre, a sharp MINIMUM just inside ±λ/4, and a PEAK riding immediately outside it
44
- // at ±λ/4. At λ=640, counts by Δy/λ: −0.250 → 1107 | −0.225 → 306 | 0.000 → 1011 |
45
- // +0.225 → 297 | +0.250 → 1125. At λ=320 the same shape, shallower. TWO points on
46
- // the wall at ¼ λ_eff, with a plateau — not a hole — between them.
47
- //
48
- // AND THAT IS WHY λ IS 320 HERE. The dip-to-peak separation is 0.025λ: 16 px at
49
- // λ=640, 8 px at λ=320, 4 px at λ=160 — which is why the λ=160 run reads as a broad
50
- // hump with the feature only recoverable by a windowed search on a smoothed profile.
51
- // Below ~λ=300 this stage cannot resolve the thing it exists to show. So this rung
52
- // shows ONE well-resolved band pair per source rather than three mushy ones. The 4 px
53
- // pedestal does NOT scale with λ (it is engine scale — a quantum steps ≤2 px/axis/
54
- // tick) and no mechanism is offered for it here. The m ≥ 1 rungs of the ladder are
55
- // NOT established: of the H1 runs' 8 predicted positions, 3 matched, and the m=1
56
- // positions carried minima.
57
- //
58
- // THE LEG IS ONE NAMED CONSTANT (`legFrac`), and so is the plate's DETECTOR MODE —
59
- // both so they can be moved deliberately from a one-line edit and the consequence
60
- // re-derived, rather than being literals nobody chose.
61
- //
62
- // The beam is the `dse-tunnel-rebuild` beam and the H1 beam, unchanged: wavelength
63
- // arg 3.2 → mass 5 / capacity 16 → fullness 0.3125, which LightColor renders yellow,
64
- // and λ_eff = 3.2 × wavelengthScale. The leaves are emitted COLOURLESS; the colour
65
- // is DERIVED from fullness, never forced.
66
- //
67
- // 2D ONLY, deliberately. There is no `modes` strip and no `exp.is3D` guard: these
68
- // rungs declare one world each. A second dimensional variant would be a separate
69
- // recipe declaration, not a mode.
70
- // THE PAIR. `dse-one-emitter.js` is the SAME apparatus with ONE source entry. The two files differ ONLY in the
71
- // title lines, this note, and their `source*` entries of `parts` (with the world call naming the first
72
- // of them) — lib/frqtl/scenes/test/scene-files.test.mjs fails if anything else diverges, so the A/B the
73
- // pair exists to make stays an A/B.
74
-
75
- import { SphereEmitter, Wall } from '@descix/frqtl-web/parts';
76
-
77
- const CW = 600, H = 400; // THE WORLD this scene states (its co-init call below)
78
- // ── THE ONLY LENGTH ─────────────────────────────────────────────────────
79
- // lamPx is lambda_eff in pixels. 320 is chosen for RESOLUTION, not for how many
80
- // orders fit: the feature this rung exists to show (the minimum just inside the
81
- // band and the peak riding outside it) is 0.025*lambda wide, which is 8 px at
82
- // lambda 320 and 3 px at lambda 128. A stage that cannot separate them shows a
83
- // smooth hump and leaves you unable to say whether the physics or the sampling
84
- // was at fault. One well-resolved pair beats three mushy ones.
85
- const lamPx = 320;
86
- const sinkW = Math.round(lamPx / 16); // 20 — far-edge sink thickness
87
-
88
- // ── THE DETECTOR LEG — ONE NAMED CONSTANT ───────────────────────────────
89
- // D = legFrac * lambda, measured from the SOURCE (there is no barrier, so the
90
- // emitter itself is the reference point of the ladder). legFrac = 1/4 is the
91
- // leg the ring formula was written for; see the block comment above.
92
- const legFrac = 1 / 4;
93
- const D = Math.round(lamPx * legFrac); // 80
94
- const srcX = Math.round(lamPx / 16); // 20 — launch room; the canvas edge is the back sink
95
- const detX = srcX + D; // 100 — the plate's FRONT FACE
96
- // PLATE THICKNESS IS AN OPTICAL CHOICE UNDER A FLUX DETECTOR, not trim. A flux
97
- // probe counts every tick a quantum overlaps it, so it integrates the density
98
- // ACROSS its thickness — and the ring crest is a CIRCLE about the source, not a
99
- // plane. At the m=0 band the crest's slope is dy/dx = -D/y = -1, so a slab t px
100
- // thick smears the band over ~t px of y. Against an 8 px feature a lambda/8 (40
101
- // px) plate would guarantee mush. lambda/64 = 5 px keeps the smear sub-feature
102
- // and is still unjumpable (a quantum steps at most 2 px/axis/tick). A 'hits'
103
- // plate would not care — it counts at first contact, i.e. at the front face —
104
- // which is exactly why the reference H1 runs could afford a lambda/8 slab.
105
- const detW = Math.round(lamPx / 64); // 5 — plate thickness
106
- // ── THE PLATE — A COUNTING WALL THAT DOES NOT ABSORB ────────────────────
107
- // ONE NAMED CONSTANT, so the reading can be swapped in a single edit and the two
108
- // instruments compared rather than argued about:
109
- // 'transparent-probe' = collisionBehavior 'nothing' + mark/count. FLUX. The
110
- // quantum is counted on every tick it overlaps the plate and then flies on,
111
- // so the reading is the OCCUPANCY INTEGRAL at the plate plane — the density
112
- // at that plane. This is the shipped choice.
113
- // 'detector-screen' = destroy + count. HITS. Each quantum is counted once, at
114
- // arrival, and removed: a transit census, the headcount a photographic plate
115
- // would report. It is what the H1 reference runs used.
116
- // NEITHER ABSORBS. 'absorb' stores the arriving mass and re-radiates it — that is
117
- // the photoelectric experiment, and it saturates.
118
- const detectorPreset = 'transparent-probe';
119
-
120
- const lamArg = 3.2; // capacity/mass -> fullness 1/3.2 = 0.3125 (yellow)
121
- const fwl = 4 * (lamPx / lamArg); // 400 -> wavelengthScale floor(400/4) = 100
122
- const wlScale = Math.floor(fwl / 4); // 100
123
- const cy = Math.round(H / 2); // 200 — the source plane's midline
124
- // TWO-SOURCE SEPARATION. Two sources reinforce when their separation is a
125
- // multiple of lambda/2, and the coupling falls as (lambda/d)^2, so lambda/2 is
126
- // the strongest case. At this leg it also puts a band of EACH source on the
127
- // midline y = H/2 (source 1's lower band and source 2's upper band coincide
128
- // there) — the doubly-reinforced central peak, equidistant from both emitters,
129
- // exactly where the one-source rung has no peak at all.
130
- const sep = lamPx / 2; // 160 -> sources at y 120 and 280
131
- // ── THE LIGHT SOURCE ────────────────────────────────────────────────────
132
- // preset 'hemisphere' = 180 degrees of FORWARD (+X) emission: the SAME
133
- // ball-rejected sampler as the omni source with the forward axis' range halved,
134
- // so nothing about the walk changes, it is simply pointed at the plate. The
135
- // default emission box is 0 for this preset, so the source is a single PIXEL —
136
- // load-bearing, because any spread at birth blurs every band by that spread.
137
- // NO START WALL: the backward population is empty by construction, and `wrap` is
138
- // false, so the canvas edge kills whatever transverse wiggle out-runs its
139
- // forward component. One sink, not two.
140
- const SRC = {
141
- preset: 'hemisphere', is2D: true, chargeMode: 'random',
142
- wavelength: lamArg, fundamentalDimension: 2, fundamentalWaveLength: fwl,
143
- burstSize: 8, emitEveryTicks: 1
144
- };
145
-
146
- export const parts = {
147
- source1: [SphereEmitter, { ...SRC, center: [srcX, Math.round(cy - sep / 2)] }],
148
- source2: [SphereEmitter, { ...SRC, center: [srcX, Math.round(cy + sep / 2)] }],
149
- // FAR-EDGE SINK. The plate below counts WITHOUT removing, so quanta fly on past
150
- // it; this is the experiment-boundary sink downstream of the plate. It is not
151
- // load-bearing (wrap is false, so the canvas edge would remove them anyway) — it
152
- // is where the population bound is STATED rather than inferred.
153
- far_sink: [Wall, { rect: [CW - sinkW, 0, sinkW, H], collisionBehavior: 'destroy',
154
- color: [120, 120, 120], alpha: 40 }],
155
- // markTag — the plate's PROVENANCE stamp. The mark hook fires ONLY when the
156
- // collision trigger does (genuine overlap), whereas the engine buffers every
157
- // frame inside the LO's mb_w-PADDED search rect whether it triggered or not. So a
158
- // raw frame count off the buffer is a padded-occupancy reading and the
159
- // mark-filtered count is the trigger-gated census: two different instruments, and
160
- // an offline reader needs the tag to tell them apart.
161
- screen: [Wall, { rect: [detX, 0, detW, H],
162
- preset: detectorPreset, markTag: 'plate', color: [120, 200, 120] }],
163
- };
164
-
165
- export default function buildExperiment(exp) {
166
- if (Math.abs(lamArg * wlScale - lamPx) > 1e-9) {
167
- throw new Error('[dse-h1] lamPx ' + lamPx + ' is not realisable as lambda_eff: the engine ' +
168
- 'can only produce wavelengthArg x floor(fwl/4) = ' + (lamArg * wlScale) + '. (FAIL-LOUD.)');
169
- }
170
- exp.createCanvas(CW, H, SphereEmitter.worldCfg(parts.source1[1])); // THE WORLD: a 2D canvas + universe — first, then parts
171
- const p = exp.declare(parts);
172
- // ── THE INSTRUMENT — the live count-by-y histogram, REGISTERED TO THE PLATE ──
173
- // The panel takes the plate's OWN rect y-origin (0) and height (H), so the two
174
- // are co-extensive on the bin axis at any canvas size, and it starts just past
175
- // the plate's far face so it reads as sitting behind it. orientation 90 = bars
176
- // stacked along y; the chart contract reverses the y scale when horizontal, so
177
- // bin 0 (the SMALLEST y) sits at the TOP, matching the y-DOWN world.
178
- // HONEST LIMIT (inherited from the shipped 2D block, and it is real): the bin
179
- // axis is a CATEGORY axis over the bins that actually received hits, not a linear
180
- // 0..H axis. Registration is exact in ORDER and DIRECTION always, and exact in
181
- // absolute screen height only insofar as the hits span the wall. Here the plate
182
- // is a full-height flux probe under a 180-degree source, so the occupied span
183
- // fills the wall within a few hundred ticks and the axis is very nearly linear —
184
- // but the ladder positions below must be read off the raw census, never measured
185
- // with a ruler on the panel.
186
- // SERIES 'line', NOT 'bar', AND THAT IS A MEASUREMENT NOT A TASTE. The bins are
187
- // per-pixel y, so a full-height plate produces ~400 categories in a ~240 px plot:
188
- // each bar is sub-pixel TALL, its 0.55 fill alpha is diluted by the coverage, and
189
- // the profile collapses into a faint sliver hugging the axis. Measured identically
190
- // on the shipped 1C Counting Wall rung, so it is the chart contract's behaviour at
191
- // this bin count and not something these rungs introduced. A line joins the same 400
192
- // points into one continuous curve, which reads as the profile it is. CAVEAT, stated
193
- // because it bites: Chart.js does not begin a LINE value axis at zero, so once the
194
- // profile's floor rises the curve compresses toward the left edge. Read positions off
195
- // it, not magnitudes; magnitudes come from getCollisionHistogram().
196
- exp.addObject('LiveChart', {
197
- source: p.screen, orientation: 90,
198
- rect: [detX + detW + 8, 0, Math.max(60, CW - (detX + detW + 8) - 8), H],
199
- series: 'line', label: 'count by y', refreshMs: 100, color: [24, 32, 46], alpha: 225
200
- });
201
- }
@@ -1,136 +0,0 @@
1
- /* Copyright 2023-2026 by Essam Abadir */
2
-
3
- /**
4
- * SCENE — Two Slits at the Canonical Leg
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 (`export const parts` + `exp.declare`, or `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 dse-two-slits).
11
- */
12
-
13
- // ── TWO SLITS AT THE CANONICAL LEG ───────────────────────────────────────────
14
- //
15
- // The SAME apparatus as `dse-two-emitters` — same λ, same separation, same leg,
16
- // same detector, same chart — but the two point sources are now SLIT EXITS in
17
- // an opaque barrier rather than two free emitters. ONE emitter sits behind the
18
- // barrier and feeds BOTH slit channels; each channel is a `directed` Tunnel
19
- // with the exit gate COLLAPSED + PHASE-RESET, exactly the composition
20
- // `dse-tunnel-rebuild` already uses to make a slit's own mouth stand in for a
21
- // point source ("THE TUNNEL IS A CONFIGURED EMITTER", see that
22
- // rung's source comment for the flag-by-flag mapping). Composed only: Wall +
23
- // Tunnel, zero new parts, zero engine code.
24
- //
25
- // WHY THE NUMBERS ARE THE SAME AS `dse-two-emitters`. That rung places its two
26
- // SOURCES at y = cy∓sep/2 (120, 280) and its plate at D = λ_eff/4 past the
27
- // SOURCE plane (srcX). Here the slit CENTRES sit at the identical y = 120, 280
28
- // (`sep` unchanged), and the plate sits at the identical D = λ_eff/4 — now
29
- // measured past the SLIT EXIT plane (the barrier's back face), which is this
30
- // apparatus's new "source plane". If the exit gate's point-source claim holds,
31
- // the two apparatuses must draw the SAME picture: a central peak at y = 200
32
- // (source 1's lower band and source 2's upper band coincide there) flanked by
33
- // the outer bands at y = 40 and y = 360 — because nothing about the DERIVATION
34
- // of those positions used "free emitter" anywhere; it only used "point source
35
- // at (x0, y0), phase-locked, λ_eff". A slit exit under `exitPhaseReset` is
36
- // exactly that.
37
- //
38
- // λ_eff, the leg, and the plate/instrument are the scenes/dse-one-emitter.js numbers,
39
- // copied rather than referenced because this rung's body is shaped completely
40
- // differently (a barrier + two Tunnels stand where two bare SphereEmitters
41
- // stood) — there is no shared statement to factor without re-introducing the
42
- // exact config-mirror risk the emitter pair's line-diff test guards elsewhere.
43
- // The bare numbers (320 / 3.2 / 400 / 80 / 160) are the SAME numbers, and the
44
- // fail-loud realisability check is repeated verbatim so a future λ edit here
45
- // cannot silently drift from the pair it must match.
46
-
47
- import { SphereEmitter, Tunnel, Wall } from '@descix/frqtl-web/parts';
48
-
49
- // Two Slits — reproduces dse-two-emitters' picture with the two point sources built
50
- // as SLIT EXITS in a barrier instead of two free emitters. Barrier = Wall segments
51
- // (destroy); each slit = a directed Tunnel with the exit gate collapsed + phase-reset
52
- // (the dse-tunnel-rebuild composition) so the mouth is a phase-locked point source.
53
- const CW = 600, H = 400; // THE WORLD this scene states (its co-init call below)
54
- // ── THE ONLY LENGTH — IDENTICAL to the canonical-leg pair ───────────────
55
- const lamPx = 320;
56
- const lamArg = 3.2; // fullness 1/3.2 = 0.3125 -> yellow
57
- const fwl = 4 * (lamPx / lamArg); // 400 -> wavelengthScale floor(400/4) = 100
58
- const wlScale = Math.floor(fwl / 4); // 100
59
- // ── SLIT SEPARATION — IDENTICAL to dse-two-emitters' source separation ──
60
- const legFrac = 1 / 4;
61
- const D = Math.round(lamPx * legFrac); // 80 — the SAME leg, now measured from the
62
- // slit EXIT plane, this apparatus's source plane
63
- const sep = lamPx / 2; // 160 -> slit centres at y 120 and 280 — the
64
- // EXACT y positions dse-two-emitters used
65
- const cy = Math.round(H / 2); // 200
66
- const slitCY1 = cy - sep / 2, slitCY2 = cy + sep / 2; // 120, 280
67
- // ── THE BARRIER — one opaque plate, two slit apertures ──────────────────
68
- const barrierW = Math.round(lamPx / 8); // 40 — plate thickness (dimensionless x lamPx,
69
- // an independent dial from the transverse
70
- // wavelength, same discipline as dse-tunnel-rebuild)
71
- const slitH = Math.round(lamPx / 8); // 40 — slit aperture height (before liners)
72
- const linerH = Math.round(lamPx / 64); // 5 — liner thickness (unjumpable: max step 2px/tick)
73
- const bore = slitH - 2 * linerH; // 30 — the clear aperture between the liners
74
- const srcX = Math.round(lamPx / 16); // 20 — the emitter, well behind the barrier
75
- const barrierL = srcX + Math.round(lamPx / 2); // 180 — half a wavelength of travel room
76
- const slit1Top = slitCY1 - slitH / 2, slit2Top = slitCY2 - slitH / 2; // 100, 260
77
- const exitX = barrierL + barrierW; // 220 — the slit EXIT plane (barrier back face)
78
- const detX = exitX + D; // 300 — SAME leg as dse-two-emitters, now
79
- // measured from the slit exits
80
- const detW = Math.round(lamPx / 64); // 5 — plate thickness (flux probe, sub-feature)
81
- const sinkW = Math.round(lamPx / 16); // 20 — far-edge sink thickness
82
-
83
- export const parts = {
84
- source: [SphereEmitter, {
85
- preset: 'hemisphere', is2D: true, chargeMode: 'random', center: [srcX, cy],
86
- wavelength: lamArg, fundamentalDimension: 2, fundamentalWaveLength: fwl,
87
- burstSize: 8, emitEveryTicks: 1
88
- }],
89
- // Barrier segments — opaque destroy sinks, leaving the two slit gaps.
90
- wall1: [Wall, { rect: [barrierL, 0, barrierW, slit1Top],
91
- collisionBehavior: 'destroy', color: [200, 60, 60] }],
92
- wall2: [Wall, { rect: [barrierL, slit1Top + slitH, barrierW, slit2Top - (slit1Top + slitH)],
93
- collisionBehavior: 'destroy', color: [150, 40, 40] }],
94
- wall3: [Wall, { rect: [barrierL, slit2Top + slitH, barrierW, H - (slit2Top + slitH)],
95
- collisionBehavior: 'destroy', color: [200, 60, 60] }],
96
- // The two slit channels — directed Tunnels, exit gate COLLAPSED + PHASE-RESET (the
97
- // dse-tunnel-rebuild composition): each channel's own mouth stands in for a point
98
- // source at the slit's own y, phase-locked — what a SphereEmitter would do at that pixel.
99
- slit1_channel: [Tunnel, {
100
- left: barrierL, top: slit1Top + linerH, length: barrierW, bore: bore,
101
- wallThickness: linerH, axis: 'x', mode: 'directed',
102
- exitCollapse: true, exitRecenter: true, exitPhaseReset: true,
103
- color: [80, 140, 220]
104
- }],
105
- slit2_channel: [Tunnel, {
106
- left: barrierL, top: slit2Top + linerH, length: barrierW, bore: bore,
107
- wallThickness: linerH, axis: 'x', mode: 'directed',
108
- exitCollapse: true, exitRecenter: true, exitPhaseReset: true,
109
- color: [80, 140, 220]
110
- }],
111
- // FAR-EDGE SINK — the same experiment-boundary sink the canonical-leg pair uses.
112
- far_sink: [Wall, { rect: [CW - sinkW, 0, sinkW, H], collisionBehavior: 'destroy',
113
- color: [120, 120, 120], alpha: 40 }],
114
- // ── THE PLATE — SAME instrument as the canonical-leg pair: a counting wall that does
115
- // NOT absorb, at the SAME leg D past the new source plane (the slit exits).
116
- screen: [Wall, { rect: [detX, 0, detW, H],
117
- preset: 'transparent-probe', markTag: 'plate', color: [120, 200, 120] }],
118
- };
119
-
120
- export default function buildExperiment(exp) {
121
- if (Math.abs(lamArg * wlScale - lamPx) > 1e-9) {
122
- throw new Error('[dse-two-slits] lamPx ' + lamPx + ' is not realisable as lambda_eff: the ' +
123
- 'engine can only produce wavelengthArg x floor(fwl/4) = ' + (lamArg * wlScale) + '. (FAIL-LOUD.)');
124
- }
125
- // ── THE LIGHT SOURCE — ONE emitter, behind the barrier, lighting BOTH slits ──
126
- // preset 'hemisphere': 180 degrees of FORWARD emission, aimed at the barrier — the
127
- // same walk-character (chargeMode/wavelength/fundamentalWaveLength) the slit exits must
128
- // carry through so the exit's phase-locked point source is at the SAME λ_eff.
129
- exp.createCanvas(CW, H, SphereEmitter.worldCfg(parts.source[1])); // THE WORLD: a 2D canvas + universe — first, then parts
130
- const p = exp.declare(parts);
131
- exp.addObject('LiveChart', {
132
- source: p.screen, orientation: 90,
133
- rect: [detX + detW + 8, 0, Math.max(60, CW - (detX + detW + 8) - 8), H],
134
- series: 'line', label: 'count by y', refreshMs: 100, color: [24, 32, 46], alpha: 225
135
- });
136
- }
@@ -1,17 +0,0 @@
1
- /* Copyright 2023-2026 by Essam Abadir */
2
-
3
- /**
4
- * SCENE — Empty Universe
5
- *
6
- * STUDIO's blank world, a scene like every other: it states its world in its first (and only)
7
- * statement and constructs nothing — the palette authors into it. Named by
8
- * lib/shim/registry/scene-source.js::EMPTY_SCENE (not a walk-through rung).
9
- */
10
-
11
- export const parts = {};
12
-
13
- export default function buildExperiment(exp) {
14
- // THE WORLD: a 600x400 2D canvas + universe on the dim-2 fundamental, λ multiplier 8.
15
- exp.createCanvas(600, 400, { dimension: 2, wavelengthScale: 8 });
16
- exp.declare(parts);
17
- }
@@ -1,56 +0,0 @@
1
- /* Copyright 2023-2026 by Essam Abadir */
2
-
3
- /**
4
- * SCENE — 4B · Laser Build-Up
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 (`export const parts` + `exp.declare`, or `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 r4b-laser-buildup).
11
- */
12
-
13
- // LASER BUILD-UP (D4) — the three
14
- // components of a laser assembled one at a time on top of the earlier Light Lab rungs:
15
- // 1. a GAIN SOURCE — the hemisphere emitter (rung 3's omni source, restricted forward)
16
- // 2. a RESONANT BORE — the directed corridor, which re-aims every wall hit forward
17
- // 3. an OUTPUT COUPLER — the exit gate, optionally collapsing the mouth to a point and
18
- // optionally re-phase-locking what leaves it
19
- // The two exit flags are INDEPENDENT and both live, which is what lets the rung separate
20
- // "does the point source matter?" from "does coherence matter?".
21
- //
22
- // SINGLE AUTHORITY: the emitter + tunnel configs come from `setupTunnelPointExit.sceneSpec()`
23
- // — the SAME pure function the standalone `tunnel-point-exit` page's factory consumes. The
24
- // rung is the addObject form (so STUDIO can select and edit the objects); the page is the
25
- // slider-driven form. They share the numbers rather than each hand-carrying a copy.
26
-
27
- import { SphereEmitter, Tunnel } from '@descix/frqtl-web/parts';
28
-
29
- // Laser Build-Up — gain source + resonant bore + output coupler.
30
- // Geometry and per-leaf creation conditions come from the ONE scene-spec authority,
31
- // Tunnel.pointExitSpec() (setupTunnelPointExit.sceneSpec), so this rung and the standalone page cannot drift.
32
- const W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
33
- const SPEC = Tunnel.pointExitSpec({
34
- canvasW: W, canvasH: H,
35
- tunnelLength: 240, // REBUILD-class: corridor geometry, re-authored not dragged
36
- wavelengthScale: 16, // eff lambda 256px — the self-interference golden's own regime
37
- exitRecenter: true, // output coupler collapses the mouth to a point
38
- exitPhaseReset: false // coherence OFF by default — turning it ON is the experiment
39
- });
40
- // The spec names its tunnel; in `parts` the key is the name.
41
- const { name: _tunnelName, ...TUNNEL } = SPEC.tunnel;
42
-
43
- export const parts = {
44
- // 1. THE GAIN SOURCE — a hemisphere emitter just inside the input mouth, so the whole
45
- // burst is aimed down the bore instead of half of it escaping backwards.
46
- emitter: [SphereEmitter, { ...SPEC.emitter }],
47
- // 2+3. THE BORE AND THE OUTPUT COUPLER — one directed Tunnel carrying the exit gate.
48
- // mode 'directed': every wall hit re-emits forward within 180 degrees of the axis.
49
- // exitRecenter / exitPhaseReset are the two coupler flags, both live in the Inspector.
50
- point_exit_tunnel: [Tunnel, { ...TUNNEL }],
51
- };
52
-
53
- export default function buildExperiment(exp) {
54
- exp.createCanvas(W, H, SphereEmitter.worldCfg(parts.emitter[1])); // THE WORLD: a 2D canvas + universe — first, then parts
55
- exp.declare(parts);
56
- }
@@ -1,60 +0,0 @@
1
- /* Copyright 2023-2026 by Essam Abadir */
2
-
3
- /**
4
- * SCENE — 1A · Mirror (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 (`export const parts` + `exp.declare`, or `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 cl1a-mirror-3d).
11
- */
12
-
13
- import { SphereEmitter, Wall } from '@descix/frqtl-web/parts';
14
-
15
- // Collision Lab 1A · 3D — a collimated LASER (particle-walk pins PURE +x in 2D AND
16
- // 3D) into a 3D mirror SLAB (depth → z-slab containing z=0), rendered as a box;
17
- // orbit to view. Same LASER tuning as the 2D 1A: single positive
18
- // charge, wavelength 1, wavelengthScale 1 (fundamentalWaveLength 4), tiny burst.
19
- // FACE-ON default: orientation 0 presents the full
20
- // drawn face to the beam → retro-reflection (beam straight back, recolored) — the
21
- // one mirror visual that reads correctly from EVERY camera azimuth. (A tilted
22
- // default's L-shaped 2th exit is correct optics but its mirror-flipped projection
23
- // reads as pass-through from presets that view the xy-plane from behind — measured:
24
- // zero leaves ever behind the face; the read was projection handedness, not physics.)
25
- // Rotate the orientation slider to steer: 45 -> straight up, 30 -> R(120), etc.
26
- const W = 600, H = 400; // THE WORLD this scene states (its co-init call below)
27
- const cy = Math.round(H / 2);
28
-
29
- export const parts = {
30
- source: [SphereEmitter, {
31
- preset: 'particle-walk', is2D: false, center: [Math.round(W * 0.15), cy],
32
- chargeMode: 'positive', fundamentalDimension: 2, wavelength: 1,
33
- fundamentalWaveLength: 4, burstSize: 2, emitEveryTicks: 1
34
- }],
35
- mirror: [Wall, {
36
- rect: [Math.round(W * 0.6), Math.round(H * 0.2), 20, Math.round(H * 0.6)],
37
- z: -80, depth: 160,
38
- collisionBehavior: 'bounce', bounceDirection: 'reflect', orientationDeg: 0,
39
- color: [255, 120, 0], hooks: ['recolor']
40
- }],
41
- // SCENE Z-COLLIMATOR (without it the z lets things pass through instead of
42
- // reflecting): the beam's z-tail DIFFUSES over time (measured:
43
- // |z| at the wall trending 68 of the 80 rim within seconds), and any quantum
44
- // beyond the plate's z-band would sail BESIDE the plate — reading as pass-
45
- // through from rooftop. Two INVISIBLE full-canvas destroy boundaries flush with
46
- // the plate's z-band clamp the scene to the mirror's face: the universe z-edge
47
- // moved inward (the sanctioned experiment-boundary sink, same as 2D strays
48
- // dying at the canvas edge) — so the mirror provably faces EVERY alive quantum
49
- // and nothing can ever appear past the plate. 40px deep: un-jumpable (max z
50
- // step is ±2/tick).
51
- zClipFar: [Wall, { rect: [0, 0, W, H], z: 80, depth: 40,
52
- collisionBehavior: 'destroy', visible: false }],
53
- zClipNear: [Wall, { rect: [0, 0, W, H], z: -120, depth: 40,
54
- collisionBehavior: 'destroy', visible: false }],
55
- };
56
-
57
- export default function buildExperiment(exp) {
58
- exp.createScene(W, H, 600, SphereEmitter.worldCfg(parts.source[1])); // THE WORLD: a 3D scene + universe, 600x400x600 — first, then parts
59
- exp.declare(parts);
60
- }