@descix/frqtl-node 0.6.1-p3.1 → 0.6.1-p3.3

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.
@@ -258,7 +258,9 @@ export var WALKTHROUGH = [
258
258
  'tall/thin mirrors sit at the vertices of a regular decagon; each mirror\'s tilt is ' +
259
259
  'solved from its incoming/outgoing chords so the reflected centre-line hits the next ' +
260
260
  'mirror exactly, and by the star\'s symmetry the tenth leg feeds straight back into ' +
261
- 'the first — the polygon closes on itself. Each mirror is a DIFFERENT colour and, on ' +
261
+ 'the first — the polygon closes on itself. The scene OPENS with two mirrors (m4, m6) standing ' +
262
+ 'off their vertices, so the ring starts broken and the light spills: drag one home with the ' +
263
+ 'Hand and watch the field reshape live. Each mirror is a DIFFERENT colour and, on ' +
262
264
  'collision, TAGS the beam with its own colour (its drawn colour = its identity): so a ' +
263
265
  'particle\'s colour history IS its bounce sequence — red after m1, orange after m2, … ' +
264
266
  'magenta after m10, then red again as it re-enters the loop (a pkg-node colour-transition ' +
@@ -280,19 +282,26 @@ export var WALKTHROUGH = [
280
282
  'plateaus and FPS stays healthy while the circuit runs indefinitely — and the gate ' +
281
283
  'survives Restart (it is captured descriptor DATA, not recipe code).',
282
284
  source: 'scenes/mirror-route.js',
283
- // 3D FIRST = the rung's DEFAULT mode (the first declared entry). It opens on the
284
- // 'table-top' camera preset (three-camera-contract.js cameraPresets) with god-rays on at
285
- // the sun's settings (SunLook.SUN_GODRAYS, owned by lib/shim/render/sun-look.js), computed
286
- // from the light particles only (godraySource 'leaf'), so the mirrors shadow the beam and
287
- // cast no rays.
285
+ // 3D FIRST = the rung's DEFAULT mode (the first declared entry), and this rung is FIRST
286
+ // CONTACT (FIRST_CONTACT_WALKTHROUGH_ID below). It opens face-on to the ring (the
287
+ // 'table-top' preset's axes: azimuth 0, elevation 0, roll 0) but closer than that preset's
288
+ // 780, at distance 440, so the top and bottom slabs sit near the canvas's top and bottom
289
+ // edges. The look is the one a reference STUDIO session exported for this scene: the
290
+ // particles hidden (renderBaseCanvas false), so the picture is the light field alone — strong
291
+ // 'field' god rays computed from the light particles only (godraySource 'leaf'), so the
292
+ // mirrors shadow the beam and cast no rays — plus a soft glow. Every other render option
293
+ // that session exported sat at the renderer's own default, so it is not restated here.
288
294
  // The 2D mode stays one click away on the MODE strip.
289
295
  modes: [
290
296
  {
291
297
  id: '3d', label: '3D',
292
298
  renderState: {
293
299
  renderer: 'three',
294
- camera: { orbit: null, preset: 'table-top' },
295
- render: renderRowsFromBag(Object.assign({}, SunLook.SUN_GODRAYS, { godraySource: 'leaf' }))
300
+ camera: { orbit: { azimuth: 0, elevation: 0, distance: 440, roll: 0 }, preset: null },
301
+ render: renderRowsFromBag({
302
+ renderBaseCanvas: false, godrays: 3, godrayDensity: 1.085,
303
+ godraySource: 'leaf', godrayMode: 'field', glowIntensity: 0.27
304
+ })
296
305
  }
297
306
  },
298
307
  { id: '2d', label: '2D' }
@@ -375,89 +384,26 @@ export var WALKTHROUGH = [
375
384
  renderState: {
376
385
  render: [{ key: 'colorScheme', value: 'roygbiv' }]
377
386
  },
378
- // 3D FIRST = the rung's DEFAULT mode (the first declared entry), because this
379
- // rung is FIRST CONTACT: STUDIO opens on it, and the thing a newcomer should
380
- // meet is the experiment standing up in space with a camera they can move.
381
- // The 2D mode stays one click away on the MODE strip.
387
+ // 3D FIRST = the rung's DEFAULT mode (the first declared entry): the experiment
388
+ // standing up in space with a camera you can move. The 2D mode stays one click away
389
+ // on the MODE strip.
382
390
  modes: [
383
391
  {
384
392
  id: '3d', label: '3D',
385
- // FIRST-CONTACT FRAMING: the camera stands BEHIND the emitter, facing the
386
- // slit wall slightly rotated, so the interference builds up partially
387
- // visible and invites the viewer to move the camera.
388
- //
389
- // DERIVED FROM THE SCENE, not tuned against a picture. The camera sits at
390
- // target + d·(cos el·sin az, sin el, cos el·cos az)
391
- // looking back at `target` (three-provider `_applyOrbit`, world y-DOWN).
392
- // target (138, 200, 0) — the midpoint of the WORKING LENGTH (emitter
393
- // x=20 → detector far face x=256) at the beam height
394
- // (H/2): (20 + 256)/2 = 138. It is DERIVED from the
395
- // geometry, so it moves whenever the detector does. The
396
- // default centre is the CANVAS centre x=300, which sits
397
- // PAST the detector: orbiting about it swings the
398
- // experiment around a point outside itself and pushes
399
- // the near objects into the lens. The experiment orbits
400
- // about the experiment.
401
- // azimuth -60 → sin az = -0.866 ⇒ camera x = 138 - 0.82·d ≈ -257 at
402
- // d = 480: BEHIND the emitter, looking down +x through the
403
- // slit wall (x = 96..160) toward the detector (x = 236).
404
- // cos az = 0.5 swings it 30° off that axis in z — the
405
- // "slightly rotated" part: dead-on, the slit wall would
406
- // eclipse the detector completely; at 30° the detector's
407
- // own hit map shows PAST the barrier edge, partially, which
408
- // is exactly the invitation to orbit.
409
- // elevation -18 → sin el < 0 ⇒ a smaller world y = ABOVE the beam plane
410
- // (y-down): a gentle look down onto it, so the slit plate
411
- // and the tunnels read as solids rather than edge-on lines.
412
- // distance 480 → the full 256-px working length and the 400-px canvas
413
- // height inside the fixed 50° vertical FOV, with the
414
- // quanta streaming through the slits still legible. It
415
- // frames the working length, so it is re-derived with the
416
- // geometry — and by MEASUREMENT (project the scene's
417
- // bounding box through the camera basis and read the frame
418
- // fill: 480 gives max|ndc| 0.851), never by scaling the
419
- // span, which over-corrects because the working length runs
420
- // mostly ALONG the view direction and foreshortens.
421
- // roll +90 → the SLIT-SEPARATION AXIS onto the screen HORIZONTAL.
422
- // Derived from the scene like the rest, and forced by a
423
- // constraint 2D imposes on 3D: 2D has only x and y, so with
424
- // the beam on x the ONLY transverse axis available is y —
425
- // the slits MUST split along y and the fringes MUST spread
426
- // along y. 3D reuses that one setup (2D≡3D is this rung's
427
- // headline), so it inherits a floor-to-ceiling wall with the
428
- // detector eclipsed behind it. No orbit parameter can fix
429
- // that: `elevation` moves the eye along ±y, which is the
430
- // slit-separation axis itself, so "getting above it" slides
431
- // the eye ALONG the pattern instead of over the wall. ROLL is
432
- // the missing degree of freedom — it turns the VIEWER about
433
- // the beam axis, leaving the world untouched, and lands world
434
- // y (fringe spread) on the screen horizontal and world z (the
435
- // corridor extrusion) on the vertical: a WIDE, low plate with
436
- // the two slots SIDE BY SIDE and the fringes reading as bands
437
- // left-to-right — the textbook DSE picture. The plate's
438
- // on-screen bounding box goes 182×648 (tall) → 648×182 (wide);
439
- // the framing is otherwise IDENTICAL, since a roll moves
440
- // neither the eye nor the radius.
441
- // THE SIGN is the visually approved one. Measured through
442
- // `_orbitCameraBasis` at this exact pose (detector plate
443
- // corners, 600×600): at roll +90 the low-y corner projects to
444
- // screen-x 20.9 px and the high-y corner to 633.0 px (+y →
445
- // RIGHT); at roll −90 they land at 579.1 px and −33.0 px
446
- // (+y → LEFT). Both are equally "wide".
393
+ // THE DSE'S OPENING FRAMING, a choice about what to show first, fitted to a reference STUDIO
394
+ // picture: close in, rolled so the barrier runs from upper left down to lower right
395
+ // across the canvas with its two slits near the centre, the detector plate above and
396
+ // behind it showing its bands and running off the top edge, and the emitter's
397
+ // god-ray fan spreading from the lower left. The orbit revolves about
398
+ // `target`, the midpoint of the working length (emitter x=20 to the detector's
399
+ // far face) at the beam height H/2, so it moves whenever the detector does.
447
400
  // preset: null — deliberately NOT a named preset. `setPreset` is a FULL
448
- // framing reset: it forces `target: null` (and now `roll: 0`),
449
- // which is exactly what makes the preset buttons the reliable
450
- // way back — and exactly why they cannot express this pose,
451
- // whose whole point is the scene-derived `target` above.
452
- // NOT a compensator: the former azimuth:38 papered over a renderer defect
453
- // (a ferried canvas drawn with a y-up UV assumption in the y-down world,
454
- // on a single-sided plane), fixed at the root — orientation is derived in
455
- // world space and both broad faces carry the map, so every orbit is now
456
- // equally correct. This pose is a CHOICE about what to show first.
401
+ // framing reset (it forces `target: null` and `roll: 0`), so the
402
+ // preset buttons are the way back and cannot express this pose.
457
403
  renderState: {
458
404
  renderer: 'three',
459
405
  camera: {
460
- orbit: { azimuth: -60, elevation: -18, distance: 546, roll: 90,
406
+ orbit: { azimuth: -26, elevation: -18, distance: 440, roll: -115,
461
407
  target: { x: 177.5, y: 200, z: 0 } },
462
408
  preset: null
463
409
  }
@@ -737,15 +683,17 @@ export function effectiveRenderState(rung, modeId) {
737
683
  * than silently landing on a blank canvas.
738
684
  *
739
685
  * ── Layer 2: what it shows ───────────────────────────────────────────────────
740
- * The double-slit rebuilt on the Tunnel object, in its DEFAULT (3D) mode: the
741
- * camera stands behind the emitter looking obliquely at the slit wall, and the
742
- * interference pattern builds on the detector plate while you watch. This is
743
- * PRODUCT INTENT — the first thing FRQTL shows is its signature experiment, framed
744
- * so the pattern is already forming and the camera is obviously yours to move.
686
+ * The infinite mirror (the ten-mirror {10/3} star) in its DEFAULT (3D) mode, face-on:
687
+ * the light field itself, shadowed and reflected live by the mirrors, with two
688
+ * mirrors standing off their star positions so the first thing to try is dragging
689
+ * one home with the Hand. PRODUCT INTENT — the first thing FRQTL shows is light
690
+ * doing what light does, computed live, with objects that are obviously yours to move.
691
+ * The showcase roster's first tile (exhibits.js EXHIBITS[0]) is this rung too; its
692
+ * test holds the two equal.
745
693
  *
746
694
  * @type {string}
747
695
  */
748
- export var FIRST_CONTACT_WALKTHROUGH_ID = 'dse-tunnel-rebuild';
696
+ export var FIRST_CONTACT_WALKTHROUGH_ID = 'cl1d-mirror-route';
749
697
 
750
698
  /**
751
699
  * walkthroughById(id) — id → rung, or null. The ONE resolver every consumer of a
@@ -175,6 +175,26 @@ function bytesToBase64(bytes) {
175
175
  return btoa(bin);
176
176
  }
177
177
 
178
+ /**
179
+ * base64ToBytes(b64) — the inverse of `bytesToBase64`: an envelope's `encoded.bytes` back to the bytes a
180
+ * consumer saves or plays (a page turning a recording into a file). One encoding, one owner of both directions.
181
+ *
182
+ * @param {string} b64
183
+ * @returns {Uint8Array}
184
+ */
185
+ function base64ToBytes(b64) {
186
+ if (typeof b64 !== 'string') {
187
+ _fail('base64ToBytes expects the base64 string of encoded.bytes, got ' + typeof b64);
188
+ }
189
+ if (typeof atob !== 'function') {
190
+ _fail('base64ToBytes needs a global atob (every browser, and node >= 16)');
191
+ }
192
+ var bin = atob(b64);
193
+ var out = new Uint8Array(bin.length);
194
+ for (var i = 0; i < bin.length; i++) { out[i] = bin.charCodeAt(i); }
195
+ return out;
196
+ }
197
+
178
198
  /**
179
199
  * Compose a single-capture envelope.
180
200
  *
@@ -274,6 +294,7 @@ export {
274
294
  ENCODED_KEYS,
275
295
  ENCODERS,
276
296
  bytesToBase64,
297
+ base64ToBytes,
277
298
  stillEnvelope,
278
299
  videoEnvelope
279
300
  };
@@ -5,29 +5,24 @@
5
5
  *
6
6
  * The values are the FINAL FX bag EXACTLY as played and captured in an export session
7
7
  * (new_sun.png is the primary oracle; sun.png stays the historical dense reference), verbatim from
8
- * the exported renderState. They are in the write door's shape — a BAG, what
8
+ * the exported renderState, plus `godrayMode: 'radial'`, the mode chosen for the sun once the modes existed. They are in the write door's shape — a BAG, what
9
9
  * `runner.setRenderOptions(bag)` takes — and every key is a capability the three renderer declares.
10
10
  *
11
11
  * SunLook.SUN_RENDER — the sun's whole look (rung r8-sun's render rows).
12
- * SunLook.SUN_GODRAYS — the god-ray pair, PICKED from SUN_RENDER: what another scene lights up
13
- * "at the sun's settings" with (the mirror's 3D mode, the 3D DSE's switch).
14
12
  *
15
- * `godraySource` is NOT here: what the rays are computed from is each borrower's declared choice.
13
+ * `godrayMode` 'radial' is the sun's own (its rays fan from one light point, the source frames' centroid).
16
14
  * The camera is NOT here: a pose is a rung row's view, not a look.
17
15
  *
18
- * Readers: `lib/shim/registry/walkthrough.js` (a named import) and a scene sketch (`SunLook`, the
19
- * bridge's publication of this module until the game layer imports it). Zero physics: render
16
+ * Readers: `lib/shim/registry/walkthrough.js` (a named import); a scene sketch may import it too
17
+ * (`@descix/frqtl-web/shim/render/sun-look.js`). Zero physics: render
20
18
  * options are read only by the renderer.
21
19
  */
22
20
 
23
21
 
24
22
  var SUN_RENDER = Object.freeze({
25
23
  shape: 'sphere', colorScheme: 'warmcool',
26
- godrays: 1.08, godrayDensity: 1.085,
24
+ godrays: 1.08, godrayDensity: 1.085, godrayMode: 'radial',
27
25
  glowIntensity: 0.93, heat: 0.35, smoke: 0
28
26
  });
29
27
 
30
- // The pair other scenes light up "at the sun's settings" with — PICKED from SUN_RENDER, never re-typed.
31
- var SUN_GODRAYS = Object.freeze({ godrays: SUN_RENDER.godrays, godrayDensity: SUN_RENDER.godrayDensity });
32
-
33
- export { SUN_RENDER, SUN_GODRAYS };
28
+ export { SUN_RENDER };
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # @descix/frqtl-node
2
2
 
3
3
  > The FRQTL physics engine (compiled Rust → WASM) for node: the same modules as @descix/frqtl-web, plus
4
- > `bootNode` and the hosted eyes. 0.6.1-p3.1 on npm dist-tag `next`. Node ≥ 22.12.
4
+ > `bootNode` and the hosted eyes. 0.6.1-p3.3 on npm dist-tag `next`. Node ≥ 22.12.
5
5
 
6
6
  - Install: `npm install @descix/frqtl-node`
7
7
  - `import { bootNode, Wall } from '@descix/frqtl-node'` (require() works too); `await bootNode()` → the engine handle.
@@ -9,5 +9,5 @@
9
9
  - `./project`: `linkProject` links a scene file against this package's own copy.
10
10
  - `./host-view`: `declareHostView`, `HOST_VIEW_OUTCOMES`, `CONTRACT_VIOLATIONS`, `HOST_SDK_SCRIPT` — the same declarer as @descix/frqtl-web/host-view, pure, for a node test of a call site.
11
11
  - `./register`: `node --import @descix/frqtl-node/register scene.mjs` resolves a scene's `@descix/frqtl-web/...` imports to this package.
12
- - Full agent guide: https://cdn.jsdelivr.net/npm/@descix/frqtl-node@0.6.1-p3.1/CLAUDE.md
12
+ - Full agent guide: https://cdn.jsdelivr.net/npm/@descix/frqtl-node@0.6.1-p3.3/CLAUDE.md
13
13
  - Acceptance is visual; no CLI ships. No account, credits or tokens are needed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descix/frqtl-node",
3
- "version": "0.6.1-p3.1",
3
+ "version": "0.6.1-p3.3",
4
4
  "description": "FRQTL for node \u2014 the discrete fractal physics engine (compiled Rust \u2192 WASM), its parts as ES modules, the node engine door (bootNode) and the hosted eyes.",
5
5
  "type": "module",
6
6
  "engines": {