betterwithage commited on
Commit
b529805
·
verified ·
1 Parent(s): 460a167

chore(sync): mirror static/3d kit + Dockerfile-COPY'd web/*.html to Space (hf-sync)

Browse files

Automated 3D/web-page asset sync from szl-holdings/a11oy main via hf-sync.
web/*.html set parsed live from the Dockerfile COPY directives.
Added/updated: 52 file(s)
Deleted (gone from GitHub main): (none)

Keeps static/3d/** and every Dockerfile-COPY'd web/<page>.html identical
to GitHub main so an HF factory rebuild never drops a GitHub edit (closes
the chronic hf-module-drift red for the 3D kit + baked web pages).

Files changed (1) hide show
  1. static/3d/surfaces/pinn.js +964 -52
static/3d/surfaces/pinn.js CHANGED
@@ -1,77 +1,989 @@
1
  // SPDX-License-Identifier: Apache-2.0
2
- // © 2026 Lutar, Stephen P. Jr. — SZL Holdings · Doctrine v11
3
  //
4
- // surfaces/pinn.js — PINN Thermal/Field surface STUB (Dev0 foundation; Dev6 fills this).
5
  //
6
- // Leader/technique to model: Kitware VTK.js + TSL compute
7
- // Primary live endpoint (doctrine: WIRE TO LIVE DATA, never fabricate): /api/a11oy/v1/pnt/limits
 
 
 
 
 
8
  //
9
- // CONTRACT every surface module is an ES module default-exporting:
10
- // { id, title, endpoints[], mount(ctx), unmount() }
11
- // ctx (provided by the /holographic shell via szl3d):
12
- // ctx.stage Stage from szl3d_boot.boot() scene, camera, renderer, THREE,
13
- // start, stop, onFrame, setBloom, backend, ...
14
- // ctx.container the surface's DOM panel (for overlays/badges/HUD)
15
- // ctx.live the szl3d_live module (ctx.live.poll(endpoint, ms, onData, {badge}))
16
- // ctx.label the szl3d_label module (ctx.label.chip / .billboard / .legend)
17
- // ctx.THREE the three module
18
- // mount() attaches scene objects + starts polling. unmount() stops polls and removes
19
- // DOM it added (the shell disposes the stage).
 
20
  //
21
- // This STUB renders an honest "awaiting Dev6" placeholder + a LIVE badge wired to the
22
- // real endpoint, proving the toolkit primitives work end-to-end before the real viz lands.
 
 
 
 
 
 
23
 
24
  const ID = "pinn";
25
  const TITLE = "PINN Thermal/Field";
26
- const ENDPOINT = "/api/a11oy/v1/pnt/limits";
27
- const ACCENT = 0xff9d5c;
28
 
29
- let _stage = null, _handle = null, _overlay = null, _obj = null;
 
 
30
 
31
- function mount(ctx) {
32
- _stage = ctx.stage;
33
- const THREE = ctx.THREE;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- const geo = new THREE.IcosahedronGeometry(2.4, 1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  const mat = new THREE.MeshStandardMaterial({
37
- color: ACCENT, emissive: ACCENT, emissiveIntensity: 0.35,
38
- metalness: 0.4, roughness: 0.35, wireframe: true,
 
39
  });
40
- _obj = new THREE.Mesh(geo, mat);
41
- _stage.scene.add(_obj);
42
- _stage.onFrame(() => { if (_obj) { _obj.rotation.y += 0.004; _obj.rotation.x += 0.0015; } });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  try {
45
- const bb = ctx.label.billboard(THREE, "STRUCTURAL-ONLY", { text: TITLE, scale: 0.7, position: [0, 3.4, 0] });
46
- _stage.scene.add(bb);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  } catch (_) {}
48
 
49
- _overlay = document.createElement("div");
50
- _overlay.className = "szl3d-surface-overlay";
51
- Object.assign(_overlay.style, { position: "absolute", left: "14px", top: "14px", zIndex: "5",
52
- display: "flex", flexDirection: "column", gap: "8px", maxWidth: "min(92%,420px)" });
53
- const h = document.createElement("div");
54
- h.style.cssText = "font:600 13px ui-sans-serif,system-ui;color:#eef3f6;letter-spacing:.4px";
55
- h.textContent = TITLE + " · awaiting Dev6";
56
- const badge = ctx.live.createBadge();
57
- const legend = ctx.label.legend();
58
- legend.style.opacity = "0.85";
59
- _overlay.appendChild(h); _overlay.appendChild(badge.el); _overlay.appendChild(legend);
60
- (ctx.container || document.body).appendChild(_overlay);
61
 
62
- _handle = ctx.live.poll(ENDPOINT, 5000, (json, meta) => {
63
- // Dev6: replace with the real viz update. Honesty label is in meta.label.
64
- if (meta.label && _obj) { /* map live values to geometry here */ }
65
- }, { badge });
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- return { id: ID, started: true };
 
 
 
 
 
68
  }
69
 
70
  function unmount() {
71
- try { if (_handle) _handle.stop(); } catch (_) {}
 
 
 
72
  try { if (_overlay && _overlay.parentNode) _overlay.parentNode.removeChild(_overlay); } catch (_) {}
73
- try { if (_obj && _stage) _stage.scene.remove(_obj); } catch (_) {}
74
- _handle = null; _overlay = null; _obj = null; _stage = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  }
76
 
77
- export default { id: ID, title: TITLE, endpoints: [ENDPOINT], mount, unmount };
 
 
 
 
 
 
 
 
 
1
  // SPDX-License-Identifier: Apache-2.0
2
+ // © 2026 Lutar, Stephen P. Jr. — SZL Holdings · ORCID 0009-0001-0110-4173 · Doctrine v11
3
  //
4
+ // surfaces/pinn.js — PINN Thermal/Field surface (Dev6).
5
  //
6
+ // Leader/technique modeled: Kitware VTK.js / VolView cinematic volume rendering +
7
+ // three.js TSL compute (frontier). Implemented here as a WebGPU-attempt / WebGL2-fallback
8
+ // GLSL ray-marched 3D scalar-field volume + GPU-instanced Gaussian-splat scalar field
9
+ // (novel holographic presentation per the viz-leaders research §6 + §8), an isosurface
10
+ // shell, a PDE-residual displacement heatmap, an instanced vector arrow field, and the
11
+ // compute_bounds physical-ceiling ladder (Landauer / Margolus-Levitin / Bremermann /
12
+ // Bekenstein) rendered straight from the MEASURED+SIGNED physical-bounds certificate.
13
  //
14
+ // DOCTRINE v11 HONESTY (load-bearing do not soften):
15
+ // * The certificate is MEASURED + SIGNED: avg_power_w / wall_time_s / temperature_k are
16
+ // real on-metal NVML samples (sovereign GPU betterwithage), the energy is DERIVED
17
+ // (P×t), the envelope is signed with a real Ed25519 DSSE signature (FA-001 on-metal),
18
+ // cosign.pub-anchored (ECDSA-P256), and anchored in the public Rekor transparency log.
19
+ // We render that proudly AND accurately — exact algs, keyids, Rekor log_index/uuid.
20
+ // * The rendered 3D field is a *visualization of the model* a deterministic analytic
21
+ // thermal/PDE field seeded by the MEASURED scalars. It is labelled MODELED, never
22
+ // MEASURED. If no cert value is available we fall to an explicitly-labelled SAMPLE
23
+ // field. We NEVER fabricate a field number or claim the rendered voxels are measured.
24
+ // * The agentic-PINN residual trail (/pinn/residual) is AWAITING_GPU_SOLVE in this
25
+ // environment — we render the honest AWAITING state, never a fabricated residual.
26
  //
27
+ // LIVE DATA (never hardcoded read via ctx.live.poll):
28
+ // /api/a11oy/v1/pinn/certificate MEASURED+SIGNED physical-bounds certificate (primary)
29
+ // /api/a11oy/v1/pnt/limits compute_bounds pillar (4-pillar fundamental-limits index)
30
+ // /api/a11oy/v1/pinn/residual governed agentic solve residual trail (AWAITING here)
31
+ //
32
+ // CONTRACT: default-export { id, title, endpoints[], mount(ctx), unmount() }.
33
+ // The shell shares ONE Stage across surfaces; we add objects to ctx.stage.scene, register
34
+ // per-frame work via ctx.stage.onFrame, and on unmount stop every poll + remove what we added.
35
 
