dancinlife Claude Opus 4.7 (1M context) commited on
Commit
95949ca
ยท
1 Parent(s): 76d7531

add: ๐ŸŒŠ Engine โ†’ Emergence tab โ€” pipeline view ties Aร—G to MI

Browse files

Fourth tab fuses the dual-engine and emergence concepts into a single
pipeline visualization, matching anima's central thesis: the act of A and G
repelling each other generates the mutual information that downstream
emerges as integrated experience.

Layout (top โ†’ bottom):
- Stage 1: compact phase space โ€” A (blue) and G (pink) clouds + repulsion
arrow, driven by the same sin/cos separation profile as the Aร—G tab
- Stage 2: two streams โ€” Stream L = projection of engine A + noise, Stream
R = same from G; rolling 200-sample buffers
- Stage 3: H(L), H(R), H(L,R), MI scoreboard + scatter + EMERGENT badge

Single 'separation' slider drives the whole pipeline. At sep โ‰ˆ 0 both
streams collapse to noise โ†’ MI โ‰ˆ 0 โ†’ badge dark. At sep โ‰ˆ 1 streams
follow opposite poles โ†’ distinguishable โ†’ MI > 0.30 โ†’ EMERGENT โœจ.

Self-contained entropy/jointEntropy/bin functions (PL_-prefixed) so this
tab doesn't depend on the emergence script's globals.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. index.html +328 -0
index.html CHANGED
@@ -180,6 +180,7 @@
180
  <button class="tab active" data-tab="emergence">โœจ Emergence</button>
181
  <button class="tab" data-tab="dtc">๐Ÿ”ฎ Time Crystal</button>
182
  <button class="tab" data-tab="ag">๐Ÿงฒ Aร—G Repulsion Field</button>
 
183
  </nav>
184
 
185
  <div id="tab-emergence" class="tab-panel active">
@@ -357,6 +358,7 @@ document.querySelectorAll(".tab").forEach(btn => {
357
  if (typeof window.applyEmergenceResize === "function") window.applyEmergenceResize();
358
  if (typeof window.applyDtcResize === "function") window.applyDtcResize();
359
  if (typeof window.applyAgResize === "function") window.applyAgResize();
 
360
  });
361
  });
362
  </script>
@@ -752,6 +754,60 @@ dtcResetBtn.addEventListener("click", () => {
752
  setInterval(dtcTick, DTC_TICK_MS);
753
  </script>
754
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
755
  <script>
756
  "use strict";
757
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
@@ -1067,5 +1123,277 @@ agResetBtn.addEventListener("click", () => {
1067
 
1068
  setInterval(agTick, AG_TICK_MS);
1069
  </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1070
  </body>
1071
  </html>
 
180
  <button class="tab active" data-tab="emergence">โœจ Emergence</button>
181
  <button class="tab" data-tab="dtc">๐Ÿ”ฎ Time Crystal</button>
182
  <button class="tab" data-tab="ag">๐Ÿงฒ Aร—G Repulsion Field</button>
183
+ <button class="tab" data-tab="pipeline">๐ŸŒŠ Engine โ†’ Emergence</button>
184
  </nav>
185
 
186
  <div id="tab-emergence" class="tab-panel active">
 
358
  if (typeof window.applyEmergenceResize === "function") window.applyEmergenceResize();
359
  if (typeof window.applyDtcResize === "function") window.applyDtcResize();
360
  if (typeof window.applyAgResize === "function") window.applyAgResize();
361
+ if (typeof window.applyPipelineResize === "function") window.applyPipelineResize();
362
  });
363
  });
364
  </script>
 
754
  setInterval(dtcTick, DTC_TICK_MS);
755
  </script>
756
 
757
+ <div id="tab-pipeline" class="tab-panel">
758
+ <div class="card">
759
+ <div class="header">
760
+ <div>
761
+ <h2 class="title">Engine โ†’ Emergence โ€” ๋“€์–ผ ์—”์ง„์ด emergence ๋ฅผ ์ƒ์„ฑํ•˜๋Š” pipeline</h2>
762
+ <div class="status" id="pl-status">์Šฌ๋ผ์ด๋”๋กœ ๋‘ ์—”์ง„์˜ ๋ถ„๋ฆฌ ์ •๋„๋ฅผ ์กฐ์ ˆํ•˜์„ธ์š”.</div>
763
+ </div>
764
+ <div class="metrics">
765
+ <div class="metric"><div class="metric-label">tension</div><div class="metric-value" id="pl-tension">0.00</div></div>
766
+ <div class="metric"><div class="metric-label">H(L)</div><div class="metric-value" id="pl-hl">0.00</div></div>
767
+ <div class="metric"><div class="metric-label">H(R)</div><div class="metric-value" id="pl-hr">0.00</div></div>
768
+ <div class="metric"><div class="metric-label">MI</div><div class="metric-value" id="pl-mi" style="color:#c8f7cd">0.000</div></div>
769
+ </div>
770
+ </div>
771
+
772
+ <div class="canvas-area">
773
+ <div class="channel-row">
774
+ <div class="channel-label">Stage 1 โ€” Phase space: A ์™€ G ๊ฐ€ separation ์— ๋น„๋ก€ํ•ด ์–‘๊ทน ๋ถ„๋ฆฌ</div>
775
+ <canvas class="phase-canvas" id="pl-phase" style="height:180px"></canvas>
776
+ </div>
777
+ <div class="stream-row">
778
+ <div class="stream-label" style="color:var(--blue)">Stage 2 โ€” Stream L (engine A ์˜ projection + noise)</div>
779
+ <canvas class="stream-canvas" id="pl-stream-l"></canvas>
780
+ </div>
781
+ <div class="stream-row">
782
+ <div class="stream-label" style="color:var(--green)">Stage 2 โ€” Stream R (engine G ์˜ projection + noise)</div>
783
+ <canvas class="stream-canvas" id="pl-stream-r"></canvas>
784
+ </div>
785
+ <hr class="divider">
786
+ <div class="bottom-row">
787
+ <div class="scatter-wrap">
788
+ <canvas class="scatter-canvas" id="pl-scatter" width="240" height="240"></canvas>
789
+ <div class="scatter-axis">Stream L</div>
790
+ </div>
791
+ <div class="badge" id="pl-badge">EMERGENT โœจ</div>
792
+ </div>
793
+ </div>
794
+
795
+ <div class="controls">
796
+ <span class="label">separation (Aโ†”G)</span>
797
+ <input type="range" id="pl-sep" min="0" max="100" value="60">
798
+ <span class="coup-val" id="pl-sep-val">0.60</span>
799
+ <button class="reset-btn" id="pl-reset">์ดˆ๊ธฐํ™”</button>
800
+ </div>
801
+
802
+ <div class="footnote">
803
+ <strong>Stage 1</strong>: A ์™€ G ๊ฐ€ separation ์— ๋น„๋ก€ํ•ด sin/cos ์–‘๊ทน์œผ๋กœ ๋ถ„๋ฆฌ โ€” tension = โ€–A โˆ’ Gโ€–ยฒ ๊ฐ€ ๊ทธ magnitude.<br>
804
+ <strong>Stage 2</strong>: ๋งค tick A ์˜ projection (+ noise) ์„ Stream L ์— push, G ์˜ projection ์„ Stream R ์— push.<br>
805
+ <strong>Stage 3</strong>: MI(L, R) = H(L) + H(R) โˆ’ H(L,R). ๋ถ„๋ฆฌ โ†‘ โ†’ ๋‘ ์ŠคํŠธ๋ฆผ distinguishable โ†‘ โ†’ MI โ†‘ โ†’ EMERGENT.<br>
806
+ ํ•ต์‹ฌ: <em>๋‘ ์—”์ง„์˜ ๋ถ„๋ฆฌ ํ–‰์œ„๊ฐ€ emergence ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค</em> โ€” anima ์ธ์šฉ๊ตฌ์˜ ์ง์ ‘ ์‹œ๊ฐํ™”.
807
+ </div>
808
+ </div>
809
+ </div>
810
+
811
  <script>