36
  const ID = "pinn";
37
  const TITLE = "PINN Thermal/Field";
 
 
38
 
39
+ const EP_CERT = "/api/a11oy/v1/pinn/certificate";
40
+ const EP_LIMITS = "/api/a11oy/v1/pnt/limits";
41
+ const EP_RESIDUAL = "/api/a11oy/v1/pinn/residual";
42
 
43
+ // Volume sampling resolution for the GPU-built 3D scalar-field texture.
44
+ const VOX = 48;
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Module state (single active surface at a time per the shell contract).
48
+ // ---------------------------------------------------------------------------
49
+ let _ctx = null, _stage = null, _THREE = null;
50
+ let _group = null; // root group holding all scene objects we add
51
+ let _handles = []; // poll handles to stop on unmount
52
+ let _overlay = null; // DOM HUD
53
+ let _frameReg = null; // our onFrame closure (guards on null after unmount)
54
+ let _disposables = []; // geometries / materials / textures to dispose
55
+
56
+ // live cert-derived state (all MODELED-for-render, sourced from MEASURED scalars)
57
+ const F = {
58
+ haveCert: false,
59
+ certLabel: "SAMPLE", // honesty token for the FIELD render (MEASURED scalars → MODELED field)
60
+ signed: false,
61
+ // MEASURED scalars (from cert.measured.*_MEASURED) — null until live
62
+ tempK: null, powerW: null, wallS: null, energyJ: null,
63
+ // DERIVED bounds (from cert.*)
64
+ landauerMult: null, mlFrac: null, bremFrac: null, bekFrac: null, bounded: null,
65
+ // signatures / anchors
66
+ ed25519Keyid: null, cosignKeyid: null, cosignPubUrl: null, certSha: null,
67
+ rekorUuid: null, rekorIndex: null, rekorTime: null, rekorProvider: null,
68
+ khipuDigest: null,
69
+ // residual trail
70
+ residualState: "INIT", residualRounds: [],
71
+ // controls
72
+ isoThreshold: 0.55, splatOn: true, arrowsOn: true, residualDisp: 0.0,
73
+ backend: "…", t: 0,
74
+ };
75
+
76
+ // SAMPLE seed values — used ONLY for the field shape before the live cert arrives, and
77
+ // then ONLY labelled SAMPLE. These are illustrative, never presented as measurement.
78
+ const SAMPLE = { tempK: 320, powerW: 30, wallS: 60 };
79
+
80
+ // ---------------------------------------------------------------------------
81
+ // Honest accessor: the scalars that drive the field. Returns {tempK,powerW,label}.
82
+ // When the MEASURED cert is live → real scalars, field labelled MODELED.
83
+ // Before that → SAMPLE seed, field labelled SAMPLE. Never fabricates.
84
+ // ---------------------------------------------------------------------------
85
+ function _fieldScalars() {
86
+ if (F.haveCert && F.tempK != null) {
87
+ return { tempK: F.tempK, powerW: F.powerW != null ? F.powerW : SAMPLE.powerW, label: "MODELED" };
88
+ }
89
+ return { tempK: SAMPLE.tempK, powerW: SAMPLE.powerW, label: "SAMPLE" };
90
+ }
91
+
92
+ // ---------------------------------------------------------------------------
93
+ // Analytic PINN-style thermal field f(x,y,z) ∈ [0,1] — a deterministic heat-kernel
94
+ // surrogate (sum of Gaussian thermal sources + a steady diffusion gradient). This is
95
+ // the MODELED field the volume/isosurface/splats/arrows all read. Seeded by the
96
+ // MEASURED temperature so the hot core scales with real telemetry. Pure CPU mirror of
97
+ // the GLSL so isosurface + arrows + splats agree with the ray-march.
98
+ // ---------------------------------------------------------------------------
99
+ function _sampleField(x, y, z, hot) {
100
+ // x,y,z ∈ [-1,1]. hot ∈ ~[0.4,1.1] scales the central source from measured temp.
101
+ const r2 = x * x + y * y + z * z;
102
+ const core = Math.exp(-2.4 * r2) * (0.85 + 0.35 * hot);
103
+ // two offset thermal lobes (conduction toward edges)
104
+ const dx1 = x - 0.45, dy1 = y - 0.15;
105
+ const lobe1 = 0.45 * Math.exp(-5.0 * (dx1 * dx1 + dy1 * dy1 + z * z));
106
+ const dx2 = x + 0.4, dz2 = z + 0.35;
107
+ const lobe2 = 0.4 * Math.exp(-5.5 * (dx2 * dx2 + y * y + dz2 * dz2));
108
+ // a gentle diffusion gradient (cooler at +y, the "exhaust" direction)
109
+ const grad = 0.12 * (1.0 - (y + 1.0) * 0.5);
110
+ let v = core + lobe1 + lobe2 + grad;
111
+ return Math.max(0, Math.min(1, v));
112
+ }
113
+
114
+ // PDE residual surrogate r(x,y,z) ∈ [0,1] — large where the analytic field has high
115
+ // curvature (the steep flank of the hot core), which is exactly where a real PINN's
116
+ // physics-loss collocation would densify (RAR/RAD). MODELED bound, not measured.
117
+ function _sampleResidual(x, y, z, hot) {
118
+ const r2 = x * x + y * y + z * z;
119
+ const flank = Math.exp(-2.4 * r2) * r2 * 4.0; // peaks on the gradient flank
120
+ return Math.max(0, Math.min(1, flank * (0.7 + 0.5 * hot)));
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // Build the 3D scalar-field texture (Data3DTexture, R channel = scalar in [0,1]).
125
+ // On WebGPU/WebGL2 alike this is sampled by the ray-march material. Rebuilt when the
126
+ // measured temperature changes (rare), not per-frame.
127
+ // ---------------------------------------------------------------------------
128
+ function _buildVolumeTexture(hot) {
129
+ const THREE = _THREE;
130
+ const n = VOX, data = new Uint8Array(n * n * n);
131
+ let i = 0;
132
+ for (let zi = 0; zi < n; zi++) {
133
+ const z = (zi / (n - 1)) * 2 - 1;
134
+ for (let yi = 0; yi < n; yi++) {
135
+ const y = (yi / (n - 1)) * 2 - 1;
136
+ for (let xi = 0; xi < n; xi++) {
137
+ const x = (xi / (n - 1)) * 2 - 1;
138
+ data[i++] = Math.round(_sampleField(x, y, z, hot) * 255);
139
+ }
140
+ }
141
+ }
142
+ const tex = new THREE.Data3DTexture(data, n, n, n);
143
+ tex.format = THREE.RedFormat;
144
+ tex.type = THREE.UnsignedByteType;
145
+ tex.minFilter = THREE.LinearFilter;
146
+ tex.magFilter = THREE.LinearFilter;
147
+ tex.unpackAlignment = 1;
148
+ tex.needsUpdate = true;
149
+ return tex;
150
+ }
151
+
152
+ // ---------------------------------------------------------------------------
153
+ // GLSL ray-march volume material. Front-face cull off / back-face render of a unit
154
+ // cube; the fragment shader marches camera→fragment through the 3D texture and
155
+ // accumulates a temperature transfer function (blue→cyan→amber→white-hot). Works on
156
+ // WebGL2 (the Linux fallback) AND WebGPU (three compiles GLSL nodeless materials on
157
+ // the WebGL2 path; on a true WebGPU device the shell still renders via the same Mesh
158
+ // because we use ShaderMaterial which three's WebGPURenderer supports via its WGSL
159
+ // transpile for raw GLSL ShaderMaterial in r170's backend-compat path). The fallback
160
+ // is honest: if the device is WebGPU and ShaderMaterial is unsupported we still show
161
+ // the isosurface + splats, which use standard materials.
162
+ // ---------------------------------------------------------------------------
163
+ function _volumeMaterial(tex) {
164
+ const THREE = _THREE;
165
+ return new THREE.ShaderMaterial({
166
+ glslVersion: THREE.GLSL3,
167
+ transparent: true,
168
+ depthWrite: false,
169
+ side: THREE.BackSide,
170
+ uniforms: {
171
+ uVol: { value: tex },
172
+ uThreshold: { value: F.isoThreshold },
173
+ uSteps: { value: 96 },
174
+ uTime: { value: 0 },
175
+ uOpacity: { value: 0.92 },
176
+ uCamPos: { value: new THREE.Vector3() },
177
+ },
178
+ vertexShader: /* glsl */`
179
+ out vec3 vLocal;
180
+ void main(){
181
+ vLocal = position; // unit cube in [-0.5,0.5]
182
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
183
+ }
184
+ `,
185
+ fragmentShader: /* glsl */`
186
+ precision highp float;
187
+ precision highp sampler3D;
188
+ in vec3 vLocal;
189
+ out vec4 fragColor;
190
+ uniform sampler3D uVol;
191
+ uniform float uThreshold;
192
+ uniform int uSteps;
193
+ uniform float uTime;
194
+ uniform float uOpacity;
195
+ uniform vec3 uCamPos;
196
+
197
+ // temperature transfer function: cold blue -> cyan -> amber -> white-hot
198
+ vec3 tf(float t){
199
+ t = clamp(t,0.0,1.0);
200
+ vec3 cold = vec3(0.05,0.12,0.35);
201
+ vec3 mid = vec3(0.13,0.72,0.74);
202
+ vec3 warm = vec3(0.91,0.62,0.28);
203
+ vec3 hot = vec3(1.0,0.96,0.86);
204
+ vec3 c = mix(cold, mid, smoothstep(0.0,0.45,t));
205
+ c = mix(c, warm, smoothstep(0.4,0.75,t));
206
+ c = mix(c, hot, smoothstep(0.75,1.0,t));
207
+ return c;
208
+ }
209
+ // intersect ray with unit box [-0.5,0.5]^3
210
+ vec2 boxHit(vec3 ro, vec3 rd){
211
+ vec3 inv = 1.0/rd;
212
+ vec3 a = (vec3(-0.5)-ro)*inv;
213
+ vec3 b = (vec3( 0.5)-ro)*inv;
214
+ vec3 tmin = min(a,b), tmax = max(a,b);
215
+ float t0 = max(max(tmin.x,tmin.y),tmin.z);
216
+ float t1 = min(min(tmax.x,tmax.y),tmax.z);
217
+ return vec2(t0,t1);
218
+ }
219
+ void main(){
220
+ // ray in local cube space
221
+ vec3 ro = (inverse(modelMatrix) * vec4(uCamPos,1.0)).xyz;
222
+ vec3 rd = normalize(vLocal - ro);
223
+ vec2 hit = boxHit(ro, rd);
224
+ float t0 = max(hit.x, 0.0), t1 = hit.y;
225
+ if (t1 <= t0){ discard; }
226
+ int steps = uSteps;
227
+ float dt = (t1 - t0)/float(steps);
228
+ vec3 col = vec3(0.0);
229
+ float alpha = 0.0;
230
+ float t = t0 + dt*fract(sin(dot(vLocal.xy,vec2(12.9898,78.233)))*43758.5453); // jitter
231
+ for (int i=0;i<256;i++){
232
+ if (i>=steps || alpha>0.98) break;
233
+ vec3 p = ro + rd*t; // [-0.5,0.5]
234
+ vec3 uv = p + 0.5; // [0,1]
235
+ float s = texture(uVol, uv).r;
236
+ // emphasise voxels above the iso threshold; pulse subtly for the holographic feel
237
+ float w = smoothstep(uThreshold-0.12, uThreshold+0.04, s);
238
+ float dens = s*0.55 + w*0.85;
239
+ dens *= (0.85 + 0.15*sin(uTime*1.4 + s*8.0));
240
+ vec3 c = tf(s);
241
+ float a = dens * uOpacity * dt * 6.0;
242
+ a = clamp(a,0.0,1.0);
243
+ col += (1.0-alpha) * a * c;
244
+ alpha += (1.0-alpha) * a;
245
+ t += dt;
246
+ }
247
+ if (alpha < 0.003) discard;
248
+ fragColor = vec4(col, alpha);
249
+ }
250
+ `,
251
+ });
252
+ }
253
+
254
+ // ---------------------------------------------------------------------------
255
+ // Gaussian-splat scalar field (novel holographic) — one GPU-instanced additive
256
+ // billboard quad per high-scalar voxel; opacity + color = field value. This is the
257
+ // research §8 "Gaussian Splatting for scalar fields" technique applied to the MODELED
258
+ // PINN field. Built once (a fixed sparse voxel set above a low cutoff), recolored live.
259
+ // ---------------------------------------------------------------------------
260
+ function _buildSplats(hot) {
261
+ const THREE = _THREE;
262
+ const pts = [];
263
+ const n = 26; // coarse splat lattice
264
+ for (let zi = 0; zi < n; zi++) {
265
+ const z = (zi / (n - 1)) * 2 - 1;
266
+ for (let yi = 0; yi < n; yi++) {
267
+ const y = (yi / (n - 1)) * 2 - 1;
268
+ for (let xi = 0; xi < n; xi++) {
269
+ const x = (xi / (n - 1)) * 2 - 1;
270
+ const s = _sampleField(x, y, z, hot);
271
+ if (s > 0.34) pts.push([x, y, z, s]);
272
+ }
273
+ }
274
+ }
275
+ const count = pts.length;
276
+ const geo = new THREE.PlaneGeometry(1, 1);
277
+ const inst = new THREE.InstancedMesh(geo, _splatMaterial(), count);
278
+ const m = new THREE.Matrix4();
279
+ const col = new THREE.Color();
280
+ for (let k = 0; k < count; k++) {
281
+ const [x, y, z, s] = pts[k];
282
+ const sc = 0.10 + s * 0.42;
283
+ m.makeScale(sc, sc, sc);
284
+ m.setPosition(x * 2.0, y * 2.0, z * 2.0);
285
+ inst.setMatrixAt(k, m);
286
+ _tfColor(col, s);
287
+ inst.setColorAt(k, col);
288
+ }
289
+ inst.instanceMatrix.needsUpdate = true;
290
+ if (inst.instanceColor) inst.instanceColor.needsUpdate = true;
291
+ inst.userData.splatCount = count;
292
+ inst.userData.pts = pts;
293
+ _disposables.push(geo);
294
+ return inst;
295
+ }
296
+
297
+ function _splatMaterial() {
298
+ const THREE = _THREE;
299
+ // additive radial-gaussian sprite via a small canvas texture
300
+ const cnv = document.createElement("canvas");
301
+ cnv.width = cnv.height = 64;
302
+ const g = cnv.getContext("2d");
303
+ const grad = g.createRadialGradient(32, 32, 0, 32, 32, 32);
304
+ grad.addColorStop(0, "rgba(255,255,255,1)");
305
+ grad.addColorStop(0.4, "rgba(255,255,255,0.5)");
306
+ grad.addColorStop(1, "rgba(255,255,255,0)");
307
+ g.fillStyle = grad; g.fillRect(0, 0, 64, 64);
308
+ const tex = new THREE.CanvasTexture(cnv);
309
+ const mat = new THREE.MeshBasicMaterial({
310
+ map: tex, transparent: true, blending: THREE.AdditiveBlending,
311
+ depthWrite: false, vertexColors: true, opacity: 0.9,
312
+ });
313
+ _disposables.push(tex, mat);
314
+ return mat;
315
+ }
316
+
317
+ function _tfColor(col, t) {
318
+ // CPU mirror of the GLSL transfer function (cold→hot)
319
+ t = Math.max(0, Math.min(1, t));
320
+ const lerp = (a, b, k) => a + (b - a) * k;
321
+ const sm = (e0, e1, x) => { const k = Math.max(0, Math.min(1, (x - e0) / (e1 - e0))); return k * k * (3 - 2 * k); };
322
+ let r = lerp(0.05, 0.13, sm(0, 0.45, t)), gn = lerp(0.12, 0.72, sm(0, 0.45, t)), b = lerp(0.35, 0.74, sm(0, 0.45, t));
323
+ r = lerp(r, 0.91, sm(0.4, 0.75, t)); gn = lerp(gn, 0.62, sm(0.4, 0.75, t)); b = lerp(b, 0.28, sm(0.4, 0.75, t));
324
+ r = lerp(r, 1.0, sm(0.75, 1, t)); gn = lerp(gn, 0.96, sm(0.75, 1, t)); b = lerp(b, 0.86, sm(0.75, 1, t));
325
+ col.setRGB(r, gn, b);
326
+ return col;
327
+ }
328
+
329
+ // ---------------------------------------------------------------------------
330
+ // Isosurface shell — a marching-cubes-style threshold surface. We approximate it with
331
+ // an icosphere whose vertices are displaced to the radius where the field crosses the
332
+ // iso threshold along that direction (a star-shaped level-set, cheap + interactive).
333
+ // Recomputed when the slider moves. Real MC on the GPU is the TSL-compute TODO; this is
334
+ // the honest interactive fallback that runs on WebGL2 too.
335
+ // ---------------------------------------------------------------------------
336
+ function _buildIsosurface(hot, threshold) {
337
+ const THREE = _THREE;
338
+ const geo = new THREE.IcosahedronGeometry(1, 5);
339
+ const pos = geo.attributes.position;
340
+ const v = new THREE.Vector3();
341
+ const colors = new Float32Array(pos.count * 3);
342
+ const col = new THREE.Color();
343
+ for (let i = 0; i < pos.count; i++) {
344
+ v.fromBufferAttribute(pos, i).normalize();
345
+ // march outward to find where field == threshold along this ray
346
+ let rHit = 0.18;
347
+ for (let s = 0; s <= 64; s++) {
348
+ const r = 0.05 + (s / 64) * 1.4;
349
+ const f = _sampleField(v.x * r, v.y * r, v.z * r, hot);
350
+ if (f < threshold) { rHit = r; break; }
351
+ rHit = r;
352
+ }
353
+ const R = rHit * 2.0;
354
+ pos.setXYZ(i, v.x * R, v.y * R, v.z * R);
355
+ _tfColor(col, threshold);
356
+ colors[i * 3] = col.r; colors[i * 3 + 1] = col.g; colors[i * 3 + 2] = col.b;
357
+ }
358
+ geo.setAttribute("color", new THREE.BufferAttribute(colors, 3));
359
+ geo.computeVertexNormals();
360
+ pos.needsUpdate = true;
361
+ const mat = new THREE.MeshStandardMaterial({
362
+ vertexColors: true, transparent: true, opacity: 0.34,
363
+ metalness: 0.2, roughness: 0.4, emissive: 0x163040, emissiveIntensity: 0.5,
364
+ side: THREE.DoubleSide, wireframe: false,
365
+ });
366
+ const mesh = new THREE.Mesh(geo, mat);
367
+ mesh.userData.iso = true;
368
+ _disposables.push(geo, mat);
369
+ return mesh;
370
+ }
371
 
372
+ // ---------------------------------------------------------------------------
373
+ // Residual heatmap displacement shell — a second icosphere whose vertices protrude
374
+ // outward + turn orange/red where the MODELED PDE residual is high (research §6 step 4:
375
+ // "high residual = surface protrudes + turns orange/red"). residualDisp slider scales it.
376
+ // ---------------------------------------------------------------------------
377
+ function _buildResidualShell(hot) {
378
+ const THREE = _THREE;
379
+ const geo = new THREE.IcosahedronGeometry(2.6, 5);
380
+ const pos = geo.attributes.position;
381
+ const base = pos.array.slice();
382
+ const colors = new Float32Array(pos.count * 3);
383
+ const v = new THREE.Vector3(), col = new THREE.Color();
384
+ for (let i = 0; i < pos.count; i++) {
385
+ v.set(base[i * 3], base[i * 3 + 1], base[i * 3 + 2]);
386
+ const n = v.clone().normalize();
387
+ const res = _sampleResidual(n.x, n.y, n.z, hot);
388
+ // residual → red/orange ramp (separate from the temperature TF, so it reads as "error")
389
+ col.setRGB(0.2 + res * 0.8, 0.18 + res * 0.35, 0.1 + (1 - res) * 0.2);
390
+ colors[i * 3] = col.r; colors[i * 3 + 1] = col.g; colors[i * 3 + 2] = col.b;
391
+ }
392
+ geo.setAttribute("color", new THREE.BufferAttribute(colors, 3));
393
+ geo.userData.base = base; geo.userData.hot = hot;
394
+ geo.computeVertexNormals();
395
  const mat = new THREE.MeshStandardMaterial({
396
+ vertexColors: true, transparent: true, opacity: 0.0, // hidden until residualDisp>0
397
+ metalness: 0.1, roughness: 0.6, side: THREE.DoubleSide,
398
+ emissive: 0x401505, emissiveIntensity: 0.4, wireframe: true,
399
  });
400
+ const mesh = new THREE.Mesh(geo, mat);
401
+ mesh.userData.residual = true;
402
+ _disposables.push(geo, mat);
403
+ return mesh;
404
+ }
405
+
406
+ function _applyResidualDisp(mesh, disp) {
407
+ const geo = mesh.geometry;
408
+ const base = geo.userData.base, hot = geo.userData.hot;
409
+ const pos = geo.attributes.position;
410
+ const v = _THREE ? new _THREE.Vector3() : null;
411
+ for (let i = 0; i < pos.count; i++) {
412
+ const bx = base[i * 3], by = base[i * 3 + 1], bz = base[i * 3 + 2];
413
+ v.set(bx, by, bz); const len = v.length(); v.normalize();
414
+ const res = _sampleResidual(v.x, v.y, v.z, hot);
415
+ const R = len + res * disp * 1.4;
416
+ pos.setXYZ(i, v.x * R, v.y * R, v.z * R);
417
+ }
418
+ pos.needsUpdate = true;
419
+ geo.computeVertexNormals();
420
+ mesh.material.opacity = disp > 0.01 ? 0.55 : 0.0;
421
+ }
422
+
423
+ // ---------------------------------------------------------------------------
424
+ // Vector arrow field — instanced cones pointing along -∇field (heat-flow direction),
425
+ // length ∝ |∇field|. Research §6 step 5 "velocity as a vector arrow field, instanced
426
+ // ConeGeometry". MODELED gradient of the MODELED field.
427
+ // ---------------------------------------------------------------------------
428
+ function _buildArrows(hot) {
429
+ const THREE = _THREE;
430
+ const dirs = [];
431
+ const n = 7;
432
+ for (let zi = 0; zi < n; zi++) for (let yi = 0; yi < n; yi++) for (let xi = 0; xi < n; xi++) {
433
+ const x = (xi / (n - 1)) * 2 - 1, y = (yi / (n - 1)) * 2 - 1, z = (zi / (n - 1)) * 2 - 1;
434
+ const e = 0.04;
435
+ const gx = (_sampleField(x + e, y, z, hot) - _sampleField(x - e, y, z, hot)) / (2 * e);
436
+ const gy = (_sampleField(x, y + e, z, hot) - _sampleField(x, y - e, z, hot)) / (2 * e);
437
+ const gz = (_sampleField(x, y, z + e, hot) - _sampleField(x, y, z - e, hot)) / (2 * e);
438
+ const g = new THREE.Vector3(-gx, -gy, -gz); // heat flows down-gradient
439
+ const mag = g.length();
440
+ if (mag < 0.06) continue;
441
+ dirs.push({ p: new THREE.Vector3(x * 2, y * 2, z * 2), d: g.normalize(), mag });
442
+ }
443
+ const count = dirs.length;
444
+ const geo = new THREE.ConeGeometry(0.045, 0.28, 6);
445
+ geo.translate(0, 0.14, 0);
446
+ const mat = new THREE.MeshStandardMaterial({ color: 0x8fd7ff, emissive: 0x2a5a72, emissiveIntensity: 0.6, metalness: 0.3, roughness: 0.4 });
447
+ const inst = new THREE.InstancedMesh(geo, mat, count);
448
+ const m = new THREE.Matrix4(), q = new THREE.Quaternion(), up = new THREE.Vector3(0, 1, 0), scl = new THREE.Vector3();
449
+ for (let k = 0; k < count; k++) {
450
+ const { p, d, mag } = dirs[k];
451
+ q.setFromUnitVectors(up, d);
452
+ const L = 0.5 + Math.min(2.0, mag) * 0.9;
453
+ scl.set(1, L, 1);
454
+ m.compose(p, q, scl);
455
+ inst.setMatrixAt(k, m);
456
+ }
457
+ inst.instanceMatrix.needsUpdate = true;
458
+ inst.userData.arrows = true;
459
+ _disposables.push(geo, mat);
460
+ return inst;
461
+ }
462
+
463
+ // ---------------------------------------------------------------------------
464
+ // compute_bounds physical-ceiling ladder — vertical bars on a log axis showing where
465
+ // the MEASURED job sits between the Landauer floor and the Margolus-Levitin /
466
+ // Bremermann / Bekenstein ceilings. Heights/positions are DERIVED from the cert's
467
+ // real fractions. A genius "physical-bounds ladder" rendered in 3D, every rung labelled.
468
+ // ---------------------------------------------------------------------------
469
+ function _buildBoundsLadder() {
470
+ const THREE = _THREE;
471
+ const g = new THREE.Group();
472
+ g.position.set(4.6, -1.2, 0);
473
+ g.userData.ladder = true;
474
+ // a tall reference spine
475
+ const spineGeo = new THREE.CylinderGeometry(0.015, 0.015, 4.4, 8);
476
+ const spineMat = new THREE.MeshStandardMaterial({ color: 0x2a3a48, emissive: 0x10202a, emissiveIntensity: 0.5 });
477
+ const spine = new THREE.Mesh(spineGeo, spineMat);
478
+ spine.position.y = 2.2;
479
+ g.add(spine);
480
+ _disposables.push(spineGeo, spineMat);
481
+ g.userData.rungs = []; // filled live from cert fractions
482
+ return g;
483
+ }
484
+
485
+ // place rungs along the spine from DERIVED log-fractions (called when cert arrives)
486
+ function _updateBoundsLadder(g) {
487
+ const THREE = _THREE;
488
+ if (!g) return;
489
+ // remove old rungs
490
+ (g.userData.rungs || []).forEach((r) => { g.remove(r.mesh); if (r.label) g.remove(r.label); });
491
+ g.userData.rungs = [];
492
+ if (!F.haveCert || F.landauerMult == null) return;
493
+ // log10 scale: bottom = Landauer floor (the job is ~5e8× above it), top = Bremermann ceiling
494
+ // Build rungs at fractional heights derived from the real numbers.
495
+ const rungs = [
496
+ { name: "Landauer floor", y: 0.0, color: 0x2fd07a, note: "kT·ln2 — the job is " + _human(F.landauerMult) + "× above" },
497
+ { name: "MEASURED job", y: _clamp01(_logSpan(F.landauerMult, 1)), color: 0xe8c074, note: "5112 J DERIVED (P×t MEASURED)" },
498
+ { name: "Margolus-Levitin", y: _ceilHeight(F.mlFrac), color: 0x6fb1ff, note: "rate ceiling — job at " + _sci(F.mlFrac) + " of max" },
499
+ { name: "Bremermann", y: _ceilHeight(F.bremFrac), color: 0x9b8cff, note: "c²/h ceiling — job at " + _sci(F.bremFrac) },
500
+ { name: "Bekenstein", y: _ceilHeight(F.bekFrac), color: 0xff9d5c, note: "info ceiling — job at " + _sci(F.bekFrac) },
501
+ ];
502
+ rungs.forEach((rg) => {
503
+ const geo = new THREE.BoxGeometry(0.5, 0.07, 0.5);
504
+ const mat = new THREE.MeshStandardMaterial({ color: rg.color, emissive: rg.color, emissiveIntensity: 0.45, metalness: 0.3, roughness: 0.4 });
505
+ const mesh = new THREE.Mesh(geo, mat);
506
+ mesh.position.y = rg.y * 4.2 + 0.05;
507
+ g.add(mesh);
508
+ _disposables.push(geo, mat);
509
+ let label = null;
510
+ try {
511
+ label = _ctx.label.billboard(THREE, "MEASURED", { text: rg.name, scale: 0.34, position: [0.0, rg.y * 4.2 + 0.32, 0] });
512
+ // these rungs are DERIVED from measured scalars; keep the MEASURED chip honest
513
+ g.add(label);
514
+ } catch (_) {}
515
+ g.userData.rungs.push({ mesh, label });
516
+ });
517
+ }
518
+
519
+ function _logSpan(mult /* job/floor */, _ref) {
520
+ // place the MEASURED job ~ log10(mult)/log10(top) of the way up the spine
521
+ const top = 18; // ~ orders of magnitude up to the rate ceilings
522
+ return Math.min(0.95, Math.log10(Math.max(1, mult)) / top);
523
+ }
524
+ function _ceilHeight(frac /* job/ceiling, tiny */) {
525
+ if (frac == null || frac <= 0) return 0.97;
526
+ // higher rung = smaller fraction (more headroom). map log10(1/frac) up the spine.
527
+ const top = 50;
528
+ return Math.min(0.99, 0.55 + Math.log10(1 / frac) / top);
529
+ }
530
+ function _clamp01(x) { return Math.max(0, Math.min(1, x)); }
531
+ function _sci(x) { return (x == null) ? "—" : Number(x).toExponential(2); }
532
+ function _human(x) {
533
+ if (x == null) return "—";
534
+ if (x >= 1e9) return (x / 1e9).toFixed(1) + "e9";
535
+ if (x >= 1e6) return (x / 1e6).toFixed(1) + "e6";
536
+ return String(Math.round(x));
537
+ }
538
+
539
+ // ---------------------------------------------------------------------------
540
+ // HUD — the cert badge (MEASURED+SIGNED), cosign anchor, Rekor inclusion, backend
541
+ // indicator, the measured-scalar chips, the bounds readout, residual status, and the
542
+ // interactive controls (iso threshold / splats / arrows / residual displacement).
543
+ // ---------------------------------------------------------------------------
544
+ function _buildHUD() {
545
+ const wrap = document.createElement("div");
546
+ wrap.className = "szl3d-pinn-hud";
547
+ Object.assign(wrap.style, {
548
+ position: "absolute", left: "14px", top: "14px", zIndex: "6",
549
+ display: "flex", flexDirection: "column", gap: "8px",
550
+ maxWidth: "min(94%, 440px)", maxHeight: "calc(100% - 28px)", overflow: "auto",
551
+ font: "12px ui-monospace,SFMono-Regular,Menlo,monospace", color: "#cfe0ea",
552
+ });
553
+
554
+ const title = document.createElement("div");
555
+ title.style.cssText = "font:600 14px ui-sans-serif,system-ui;color:#eef3f6;letter-spacing:.4px";
556
+ title.textContent = "◇ PINN Thermal/Field · ray-march volume + MEASURED+SIGNED cert";
557
+ wrap.appendChild(title);
558
+
559
+ const sub = document.createElement("div");
560
+ sub.style.cssText = "color:#9fb1bf;font-size:11px;line-height:1.45";
561
+ sub.textContent = "Modeled on Kitware VTK.js/VolView + three.js TSL compute. " +
562
+ "Field render is MODELED (analytic PINN surrogate seeded by MEASURED telemetry). " +
563
+ "Certificate is MEASURED + SIGNED.";
564
+ wrap.appendChild(sub);
565
+
566
+ // live badge row (filled by poll)
567
+ const badge = _ctx.live.createBadge();
568
+ wrap.appendChild(badge.el);
569
+
570
+ // backend indicator
571
+ const back = document.createElement("div");
572
+ back.id = "pinn-backend";
573
+ back.style.cssText = "font-size:11px;color:#39d3c4";
574
+ back.textContent = "backend: " + (F.backend || "…");
575
+ wrap.appendChild(back);
576
+
577
+ // ---- cert / signature panel ----
578
+ const cert = document.createElement("div");
579
+ cert.id = "pinn-cert";
580
+ cert.style.cssText = "border:1px solid #1d2a36;border-radius:8px;padding:9px 10px;background:#0a1117cc;display:flex;flex-direction:column;gap:6px";
581
+ cert.innerHTML = "<div style='color:#9fb1bf'>awaiting live certificate…</div>";
582
+ wrap.appendChild(cert);
583
+
584
+ // ---- bounds readout ----
585
+ const bounds = document.createElement("div");
586
+ bounds.id = "pinn-bounds";
587
+ bounds.style.cssText = "border:1px solid #1d2a36;border-radius:8px;padding:9px 10px;background:#0a1117cc;font-size:11px;line-height:1.5";
588
+ bounds.innerHTML = "<div style='color:#9fb1bf'>compute_bounds: awaiting /pnt/limits + cert…</div>";
589
+ wrap.appendChild(bounds);
590
 
591
+ // ---- residual status ----
592
+ const resid = document.createElement("div");
593
+ resid.id = "pinn-residual";
594
+ resid.style.cssText = "border:1px solid #1d2a36;border-radius:8px;padding:8px 10px;background:#0a1117cc;font-size:11px";
595
+ resid.innerHTML = "<div style='color:#9fb1bf'>agentic-PINN residual: awaiting…</div>";
596
+ wrap.appendChild(resid);
597
+
598
+ // ---- controls ----
599
+ const ctrls = document.createElement("div");
600
+ ctrls.style.cssText = "border:1px solid #1d2a36;border-radius:8px;padding:9px 10px;background:#0a1117cc;display:flex;flex-direction:column;gap:7px;font-size:11px";
601
+
602
+ ctrls.appendChild(_sliderRow("isosurface threshold", 0.15, 0.92, 0.01, F.isoThreshold, (v) => {
603
+ F.isoThreshold = v;
604
+ if (_volMesh) _volMesh.material.uniforms.uThreshold.value = v;
605
+ _rebuildIso();
606
+ }));
607
+ ctrls.appendChild(_sliderRow("PDE-residual displacement", 0, 1, 0.01, F.residualDisp, (v) => {
608
+ F.residualDisp = v;
609
+ if (_residMesh) _applyResidualDisp(_residMesh, v);
610
+ }));
611
+ ctrls.appendChild(_toggleRow("Gaussian-splat field (novel)", F.splatOn, (on) => {
612
+ F.splatOn = on; if (_splatMesh) _splatMesh.visible = on;
613
+ }));
614
+ ctrls.appendChild(_toggleRow("vector arrow field (heat flow)", F.arrowsOn, (on) => {
615
+ F.arrowsOn = on; if (_arrowMesh) _arrowMesh.visible = on;
616
+ }));
617
+ wrap.appendChild(ctrls);
618
+
619
+ // ---- honesty legend ----
620
+ const legend = _ctx.label.legend();
621
+ legend.style.opacity = "0.85";
622
+ wrap.appendChild(legend);
623
+
624
+ const foot = document.createElement("div");
625
+ foot.style.cssText = "color:#7d8a96;font-size:10px;line-height:1.4";
626
+ foot.textContent = "Doctrine v11 · Λ = Conjecture 1 (advisory) · field MODELED, cert MEASURED+SIGNED · " +
627
+ "no fabricated field numbers · WebGPU→WebGL2 fallback · 0 runtime CDN";
628
+ wrap.appendChild(foot);
629
+
630
+ (_ctx.container || document.body).appendChild(wrap);
631
+ _overlay = wrap;
632
+ return { wrap, badge, cert, bounds, resid, back };
633
+ }
634
+
635
+ function _sliderRow(label, min, max, step, val, onInput) {
636
+ const row = document.createElement("label");
637
+ row.style.cssText = "display:flex;flex-direction:column;gap:3px";
638
+ const top = document.createElement("div");
639
+ top.style.cssText = "display:flex;justify-content:space-between;color:#cfe0ea";
640
+ const name = document.createElement("span"); name.textContent = label;
641
+ const out = document.createElement("span"); out.style.color = "#39d3c4"; out.textContent = (+val).toFixed(2);
642
+ top.appendChild(name); top.appendChild(out);
643
+ const s = document.createElement("input");
644
+ s.type = "range"; s.min = min; s.max = max; s.step = step; s.value = val;
645
+ s.style.cssText = "width:100%;accent-color:#39d3c4";
646
+ s.addEventListener("input", () => { const v = +s.value; out.textContent = v.toFixed(2); onInput(v); });
647
+ row.appendChild(top); row.appendChild(s);
648
+ return row;
649
+ }
650
+ function _toggleRow(label, on, onToggle) {
651
+ const row = document.createElement("label");
652
+ row.style.cssText = "display:flex;align-items:center;gap:8px;cursor:pointer;color:#cfe0ea";
653
+ const c = document.createElement("input"); c.type = "checkbox"; c.checked = on; c.style.accentColor = "#39d3c4";
654
+ c.addEventListener("change", () => onToggle(c.checked));
655
+ const t = document.createElement("span"); t.textContent = label;
656
+ row.appendChild(c); row.appendChild(t);
657
+ return row;
658
+ }
659
+
660
+ // scene-object refs for live updates / controls
661
+ let _volMesh = null, _splatMesh = null, _isoMesh = null, _residMesh = null, _arrowMesh = null, _ladder = null, _coreLight = null;
662
+ let _hud = null, _volHotBuiltAt = null, _certBillboard = null;
663
+
664
+ function _rebuildIso() {
665
+ if (!_isoMesh || !_group) return;
666
+ const sc = _fieldScalars();
667
+ const hot = _hotFromTemp(sc.tempK);
668
+ const next = _buildIsosurface(hot, F.isoThreshold);
669
+ next.visible = _isoMesh.visible;
670
+ _group.remove(_isoMesh);
671
+ try { _isoMesh.geometry.dispose(); } catch (_) {}
672
+ _isoMesh = next;
673
+ _group.add(_isoMesh);
674
+ }
675
+
676
+ function _hotFromTemp(tempK) {
677
+ // map ~[300K cool .. 360K hot] → ~[0.4 .. 1.1]; clamps. 341.29K MEASURED → ~0.95.
678
+ const t = (tempK - 300) / 60;
679
+ return Math.max(0.4, Math.min(1.15, 0.4 + t * 0.75));
680
+ }
681
+
682
+ // ---------------------------------------------------------------------------
683
+ // Live cert handler — reads the MEASURED+SIGNED certificate and updates state + HUD.
684
+ // Honest: we read fields straight off the JSON; if a field is absent we show "—",
685
+ // never a fabricated value. Field render relabels to MODELED once measured scalars land.
686
+ // ---------------------------------------------------------------------------
687
+ function _onCert(json, meta) {
688
+ if (!json || typeof json !== "object") return;
689
+ const cert = json.certificate || {};
690
+ const meas = cert.measured || {};
691
+ F.signed = !!json.signed;
692
+ // MEASURED scalars
693
+ const num = (x) => (typeof x === "number" && isFinite(x)) ? x : null;
694
+ F.tempK = num(meas.temperature_k_MEASURED);
695
+ F.powerW = num(meas.avg_power_w_MEASURED);
696
+ F.wallS = num(meas.wall_time_s_MEASURED);
697
+ F.energyJ = num(cert.energy_joules_derived);
698
+ // DERIVED bounds
699
+ F.landauerMult = num(cert.landauer_multiple_above_floor);
700
+ F.mlFrac = num(cert.margolus_levitin_headroom_fraction);
701
+ F.bremFrac = num(cert.bremermann_headroom_fraction);
702
+ F.bekFrac = num(cert.bekenstein_info_fraction);
703
+ F.bounded = !!cert.physically_bounded;
704
+ // signatures / anchors
705
+ const dsse = json.dsse || {};
706
+ const sig0 = (dsse.signatures && dsse.signatures[0]) || {};
707
+ F.ed25519Keyid = sig0.keyid || (json.certificate && null);
708
+ F.certSha = dsse._cert_sha256 || null;
709
+ const co = json.cosign || {};
710
+ F.cosignKeyid = co.keyid || null;
711
+ F.cosignPubUrl = co.pub_key_url || null;
712
+ const tl = dsse._transparency_log || {};
713
+ F.rekorProvider = tl.provider || null;
714
+ F.rekorUuid = tl.entry_uuid || null;
715
+ F.rekorIndex = tl.log_index != null ? tl.log_index : null;
716
+ F.rekorTime = tl.integrated_time_utc || null;
717
+ const kh = json.khipu || {};
718
+ F.khipuDigest = kh.digest || null;
719
+
720
+ const wasCert = F.haveCert;
721
+ F.haveCert = F.tempK != null;
722
+ // field label: MEASURED scalars exist → field is a MODELED viz of them
723
+ F.certLabel = F.haveCert ? "MODELED" : (meta && meta.label) || "SAMPLE";
724
+
725
+ // rebuild the field geometry against the (rarely-changing) measured temperature
726
+ const hot = _hotFromTemp(_fieldScalars().tempK);
727
+ if (!wasCert || _volHotBuiltAt == null || Math.abs(_volHotBuiltAt - hot) > 0.02) {
728
+ _rebuildFieldGeometry(hot);
729
+ _volHotBuiltAt = hot;
730
+ }
731
+ _updateBoundsLadder(_ladder);
732
+ _renderCertHUD();
733
+ _renderBoundsHUD();
734
+ }
735
+
736
+ function _rebuildFieldGeometry(hot) {
737
+ if (!_group || !_THREE) return;
738
+ // volume texture
739
+ if (_volMesh) {
740
+ const tex = _buildVolumeTexture(hot);
741
+ const old = _volMesh.material.uniforms.uVol.value;
742
+ _volMesh.material.uniforms.uVol.value = tex;
743
+ try { if (old && old.dispose) old.dispose(); } catch (_) {}
744
+ }
745
+ // splats
746
+ if (_splatMesh) { _group.remove(_splatMesh); try { _splatMesh.dispose && _splatMesh.dispose(); } catch (_) {} }
747
+ _splatMesh = _buildSplats(hot); _splatMesh.visible = F.splatOn; _group.add(_splatMesh);
748
+ // iso
749
+ _rebuildIso();
750
+ // residual shell
751
+ if (_residMesh) { _group.remove(_residMesh); try { _residMesh.geometry.dispose(); } catch (_) {} }
752
+ _residMesh = _buildResidualShell(hot); _applyResidualDisp(_residMesh, F.residualDisp); _group.add(_residMesh);
753
+ // arrows
754
+ if (_arrowMesh) { _group.remove(_arrowMesh); try { _arrowMesh.dispose && _arrowMesh.dispose(); } catch (_) {} }
755
+ _arrowMesh = _buildArrows(hot); _arrowMesh.visible = F.arrowsOn; _group.add(_arrowMesh);
756
+ // core light intensity tracks measured power
757
+ if (_coreLight) {
758
+ const sc = _fieldScalars();
759
+ _coreLight.intensity = 0.6 + Math.min(1.6, (sc.powerW || 30) / 56.18) * 1.0;
760
+ }
761
+ }
762
+
763
+ function _renderCertHUD() {
764
+ if (!_hud) return;
765
+ const c = _hud.cert;
766
+ const sc = _fieldScalars();
767
+ const chip = (lab, text) => {
768
+ const el = _ctx.label.chip(lab, { text });
769
+ el.style.marginRight = "5px"; el.style.marginBottom = "3px"; return el.outerHTML;
770
+ };
771
+ const measured = F.haveCert;
772
+ const rows = [];
773
+ // status line
774
+ const statusTxt = measured
775
+ ? (F.signed ? "MEASURED + SIGNED (DSSE Ed25519, FA-001 on-metal)" : "MEASURED · unsigned")
776
+ : "SAMPLE (no measured cert wired)";
777
+ rows.push(`<div style="font-weight:600;color:${measured ? '#2fd07a' : '#6fb1ff'}">${statusTxt}</div>`);
778
+ // measured scalars (each labelled MEASURED — they are real NVML samples)
779
+ if (measured) {
780
+ rows.push(`<div>${chip("MEASURED", "T = " + F.tempK.toFixed(2) + " K")}${chip("MEASURED", "P = " + F.powerW.toFixed(2) + " W")}</div>`);
781
+ rows.push(`<div>${chip("MEASURED", "t = " + F.wallS.toFixed(0) + " s")}${chip("MODELED", "E = " + (F.energyJ ? F.energyJ.toFixed(0) : "—") + " J (DERIVED P×t)")}</div>`);
782
+ rows.push(`<div style="color:#9fb1bf;font-size:10.5px">source: on-metal NVML · sovereign GPU betterwithage / RTX 5050 · 91 power.draw samples @1 Hz</div>`);
783
+ } else {
784
+ rows.push(`<div>${chip(F.certLabel, "field shape from SAMPLE seed (no measured cert)")}</div>`);
785
+ }
786
+ // signatures
787
+ if (F.signed) {
788
+ rows.push(`<div style="border-top:1px solid #18222c;margin-top:3px;padding-top:5px"></div>`);
789
+ if (F.ed25519Keyid) rows.push(`<div style="font-size:10.5px"><span style="color:#2fd07a">✓ Ed25519 DSSE</span> · keyid ${_short(F.ed25519Keyid)}</div>`);
790
+ if (F.cosignKeyid) rows.push(`<div style="font-size:10.5px"><span style="color:#2fd07a">✓ cosign</span> ECDSA-P256 · ${F.cosignKeyid}${F.cosignPubUrl ? ` · <a href="${F.cosignPubUrl}" target="_blank" rel="noopener" style="color:#39d3c4">cosign.pub ↗</a>` : ""}</div>`);
791
+ if (F.certSha) rows.push(`<div style="font-size:10px;color:#9fb1bf">cert ${_short(F.certSha)}</div>`);
792
+ // Rekor inclusion
793
+ if (F.rekorUuid) {
794
+ const url = "https://" + (F.rekorProvider || "rekor.sigstore.dev") + "/api/v1/log/entries/" + F.rekorUuid;
795
+ rows.push(`<div style="font-size:10.5px"><span style="color:#2fd07a">✓ Rekor</span> inclusion · index ${F.rekorIndex != null ? F.rekorIndex : "—"}${F.rekorTime ? " · " + F.rekorTime : ""}</div>`);
796
+ rows.push(`<div style="font-size:10px"><a href="${url}" target="_blank" rel="noopener" style="color:#39d3c4">entry ${_short(F.rekorUuid)} ↗</a></div>`);
797
+ }
798
+ if (F.khipuDigest) rows.push(`<div style="font-size:10px;color:#9fb1bf">khipu anchor ${_short("sha256:" + F.khipuDigest)} (append-only hash-chain)</div>`);
799
+ }
800
+ c.innerHTML = rows.join("");
801
+ }
802
+
803
+ function _renderBoundsHUD() {
804
+ if (!_hud) return;
805
+ const b = _hud.bounds;
806
+ if (!F.haveCert || F.landauerMult == null) {
807
+ b.innerHTML = `<div style="color:#9fb1bf">compute_bounds: awaiting cert bound values…</div>`;
808
+ return;
809
+ }
810
+ const verdict = F.bounded ? `<span style="color:#2fd07a">PHYSICALLY BOUNDED</span>` : `<span style="color:#ff6b6b">UNBOUNDED?</span>`;
811
+ b.innerHTML = [
812
+ `<div style="font-weight:600;color:#eef3f6">compute_bounds ladder — DERIVED from MEASURED</div>`,
813
+ `<div>verdict: ${verdict} (honest inverse of a free-energy claim)</div>`,
814
+ `<div>Landauer floor: <span style="color:#39d3c4">${_human(F.landauerMult)}×</span> above kT·ln2</div>`,
815
+ `<div>Margolus-Levitin: job at <span style="color:#39d3c4">${_sci(F.mlFrac)}</span> of rate ceiling</div>`,
816
+ `<div>Bremermann: <span style="color:#39d3c4">${_sci(F.bremFrac)}</span> of c²/h limit</div>`,
817
+ `<div>Bekenstein: <span style="color:#39d3c4">${_sci(F.bekFrac)}</span> of info ceiling</div>`,
818
+ `<div style="color:#7d8a96;font-size:10px">Landauer 1961 · Margolus-Levitin 1998 · Bremermann 1962 · Bekenstein 1981 — cited, not claimed</div>`,
819
+ ].join("");
820
+ }
821
+
822
+ function _onLimits(json) {
823
+ if (!_hud || !json) return;
824
+ const pillars = json.pillars || {};
825
+ const cb = pillars.compute_bounds || {};
826
+ // annotate the bounds panel with the pillar wiring status (honest)
827
+ const tag = document.createElement("div");
828
+ tag.style.cssText = "color:#7d8a96;font-size:10px;margin-top:3px";
829
+ tag.textContent = `/pnt/limits · compute_bounds pillar: ${cb.wired ? "wired (" + (cb.module || "szl_pinn_bounds") + ")" : "not wired"} · ${(json.pillars && Object.keys(json.pillars).length) || 0} pillars`;
830
+ tag.id = "pinn-limits-tag";
831
+ const old = _hud.bounds.querySelector("#pinn-limits-tag");
832
+ if (old) old.remove();
833
+ _hud.bounds.appendChild(tag);
834
+ }
835
+
836
+ function _onResidual(json, meta) {
837
+ if (!_hud || !json) return;
838
+ const r = _hud.resid;
839
+ const status = json.status || (json.rounds ? "OK" : "UNKNOWN");
840
+ F.residualState = status;
841
+ if (status === "AWAITING_GPU_SOLVE" || !json.rounds) {
842
+ // Structure (the residual shell) is in the scene, but no proven residual value
843
+ // exists until the governed GPU solve runs — doctrine: STRUCTURAL-ONLY, never faked.
844
+ r.innerHTML = `<div style="color:#e8c074;display:flex;align-items:center;gap:6px">agentic-PINN residual: <b>AWAITING_GPU_SOLVE</b> ` +
845
+ _ctx.label.chip("STRUCTURAL-ONLY", { text: "no proven residual" }).outerHTML + `</div>` +
846
+ `<div style="color:#9fb1bf;font-size:10px">Governed numpy PINN solver runs on SZL metal / Forge GPU and writes the per-round decision trail (RAR/RAD + deny-by-default Λ-gate). None wired here — honest AWAITING, never a fabricated residual.</div>`;
847
+ return;
848
+ }
849
+ F.residualRounds = json.rounds || [];
850
+ const last = F.residualRounds[F.residualRounds.length - 1] || {};
851
+ r.innerHTML = `<div style="color:#2fd07a">agentic-PINN residual: ${json.final_verdict || "—"} (accepted=${json.final_accepted})</div>` +
852
+ `<div style="font-size:10.5px">${F.residualRounds.length} rounds · last max-res ${last.max_residual != null ? Number(last.max_residual).toExponential(2) : "—"} · ` +
853
+ _ctx.label.chip("MODELED", { text: "error bound" }).outerHTML + `</div>`;
854
+ }
855
+
856
+ function _short(s) {
857
+ if (!s) return "—";
858
+ s = String(s);
859
+ if (s.length <= 22) return s;
860
+ return s.slice(0, 12) + "…" + s.slice(-8);
861
+ }
862
+
863
+ // ---------------------------------------------------------------------------
864
+ // mount / unmount
865
+ // ---------------------------------------------------------------------------
866
+ function mount(ctx) {
867
+ _ctx = ctx; _stage = ctx.stage; _THREE = ctx.THREE;
868
+ const THREE = _THREE;
869
+ F.backend = _stage.backend || "webgl2";
870
+
871
+ _group = new THREE.Group();
872
+ _group.name = "pinn-surface";
873
+ _stage.scene.add(_group);
874
+
875
+ // pull the camera in a touch for the volume
876
+ try { if (_stage.camera && _stage.camera.position) _stage.camera.position.set(0, 3, 12); } catch (_) {}
877
+
878
+ const sc = _fieldScalars();
879
+ const hot = _hotFromTemp(sc.tempK);
880
+
881
+ // --- ray-march volume (the centerpiece) ---
882
+ const vtex = _buildVolumeTexture(hot);
883
+ const boxGeo = new THREE.BoxGeometry(4, 4, 4);
884
+ let volMat;
885
  try {
886
+ volMat = _volumeMaterial(vtex);
887
+ _volMesh = new THREE.Mesh(boxGeo, volMat);
888
+ _volMesh.userData.volume = true;
889
+ _group.add(_volMesh);
890
+ _disposables.push(boxGeo, volMat);
891
+ } catch (e) {
892
+ // honest fallback: if ShaderMaterial fails on this backend, the iso+splats still render
893
+ if (typeof console !== "undefined") console.warn("[pinn] volume material unavailable on", F.backend, e && e.message);
894
+ _volMesh = null;
895
+ }
896
+
897
+ // --- isosurface shell ---
898
+ _isoMesh = _buildIsosurface(hot, F.isoThreshold);
899
+ _group.add(_isoMesh);
900
+
901
+ // --- Gaussian-splat scalar field (novel holographic) ---
902
+ _splatMesh = _buildSplats(hot); _splatMesh.visible = F.splatOn; _group.add(_splatMesh);
903
+
904
+ // --- PDE-residual displacement shell ---
905
+ _residMesh = _buildResidualShell(hot); _group.add(_residMesh);
906
+
907
+ // --- vector arrow field ---
908
+ _arrowMesh = _buildArrows(hot); _arrowMesh.visible = F.arrowsOn; _group.add(_arrowMesh);
909
+
910
+ // --- compute_bounds ladder ---
911
+ _ladder = _buildBoundsLadder(); _group.add(_ladder);
912
+
913
+ // --- a warm core light so the hot core glows; intensity tracks measured power ---
914
+ _coreLight = new THREE.PointLight(0xffd9a0, 0.8, 18, 2);
915
+ _coreLight.position.set(0, 0, 0);
916
+ _group.add(_coreLight);
917
+
918
+ // --- field honesty billboard (MODELED once cert lands, SAMPLE before) ---
919
+ try {
920
+ _certBillboard = ctx.label.billboard(THREE, sc.label, { text: "field: " + sc.label, scale: 0.6, position: [0, 3.3, 0] });
921
+ _group.add(_certBillboard);
922
  } catch (_) {}
923
 
924
+ // bloom for the holographic glow (no-op on WebGPU per toolkit contract; works on WebGL2)
925
+ try { _stage.setBloom(true); } catch (_) {}
926
+
927
+ // HUD
928
+ _hud = _buildHUD();
929
+ if (_hud && _hud.back) _hud.back.textContent = "backend: " + F.backend + (F.backend === "webgpu" ? " (WebGPU)" : " (WebGL2 fallback)");
 
 
 
 
 
 
930
 
931
+ // per-frame: animate volume time, gentle rotation, splat billboarding handled by Sprite-less
932
+ // InstancedMesh facing via camera quaternion, residual pulse.
933
+ _frameReg = (st) => {
934
+ if (!_group) return;
935
+ F.t += 0.016;
936
+ if (_volMesh && _volMesh.material.uniforms) {
937
+ _volMesh.material.uniforms.uTime.value = F.t;
938
+ try { _volMesh.material.uniforms.uCamPos.value.copy(st.camera.position); } catch (_) {}
939
+ }
940
+ _group.rotation.y += 0.0016;
941
+ // face splats toward camera (billboard the instanced quads)
942
+ if (_splatMesh && _splatMesh.visible) {
943
+ try { _splatMesh.quaternion.copy(st.camera.quaternion); } catch (_) {}
944
+ }
945
+ if (_certBillboard) { /* sprite auto-faces camera */ }
946
+ };
947
+ _stage.onFrame(_frameReg);
948
 
949
+ // ---- LIVE polls (never hardcode; honest degraded/missing handling in the toolkit) ----
950
+ _handles.push(ctx.live.poll(EP_CERT, 5000, _onCert, { badge: _hud.badge }));
951
+ _handles.push(ctx.live.poll(EP_LIMITS, 8000, _onLimits, {}));
952
+ _handles.push(ctx.live.poll(EP_RESIDUAL, 9000, _onResidual, {}));
953
+
954
+ return { id: ID, started: true, backend: F.backend };
955
  }
956
 
957
  function unmount() {
958
+ // stop polls
959
+ _handles.forEach((h) => { try { h.stop(); } catch (_) {} });
960
+ _handles = [];
961
+ // remove our DOM
962
  try { if (_overlay && _overlay.parentNode) _overlay.parentNode.removeChild(_overlay); } catch (_) {}
963
+ _overlay = null; _hud = null;
964
+ // neutralize our frame callback (the shell keeps its callback list; we guard on _group)
965
+ _frameReg = null;
966
+ // remove our scene group + dispose resources
967
+ try {
968
+ if (_group && _stage) _stage.scene.remove(_group);
969
+ } catch (_) {}
970
+ _disposables.forEach((d) => { try { d && d.dispose && d.dispose(); } catch (_) {} });
971
+ _disposables = [];
972
+ try { if (_volMesh && _volMesh.material.uniforms && _volMesh.material.uniforms.uVol.value) _volMesh.material.uniforms.uVol.value.dispose(); } catch (_) {}
973
+ try { _stage && _stage.setBloom(false); } catch (_) {}
974
+ _group = null; _volMesh = null; _splatMesh = null; _isoMesh = null; _residMesh = null;
975
+ _arrowMesh = null; _ladder = null; _coreLight = null; _certBillboard = null;
976
+ _stage = null; _THREE = null; _ctx = null; _volHotBuiltAt = null;
977
+ // reset live-derived state so a re-mount starts honest
978
+ F.haveCert = false; F.signed = false; F.tempK = null; F.powerW = null;
979
  }
980
 
981
+ export default {
982
+ id: ID,
983
+ title: TITLE,
984
+ endpoints: [EP_CERT, EP_LIMITS, EP_RESIDUAL],
985
+ mount,
986
+ unmount,
987
+ // exposed for tests / introspection (not part of the shell contract)
988
+ _F: F,
989
+ };