812
  "use strict";
813
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
 
1123
 
1124
  setInterval(agTick, AG_TICK_MS);
1125
  </script>
1126
+
1127
+ <script>
1128
+ "use strict";
1129
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1130
+ // ๐ŸŒŠ Engine โ†’ Emergence โ€” pipeline: A/G drives streams โ†’ MI
1131
+ // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
1132
+ const PL_DIM = 8;
1133
+ const PL_HIST = 200;
1134
+ const PL_TICK_MS = 33;
1135
+ const PL_BINS = 12;
1136
+ const PL_VRANGE = 1.6;
1137
+ const PL_MIN_FOR_METRICS = 50;
1138
+ const PL_MI_EMERGENT = 0.30;
1139
+ const PL_MI_PARTIAL = 0.05;
1140
+
1141
+ // state
1142
+ const plA = new Float32Array(PL_DIM);
1143
+ const plG = new Float32Array(PL_DIM);
1144
+ function plInit() {
1145
+ for (let i = 0; i < PL_DIM; i++) {
1146
+ plA[i] = (Math.random() - 0.5) * 0.3;
1147
+ plG[i] = (Math.random() - 0.5) * 0.3;
1148
+ }
1149
+ }
1150
+ plInit();
1151
+ let plSep = 0.60;
1152
+ let plT = 0;
1153
+ const plHistL = [];
1154
+ const plHistR = [];
1155
+
1156
+ // DOM
1157
+ const plPhase = document.getElementById("pl-phase");
1158
+ const plCtxPhase = plPhase.getContext("2d");
1159
+ const plStreamL = document.getElementById("pl-stream-l");
1160
+ const plCtxL = plStreamL.getContext("2d");
1161
+ const plStreamR = document.getElementById("pl-stream-r");
1162
+ const plCtxR = plStreamR.getContext("2d");
1163
+ const plScatter = document.getElementById("pl-scatter");
1164
+ const plCtxS = plScatter.getContext("2d");
1165
+ const plSepSlider = document.getElementById("pl-sep");
1166
+ const plSepVal = document.getElementById("pl-sep-val");
1167
+ const plResetBtn = document.getElementById("pl-reset");
1168
+ const elPlTension = document.getElementById("pl-tension");
1169
+ const elPlHL = document.getElementById("pl-hl");
1170
+ const elPlHR = document.getElementById("pl-hr");
1171
+ const elPlMI = document.getElementById("pl-mi");
1172
+ const elPlBadge = document.getElementById("pl-badge");
1173
+ const elPlStatus = document.getElementById("pl-status");
1174
+
1175
+ // resize
1176
+ let plDimsPhase, plDimsL, plDimsR;
1177
+ function plResize() {
1178
+ plDimsPhase = resizeCanvas(plPhase);
1179
+ plDimsL = resizeCanvas(plStreamL);
1180
+ plDimsR = resizeCanvas(plStreamR);
1181
+ }
1182
+ window.applyPipelineResize = plResize;
1183
+ plResize();
1184
+ window.addEventListener("resize", plResize);
1185
+
1186
+ // helpers
1187
+ function plBin(v) {
1188
+ let i = ((v + PL_VRANGE) / (2 * PL_VRANGE) * PL_BINS) | 0;
1189
+ if (i < 0) i = 0; else if (i >= PL_BINS) i = PL_BINS - 1;
1190
+ return i;
1191
+ }
1192
+ function plEntropy(arr) {
1193
+ if (!arr.length) return 0;
1194
+ const counts = new Int32Array(PL_BINS);
1195
+ for (let i = 0; i < arr.length; i++) counts[plBin(arr[i])]++;
1196
+ const inv = 1 / arr.length;
1197
+ let H = 0;
1198
+ for (let i = 0; i < PL_BINS; i++) if (counts[i] > 0) { const p = counts[i] * inv; H -= p * Math.log2(p); }
1199
+ return H;
1200
+ }
1201
+ function plJointEntropy(L, R) {
1202
+ const counts = new Int32Array(PL_BINS * PL_BINS);
1203
+ for (let i = 0; i < L.length; i++) counts[plBin(L[i]) * PL_BINS + plBin(R[i])]++;
1204
+ const inv = 1 / L.length;
1205
+ let H = 0;
1206
+ for (let i = 0; i < counts.length; i++) if (counts[i] > 0) { const p = counts[i] * inv; H -= p * Math.log2(p); }
1207
+ return H;
1208
+ }
1209
+
1210
+ // drawing
1211
+ function plValueToY(v, h) {
1212
+ const norm = (v + PL_VRANGE) / (2 * PL_VRANGE);
1213
+ return h - (norm * h * 0.84 + h * 0.08);
1214
+ }
1215
+ function plDrawStream(ctx, arr, color, w, h) {
1216
+ ctx.clearRect(0, 0, w, h);
1217
+ ctx.fillStyle = "rgba(35, 39, 48, 0.55)";
1218
+ ctx.fillRect(0, 0, w, h);
1219
+ if (arr.length < 2) return;
1220
+ ctx.beginPath();
1221
+ ctx.strokeStyle = color;
1222
+ ctx.lineWidth = 1.4;
1223
+ ctx.lineJoin = "round";
1224
+ const denom = PL_HIST - 1;
1225
+ for (let i = 0; i < arr.length; i++) {
1226
+ const x = (i / denom) * w;
1227
+ const y = plValueToY(arr[i], h);
1228
+ if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y);
1229
+ }
1230
+ ctx.stroke();
1231
+ }
1232
+ function plDrawPhase(w, h) {
1233
+ plCtxPhase.clearRect(0, 0, w, h);
1234
+ plCtxPhase.fillStyle = "rgba(35, 39, 48, 0.55)";
1235
+ plCtxPhase.fillRect(0, 0, w, h);
1236
+ const cx = w / 2, cy = h / 2;
1237
+ const scale = Math.min(w, h) * 0.32;
1238
+
1239
+ plCtxPhase.strokeStyle = "rgba(139, 146, 163, 0.18)";
1240
+ plCtxPhase.setLineDash([3, 3]);
1241
+ plCtxPhase.beginPath();
1242
+ plCtxPhase.moveTo(cx, 0); plCtxPhase.lineTo(cx, h);
1243
+ plCtxPhase.moveTo(0, cy); plCtxPhase.lineTo(w, cy);
1244
+ plCtxPhase.stroke();
1245
+ plCtxPhase.setLineDash([]);
1246
+
1247
+ const ax = cx + plA[0] * scale, ay = cy - plA[1] * scale;
1248
+ const gx = cx + plG[0] * scale, gy = cy - plG[1] * scale;
1249
+
1250
+ // small clouds
1251
+ for (let i = 0; i < 24; i++) {
1252
+ const dx = (Math.random() - 0.5) * 0.5;
1253
+ const dy = (Math.random() - 0.5) * 0.5;
1254
+ plCtxPhase.fillStyle = "rgba(123, 154, 255, 0.4)";
1255
+ plCtxPhase.beginPath();
1256
+ plCtxPhase.arc(ax + dx * scale * 0.2, ay + dy * scale * 0.2, 2, 0, Math.PI * 2);
1257
+ plCtxPhase.fill();
1258
+ plCtxPhase.fillStyle = "rgba(255, 123, 154, 0.4)";
1259
+ plCtxPhase.beginPath();
1260
+ plCtxPhase.arc(gx + dx * scale * 0.2, gy + dy * scale * 0.2, 2, 0, Math.PI * 2);
1261
+ plCtxPhase.fill();
1262
+ }
1263
+
1264
+ const arrowMag = Math.hypot(gx - ax, gy - ay);
1265
+ if (arrowMag > 5) {
1266
+ plCtxPhase.strokeStyle = "rgba(168, 230, 104, 0.85)";
1267
+ plCtxPhase.lineWidth = 2 + Math.min(4, arrowMag / 30);
1268
+ plCtxPhase.beginPath();
1269
+ plCtxPhase.moveTo(ax, ay); plCtxPhase.lineTo(gx, gy);
1270
+ plCtxPhase.stroke();
1271
+ const angle = Math.atan2(gy - ay, gx - ax);
1272
+ plCtxPhase.beginPath();
1273
+ plCtxPhase.moveTo(gx, gy);
1274
+ plCtxPhase.lineTo(gx - 9 * Math.cos(angle - Math.PI / 6), gy - 9 * Math.sin(angle - Math.PI / 6));
1275
+ plCtxPhase.moveTo(gx, gy);
1276
+ plCtxPhase.lineTo(gx - 9 * Math.cos(angle + Math.PI / 6), gy - 9 * Math.sin(angle + Math.PI / 6));
1277
+ plCtxPhase.stroke();
1278
+ }
1279
+
1280
+ plCtxPhase.fillStyle = "#7b9aff";
1281
+ plCtxPhase.beginPath(); plCtxPhase.arc(ax, ay, 4, 0, Math.PI * 2); plCtxPhase.fill();
1282
+ plCtxPhase.font = "bold 13px sans-serif"; plCtxPhase.fillText("A", ax + 8, ay - 6);
1283
+ plCtxPhase.fillStyle = "#ff7b9a";
1284
+ plCtxPhase.beginPath(); plCtxPhase.arc(gx, gy, 4, 0, Math.PI * 2); plCtxPhase.fill();
1285
+ plCtxPhase.fillText("G", gx + 8, gy - 6);
1286
+ }
1287
+ function plDrawScatter() {
1288
+ const w = 240, h = 240, pad = 12;
1289
+ plCtxS.clearRect(0, 0, w, h);
1290
+ plCtxS.strokeStyle = "rgba(139, 146, 163, 0.5)";
1291
+ plCtxS.lineWidth = 1;
1292
+ plCtxS.setLineDash([4, 4]);
1293
+ plCtxS.strokeRect(pad, pad, w - 2 * pad, h - 2 * pad);
1294
+ plCtxS.beginPath();
1295
+ plCtxS.moveTo(pad, h - pad); plCtxS.lineTo(w - pad, pad);
1296
+ plCtxS.stroke();
1297
+ plCtxS.setLineDash([]);
1298
+ for (let i = 0; i < plHistL.length; i++) {
1299
+ const xn = (plHistL[i] + PL_VRANGE) / (2 * PL_VRANGE);
1300
+ const yn = (plHistR[i] + PL_VRANGE) / (2 * PL_VRANGE);
1301
+ const x = xn * (w - 2 * pad) + pad;
1302
+ const y = h - (yn * (h - 2 * pad) + pad);
1303
+ plCtxS.fillStyle = i % 2 === 0 ? "rgba(123, 154, 255, 0.55)" : "rgba(168, 230, 104, 0.55)";
1304
+ plCtxS.beginPath();
1305
+ plCtxS.arc(x, y, 2.4, 0, Math.PI * 2);
1306
+ plCtxS.fill();
1307
+ }
1308
+ }
1309
+
1310
+ // tick
1311
+ function plTick() {
1312
+ if (window.activeTab !== "pipeline") return;
1313
+
1314
+ // engine drift
1315
+ const phase = plT * 0.025;
1316
+ for (let i = 0; i < PL_DIM; i++) {
1317
+ plA[i] += (Math.random() - 0.5) * 0.04;
1318
+ plG[i] += (Math.random() - 0.5) * 0.04;
1319
+ if (i === 0) {
1320
+ const t = plSep * 1.3 * Math.sin(phase);
1321
+ plA[i] += (t - plA[i]) * 0.06;
1322
+ plG[i] += (-t - plG[i]) * 0.06;
1323
+ } else if (i === 1) {
1324
+ const t = plSep * 1.0 * Math.cos(phase);
1325
+ plA[i] += (t - plA[i]) * 0.06;
1326
+ plG[i] += (-t - plG[i]) * 0.06;
1327
+ } else {
1328
+ plA[i] *= 0.985; plG[i] *= 0.985;
1329
+ }
1330
+ }
1331
+
1332
+ // tension
1333
+ let tSq = 0;
1334
+ for (let i = 0; i < PL_DIM; i++) {
1335
+ const r = plA[i] - plG[i];
1336
+ tSq += r * r;
1337
+ }
1338
+ const tension = tSq / PL_DIM;
1339
+
1340
+ // sample one observation per tick from each engine's distribution
1341
+ const sampleL = plA[0] + (Math.random() - 0.5) * 0.4;
1342
+ const sampleR = plG[0] + (Math.random() - 0.5) * 0.4;
1343
+ plHistL.push(sampleL);
1344
+ plHistR.push(sampleR);
1345
+ if (plHistL.length > PL_HIST) {
1346
+ plHistL.shift(); plHistR.shift();
1347
+ }
1348
+
1349
+ // emergence
1350
+ let hL = 0, hR = 0, hJ = 0, mi = 0;
1351
+ if (plHistL.length >= PL_MIN_FOR_METRICS) {
1352
+ hL = plEntropy(plHistL);
1353
+ hR = plEntropy(plHistR);
1354
+ hJ = plJointEntropy(plHistL, plHistR);
1355
+ mi = Math.max(0, hL + hR - hJ);
1356
+ }
1357
+
1358
+ elPlTension.textContent = tension.toFixed(2);
1359
+ elPlHL.textContent = hL.toFixed(2);
1360
+ elPlHR.textContent = hR.toFixed(2);
1361
+ elPlMI.textContent = mi.toFixed(3);
1362
+
1363
+ if (mi > PL_MI_EMERGENT) {
1364
+ elPlBadge.classList.add("active");
1365
+ elPlStatus.textContent = "โœจ Emergence โ€” A ์™€ G ์˜ ๋ถ„๋ฆฌ๊ฐ€ ๋‘ ์ŠคํŠธ๋ฆผ์— ๊ณตํ†ต ์ •๋ณด๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.";
1366
+ } else if (mi > PL_MI_PARTIAL) {
1367
+ elPlBadge.classList.remove("active");
1368
+ elPlStatus.textContent = "๋ถ€๋ถ„์  emergence โ€” ๋ถ„๋ฆฌ๊ฐ€ ์•ฝํ•จ, MI ๊ฐ€ ์ž„๊ณ„ ์•„๋ž˜.";
1369
+ } else {
1370
+ elPlBadge.classList.remove("active");
1371
+ elPlStatus.textContent = "๋‘ ์—”์ง„์ด ๊ฑฐ์˜ ๋™์ผ โ€” emergence 0, ์‚ฌ๊ณ  ์ •์ง€.";
1372
+ }
1373
+
1374
+ plDrawPhase(plDimsPhase[0], plDimsPhase[1]);
1375
+ plDrawStream(plCtxL, plHistL, "#7b9aff", plDimsL[0], plDimsL[1]);
1376
+ plDrawStream(plCtxR, plHistR, "#a8e668", plDimsR[0], plDimsR[1]);
1377
+ plDrawScatter();
1378
+
1379
+ plT++;
1380
+ }
1381
+
1382
+ // wiring
1383
+ plSepSlider.addEventListener("input", () => {
1384
+ plSep = parseInt(plSepSlider.value, 10) / 100;
1385
+ plSepVal.textContent = plSep.toFixed(2);
1386
+ });
1387
+ plResetBtn.addEventListener("click", () => {
1388
+ plInit();
1389
+ plHistL.length = 0; plHistR.length = 0;
1390
+ plSepSlider.value = 60;
1391
+ plSep = 0.60;
1392
+ plSepVal.textContent = "0.60";
1393
+ elPlBadge.classList.remove("active");
1394
+ });
1395
+
1396
+ setInterval(plTick, PL_TICK_MS);
1397
+ </script>
1398
  </body>
1399
  </html>