zkaedi commited on
Commit
10c1475
Β·
verified Β·
1 Parent(s): 01e5081

Upload index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +567 -509
index.html CHANGED
@@ -1,646 +1,704 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>πŸ”± ZKAEDI PRIME β€” Phase Portrait Visualizer</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com" />
8
- <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;700;800&family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet" />
 
9
  <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
10
  <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
11
  <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.9/babel.min.js"></script>
12
  <style>
13
- *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
14
- :root {
15
- --cyan: #00ffff; --magenta: #ff00ff; --dark: #06060c;
16
- --panel: rgba(255,255,255,0.02); --border: rgba(255,255,255,0.07);
17
- --violet: #8b5cf6; --green: #10b981; --amber: #f59e0b;
18
- }
19
- html, body, #root { height: 100%; background: var(--dark); color: #c0c0c0; overflow-x: hidden; }
20
- body { font-family: 'JetBrains Mono', monospace; }
21
- ::-webkit-scrollbar { width: 5px; }
22
- ::-webkit-scrollbar-track { background: var(--dark); }
23
- ::-webkit-scrollbar-thumb { background: rgba(0,255,255,0.2); border-radius: 3px; }
24
- @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.4} }
25
- @keyframes scan { 0%{top:-2px} 100%{top:100%} }
26
- @keyframes glowPulse { 0%,100%{box-shadow:0 0 15px rgba(0,255,255,0.1)} 50%{box-shadow:0 0 30px rgba(0,255,255,0.25)} }
27
- button { font-family: inherit; cursor: pointer; transition: all 0.2s; }
28
- button:hover { filter: brightness(1.3); }
29
- button:active { transform: scale(0.97); }
30
  </style>
31
  </head>
32
  <body>
33
  <div id="root"></div>
34
  <script type="text/babel">
35
- const { useState, useEffect, useRef, useCallback, useMemo } = React;
36
-
37
- // ═══════════════════════════════════════════════════════════════
38
- // πŸ”± ZKAEDI PRIME β€” Phase Portrait Visualizer
39
- // Publish the Output. Protect the Engine.
40
- // ═══════════════════════════════════════════════════════════════
41
 
42
- const C = "#00ffff", M = "#ff00ff", V = "#8b5cf6", G = "#10b981", A = "#f59e0b";
 
43
 
44
- const sigmoid = (x) => 1 / (1 + Math.exp(-Math.max(-20, Math.min(20, x))));
45
-
46
- class RNG {
47
- constructor(s) { this.s = s | 0; }
48
- next() { this.s = (this.s * 1664525 + 1013904223) & 0xffffffff; return (this.s >>> 0) / 0xffffffff; }
49
- gauss() { const u = this.next() || 1e-4, v = this.next(); return Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v); }
50
  }
51
 
52
- function genField(res, p) {
53
- const { eta, gamma, beta, sigma, iterations, seed, potential } = p;
54
- const rng = new RNG(seed);
55
- const n = res, H = new Float32Array(n * n), Hb = new Float32Array(n * n);
56
- for (let i = 0; i < n; i++) {
57
- for (let j = 0; j < n; j++) {
58
- const x = (i / n - 0.5) * 6, y = (j / n - 0.5) * 6;
59
- let v = 0;
60
- switch (potential) {
61
- case "double_well": v = (x*x-1)*(x*x-1) + y*y; break;
62
- case "spiral": { const r = Math.sqrt(x*x+y*y), th = Math.atan2(y,x); v = r*r - 3*Math.cos(th*3+r*2); break; }
63
- case "chaotic": v = Math.sin(x*2)*Math.cos(y*3) + Math.sin(x*y)*2; break;
64
- case "crystal": v = Math.cos(x*Math.PI)+Math.cos(y*Math.PI)+0.5*Math.cos((x+y)*Math.PI)+0.5*Math.cos((x-y)*Math.PI); break;
65
- default: { const r2 = x*x+y*y; v = (r2-4)*(r2-4)*0.1 - Math.exp(-r2*0.5)*3; }
66
- }
67
- Hb[i*n+j] = v; H[i*n+j] = v;
68
  }
 
69
  }
70
- for (let t = 0; t < iterations; t++) {
71
- for (let idx = 0; idx < n*n; idx++) {
72
- const sig = sigmoid(gamma * H[idx]);
73
- const noise = rng.gauss() * (1 + beta * Math.abs(H[idx]));
74
- H[idx] = Hb[idx] + eta * H[idx] * sig + sigma * noise;
75
  }
76
  }
77
- return { H, Hb, size: n };
78
  }
79
 
80
- function fieldGrad(H, n, i, j) {
81
- const g = (ci, cj) => H[Math.max(0,Math.min(n-1,ci))*n+Math.max(0,Math.min(n-1,cj))];
82
- return [(g(i+1,j)-g(i-1,j))*0.5, (g(i,j+1)-g(i,j-1))*0.5];
83
  }
84
 
85
- function trace(H, n, si, sj, steps, dt) {
86
- const pts = [];
87
- let fi = si, fj = sj;
88
- for (let s = 0; s < steps; s++) {
89
- if (fi < 1 || fi >= n-1 || fj < 1 || fj >= n-1) break;
90
- pts.push([fi, fj]);
91
- const [dx, dy] = fieldGrad(H, n, Math.floor(fi), Math.floor(fj));
92
- const mag = Math.sqrt(dx*dx+dy*dy) + 1e-8;
93
- fi -= (dy/mag)*dt; fj += (dx/mag)*dt;
94
  }
95
  return pts;
96
  }
97
 
98
- function eColor(val, mn, mx, mode) {
99
- const t = Math.max(0, Math.min(1, (val-mn)/(mx-mn+1e-10)));
100
- if (mode === "cyan_magenta") return [Math.floor(t*255), Math.floor((1-t)*255), 255];
101
- if (mode === "plasma") return [Math.floor(Math.min(255,t*510)), Math.floor(Math.max(0,(t-0.3)*400)), Math.floor((1-t)*255)];
102
- return [Math.floor(t*255), Math.floor(t*t*180), Math.floor((1-t*t)*200+t*55)];
 
103
  }
104
 
105
- function computeStats(H) {
106
- let mn=Infinity, mx=-Infinity, sum=0, sq=0;
107
- for (let i=0;i<H.length;i++){if(H[i]<mn)mn=H[i];if(H[i]>mx)mx=H[i];sum+=H[i];sq+=H[i]*H[i];}
108
- const mean=sum/H.length, vari=sq/H.length-mean*mean;
109
- const bins=60, hist=new Float32Array(bins);
110
- for(let i=0;i<H.length;i++){const b=Math.floor(((H[i]-mn)/(mx-mn+1e-10))*(bins-1));hist[b]++;}
111
- let ent=0;
112
- for(let b=0;b<bins;b++){const p=hist[b]/H.length;if(p>0)ent-=p*Math.log2(p);}
113
- return {min:mn, max:mx, mean, variance:vari, entropy:ent, histogram:hist, bins};
 
 
114
  }
115
 
116
- // ── Slider ──
117
- function Slider({label,value,min,max,step,onChange,color,unit}) {
118
- const pct = ((value-min)/(max-min))*100;
119
- return (
120
- <div style={{marginBottom:8}}>
121
- <div style={{display:"flex",justifyContent:"space-between",marginBottom:2}}>
122
- <span style={{color:color||C,fontSize:10,letterSpacing:1,textTransform:"uppercase"}}>{label}</span>
123
- <span style={{color:"#fff",fontSize:10}}>{typeof value==="number"?value.toFixed(step<1?2:0):value}{unit||""}</span>
124
- </div>
125
- <div style={{position:"relative",height:5,background:"rgba(255,255,255,0.05)",borderRadius:3}}>
126
- <div style={{position:"absolute",left:0,top:0,height:"100%",width:`${pct}%`,borderRadius:3,
127
- background:`linear-gradient(90deg,${color||C},${M})`,boxShadow:`0 0 6px ${(color||C)}30`,transition:"width 0.08s"}}/>
128
- <input type="range" min={min} max={max} step={step} value={value}
129
- onChange={e=>onChange(parseFloat(e.target.value))}
130
- style={{position:"absolute",top:-8,left:0,width:"100%",height:20,opacity:0,cursor:"pointer",margin:0}} />
131
- </div>
132
- </div>
133
- );
134
  }
135
 
136
- function Stat({label,value,color}) {
137
- return (
138
- <div style={{background:"var(--panel)",border:"1px solid var(--border)",borderRadius:5,padding:"6px 10px",flex:1,minWidth:70}}>
139
- <div style={{fontSize:8,color:"#555",letterSpacing:1,textTransform:"uppercase",marginBottom:1}}>{label}</div>
140
- <div style={{fontSize:13,color:color||C,fontWeight:700}}>{value}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  </div>
142
- );
 
 
 
 
 
 
 
143
  }
144
 
145
- function PanelHeader({children}) {
146
- return <div style={{fontSize:9,color:"#555",letterSpacing:2,marginBottom:10,textTransform:"uppercase",
147
- borderBottom:"1px solid var(--border)",paddingBottom:6}}>{children}</div>;
 
 
148
  }
149
 
150
- function Panel({children, style:s}) {
151
- return <div style={{background:"var(--panel)",border:"1px solid var(--border)",borderRadius:8,padding:14,...s}}>{children}</div>;
 
 
 
 
152
  }
153
 
154
- // ── Histogram ──
155
- function Histogram({histogram, bins, colorMode}) {
156
- if (!histogram) return null;
157
- const maxVal = Math.max(...histogram);
158
- const w = 240, h = 80;
159
- return (
160
- <svg width="100%" viewBox={`0 0 ${w} ${h}`} style={{display:"block"}}>
161
- {Array.from({length:bins},(_, i) => {
162
- const bh = (histogram[i]/maxVal)*h*0.9;
163
- const t = i/bins;
164
- const [r,g,b] = eColor(t*(1)+0, 0, 1, colorMode);
165
- return <rect key={i} x={i*(w/bins)} y={h-bh} width={w/bins-0.5} height={bh}
166
- fill={`rgb(${r},${g},${b})`} opacity={0.7} />;
167
- })}
168
- <line x1={0} y1={h} x2={w} y2={h} stroke="rgba(255,255,255,0.1)" strokeWidth={0.5}/>
169
- </svg>
170
- );
171
  }
172
 
173
- // ── Bifurcation Diagram ──
174
- function BifurcationDiagram({potential, params, paramKey, steps}) {
175
- const canvasRef = useRef(null);
176
- const w = 280, h = 120;
177
-
178
- useEffect(() => {
179
- const cv = canvasRef.current; if(!cv) return;
180
- const ctx = cv.getContext("2d");
181
- ctx.fillStyle = "rgba(6,6,12,0.95)";
182
- ctx.fillRect(0,0,w,h);
183
-
184
- const samples = 60;
185
- const probePts = 8;
186
- const rng = new RNG(params.seed);
187
-
188
- const ranges = {eta:[0,1],gamma:[0,2],beta:[0,0.5],sigma:[0,0.3]};
189
- const [lo, hi] = ranges[paramKey] || [0,1];
190
-
191
- let allVals = [];
192
- for (let s = 0; s < samples; s++) {
193
- const paramVal = lo + (s/samples)*(hi-lo);
194
- const p = {...params, potential, [paramKey]:paramVal, iterations:Math.min(params.iterations,15)};
195
- const {H, size} = genField(40, p);
196
- const vals = [];
197
- for (let k=0; k<probePts; k++) {
198
- const idx = Math.floor(rng.next()*H.length);
199
- vals.push(H[idx]);
200
- }
201
- allVals.push({x:s, vals});
202
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
- const flatVals = allVals.flatMap(a=>a.vals);
205
- const mn = Math.min(...flatVals), mx = Math.max(...flatVals);
206
-
207
- allVals.forEach(({x, vals}) => {
208
- vals.forEach(v => {
209
- const px = (x/samples)*w;
210
- const py = h - ((v-mn)/(mx-mn+1e-10))*h*0.9 - h*0.05;
211
- const t = (v-mn)/(mx-mn+1e-10);
212
- ctx.fillStyle = `rgba(0,${Math.floor(255*(1-t))},255,0.5)`;
213
- ctx.fillRect(px, py, 2, 2);
214
- });
215
- });
216
-
217
- // Axis labels
218
- ctx.fillStyle = "rgba(0,255,255,0.3)";
219
- ctx.font = "8px 'JetBrains Mono'";
220
- ctx.fillText(paramKey + " β†’", w-50, h-3);
221
- ctx.fillText("E(x)", 2, 10);
 
 
 
 
 
 
 
 
 
 
 
222
 
223
- }, [potential, params, paramKey]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
- return <canvas ref={canvasRef} width={w} height={h} style={{width:"100%",borderRadius:4,border:"1px solid var(--border)"}} />;
 
 
226
  }
227
 
228
- // ═══════════════════════════════════════════════════════════════
229
  // MAIN APP
230
- // ═══════════════════════════════════════════════════════════════
231
- function App() {
232
- const canvasRef = useRef(null);
233
- const trajRef = useRef(null);
234
- const animRef = useRef(null);
235
-
236
- const [animating, setAnimating] = useState(false);
237
- const [animStep, setAnimStep] = useState(0);
238
- const [showTrails, setShowTrails] = useState(true);
239
- const [showGrid, setShowGrid] = useState(true);
240
- const [colorMode, setColorMode] = useState("cyan_magenta");
241
- const [potential, setPotential] = useState("double_well");
242
- const [bifParam, setBifParam] = useState("eta");
243
- const [activeTab, setActiveTab] = useState("params");
244
- const [stats, setStats] = useState({min:0,max:0,mean:0,variance:0,entropy:0,histogram:null,bins:60});
245
- const [fieldData, setFieldData] = useState(null);
246
-
247
- const [params, setParams] = useState({
248
- eta:0.40, gamma:0.30, beta:0.10, sigma:0.05,
249
- iterations:12, seed:42, trajectoryCount:24, trajectoryLength:300
250
- });
251
-
252
- const res = 180;
253
-
254
- const render = useCallback((overrideIter) => {
255
- const cv = canvasRef.current, tv = trajRef.current;
256
- if (!cv||!tv) return;
257
- const ctx = cv.getContext("2d"), tctx = tv.getContext("2d");
258
- const cw = cv.width, ch = cv.height;
259
-
260
- const p = {...params, potential, iterations: overrideIter ?? params.iterations};
261
- const {H, size} = genField(res, p);
262
- const st = computeStats(H);
263
- setStats(st);
264
- setFieldData(H);
265
- const {min:mn, max:mx} = st;
266
- const scale = cw / size;
267
-
268
- // Field
269
- const img = ctx.createImageData(cw, ch);
270
- for (let i=0;i<size;i++) for(let j=0;j<size;j++){
271
- const [r,g,b] = eColor(H[i*size+j], mn, mx, colorMode);
272
- const px=Math.floor(j*scale), py=Math.floor(i*scale);
273
- const pw=Math.ceil(scale), ph=Math.ceil(scale);
274
- for(let dy=0;dy<ph&&py+dy<ch;dy++) for(let dx=0;dx<pw&&px+dx<cw;dx++){
275
- const idx=((py+dy)*cw+(px+dx))*4;
276
- img.data[idx]=r;img.data[idx+1]=g;img.data[idx+2]=b;img.data[idx+3]=220;
277
  }
278
  }
279
  ctx.putImageData(img,0,0);
280
 
281
- // Grid
282
- if(showGrid){
283
- ctx.strokeStyle="rgba(0,255,255,0.05)";ctx.lineWidth=0.5;
284
- const gs=cw/12;
285
  for(let g=0;g<=12;g++){ctx.beginPath();ctx.moveTo(g*gs,0);ctx.lineTo(g*gs,ch);ctx.stroke();
286
- ctx.beginPath();ctx.moveTo(0,g*gs);ctx.lineTo(cw,g*gs);ctx.stroke();}
287
- }
 
 
 
 
 
 
288
 
289
- // Contour lines (subtle)
290
- ctx.strokeStyle = "rgba(255,255,255,0.06)";
291
- ctx.lineWidth = 0.5;
292
- const levels = 12;
293
- for (let l=0; l<levels; l++) {
294
- const threshold = mn + (l/levels)*(mx-mn);
295
- for(let i=0;i<size-1;i++) for(let j=0;j<size-1;j++){
296
- const v00=H[i*size+j], v10=H[(i+1)*size+j], v01=H[i*size+j+1];
297
- if((v00<threshold)!==(v10<threshold)||(v00<threshold)!==(v01<threshold)){
298
- ctx.fillStyle="rgba(255,255,255,0.04)";
299
- ctx.fillRect(Math.floor(j*scale),Math.floor(i*scale),Math.ceil(scale),Math.ceil(scale));
300
- }
301
- }
302
- }
303
-
304
- // Trajectories
305
  tctx.clearRect(0,0,cw,ch);
306
- if(showTrails) {
307
- const rng = new RNG(params.seed+1000);
308
  for(let t=0;t<params.trajectoryCount;t++){
309
- const si=Math.floor(rng.next()*(size-4))+2, sj=Math.floor(rng.next()*(size-4))+2;
310
- const pts=trace(H,size,si,sj,params.trajectoryLength,0.8);
311
- if(pts.length<3) continue;
312
  for(let pi=1;pi<pts.length;pi++){
313
- const alpha=1-(pi/pts.length);
314
- const hue=(t/params.trajectoryCount)*60+160;
315
- tctx.strokeStyle=`hsla(${hue},100%,70%,${alpha*0.65})`;
316
- tctx.lineWidth=1.5*alpha+0.3;
317
- tctx.beginPath();
318
- tctx.moveTo(pts[pi-1][1]*scale,pts[pi-1][0]*scale);
319
- tctx.lineTo(pts[pi][1]*scale,pts[pi][0]*scale);
320
- tctx.stroke();
321
  }
322
- tctx.shadowBlur=5;
323
- tctx.fillStyle=C;tctx.shadowColor=C;
324
  tctx.beginPath();tctx.arc(pts[0][1]*scale,pts[0][0]*scale,2.5,0,Math.PI*2);tctx.fill();
325
- if(pts.length>2){
326
- const last=pts[pts.length-1];
327
- tctx.fillStyle=M;tctx.shadowColor=M;
328
- tctx.beginPath();tctx.arc(last[1]*scale,last[0]*scale,2,0,Math.PI*2);tctx.fill();
329
- }
330
  tctx.shadowBlur=0;
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  }
 
332
  }
333
- }, [params, potential, colorMode, showTrails, showGrid]);
334
 
335
- useEffect(()=>{render();},[render]);
 
 
 
 
 
336
 
337
  useEffect(()=>{
338
- if(!animating){if(animRef.current)cancelAnimationFrame(animRef.current);return;}
339
  let step=1;const mx=50;
340
- const go=()=>{if(step>mx){setAnimating(false);setAnimStep(0);return;}
341
- setAnimStep(step);render(step);step++;
342
- animRef.current=requestAnimationFrame(()=>setTimeout(go,50));};
343
- go();
344
- return()=>{if(animRef.current)cancelAnimationFrame(animRef.current);};
345
  },[animating]);
346
 
347
- const up = key => val => setParams(p=>({...p,[key]:val}));
348
-
349
- const exportPNG = () => {
350
- const cv = canvasRef.current, tv = trajRef.current;
351
- if(!cv) return;
352
- const exp = document.createElement("canvas");
353
- exp.width = cv.width; exp.height = cv.height;
354
- const ectx = exp.getContext("2d");
355
- ectx.drawImage(cv,0,0);
356
- if(tv) ectx.drawImage(tv,0,0);
357
- // Watermark
358
- ectx.fillStyle="rgba(0,255,255,0.4)";
359
- ectx.font="bold 10px 'JetBrains Mono'";
360
- ectx.fillText("πŸ”± ZKAEDI PRIME",10,cv.height-10);
361
- const link = document.createElement("a");
362
- link.download = `zkaedi_prime_${potential}_s${params.seed}.png`;
363
- link.href = exp.toDataURL("image/png");
364
- link.click();
365
- };
366
-
367
- const potentials = [
368
- {id:"double_well",label:"DOUBLE WELL",icon:"βŠ—"},
369
- {id:"spiral",label:"SPIRAL",icon:"πŸŒ€"},
370
- {id:"chaotic",label:"CHAOTIC",icon:"⚑"},
371
- {id:"crystal",label:"CRYSTAL",icon:"πŸ’Ž"},
372
- {id:"mexican_hat",label:"MEXICAN HAT",icon:"🎩"},
373
- ];
374
 
375
- const colors = [{id:"cyan_magenta",label:"PRIME"},{id:"plasma",label:"PLASMA"},{id:"inferno",label:"INFERNO"}];
376
- const tabs = [{id:"params",label:"DYNAMICS"},{id:"phase",label:"PHASE"},{id:"analysis",label:"ANALYSIS"}];
377
- const bifParams = [{id:"eta",label:"Ξ·"},{id:"gamma",label:"Ξ³"},{id:"beta",label:"Ξ²"},{id:"sigma",label:"Οƒ"}];
378
- const cvSize = 520;
379
-
380
- return (
381
- <div style={{minHeight:"100vh",display:"flex",flexDirection:"column"}}>
382
- {/* Scanlines */}
383
- <div style={{position:"fixed",inset:0,
384
- background:"repeating-linear-gradient(0deg,rgba(0,255,255,0.012) 0px,transparent 1px,transparent 3px)",
385
- pointerEvents:"none",zIndex:100}}/>
386
-
387
- {/* Header */}
388
- <header style={{padding:"14px 20px",borderBottom:"1px solid rgba(0,255,255,0.12)",
389
- background:"linear-gradient(180deg,rgba(0,255,255,0.03) 0%,transparent 100%)",
390
- display:"flex",alignItems:"center",justifyContent:"space-between",flexWrap:"wrap",gap:10}}>
391
- <div style={{display:"flex",alignItems:"center",gap:10}}>
392
- <div style={{width:34,height:34,borderRadius:7,
393
- background:`linear-gradient(135deg,${C}20,${M}20)`,border:`1px solid ${C}35`,
394
- display:"flex",alignItems:"center",justifyContent:"center",fontSize:16,
395
- boxShadow:`0 0 20px ${C}15`,animation:"glowPulse 3s infinite"}}>πŸ”±</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  <div>
397
- <div style={{fontSize:13,fontWeight:800,letterSpacing:3,color:C,
398
- fontFamily:"'Orbitron',monospace",textShadow:`0 0 20px ${C}50`}}>ZKAEDI PRIME</div>
399
- <div style={{fontSize:8,letterSpacing:3,color:"#444"}}>PHASE PORTRAIT VISUALIZER v1.0</div>
400
  </div>
401
  </div>
402
- <div style={{display:"flex",gap:8,alignItems:"center",flexWrap:"wrap"}}>
403
- {animating && <div style={{fontSize:9,color:M,letterSpacing:2,animation:"pulse 1s infinite",
404
- textShadow:`0 0 10px ${M}`}}>β—‰ EVOLVING t={animStep}</div>}
405
- <div style={{fontSize:8,padding:"3px 8px",borderRadius:3,
406
- background:`${C}10`,border:`1px solid ${C}25`,color:C,letterSpacing:1}}>
407
- {res}Γ—{res}</div>
408
- <div style={{fontSize:8,padding:"3px 8px",borderRadius:3,
409
- background:`${G}10`,border:`1px solid ${G}25`,color:G,letterSpacing:1}}>
410
- {potential.replace("_"," ").toUpperCase()}</div>
411
  </div>
412
  </header>
413
 
414
- {/* Main */}
415
- <div style={{display:"flex",flex:1,padding:12,gap:12,flexWrap:"wrap",alignItems:"flex-start"}}>
416
-
417
- {/* Left: Canvas + Stats + Histogram */}
418
- <div style={{flex:"1 1 520px",display:"flex",flexDirection:"column",gap:10,maxWidth:580}}>
419
- {/* Potential selector */}
420
- <div style={{display:"flex",gap:4,flexWrap:"wrap"}}>
421
  {potentials.map(p=>(
422
  <button key={p.id} onClick={()=>setPotential(p.id)} style={{
423
- padding:"5px 10px",fontSize:9,letterSpacing:1,
424
- background:potential===p.id?`${C}18`:"rgba(255,255,255,0.02)",
425
- border:`1px solid ${potential===p.id?C+"50":"rgba(255,255,255,0.06)"}`,
426
- borderRadius:5,color:potential===p.id?C:"#666",
427
- boxShadow:potential===p.id?`0 0 10px ${C}15`:"none"
428
  }}>{p.icon} {p.label}</button>
429
  ))}
430
  </div>
431
 
432
- {/* Canvas */}
433
- <div style={{position:"relative",width:"100%",maxWidth:cvSize,aspectRatio:"1",
434
- borderRadius:8,overflow:"hidden",border:`1px solid rgba(0,255,255,0.15)`,
435
- boxShadow:`0 0 50px rgba(0,255,255,0.06),inset 0 0 80px rgba(0,0,0,0.6)`}}>
436
- <canvas ref={canvasRef} width={cvSize} height={cvSize}
437
- style={{width:"100%",height:"100%",display:"block"}} />
438
- <canvas ref={trajRef} width={cvSize} height={cvSize}
439
- style={{position:"absolute",top:0,left:0,width:"100%",height:"100%"}} />
440
- {/* Scan line */}
441
- {animating && <div style={{position:"absolute",left:0,right:0,height:2,
442
- background:`linear-gradient(90deg,transparent,${C}60,transparent)`,
443
- animation:"scan 2s linear infinite",pointerEvents:"none"}} />}
444
- {/* Corners */}
445
- {["top-left","top-right","bottom-left","bottom-right"].map(c=>{
446
- const [v,h]=c.split("-");
447
- return <div key={c} style={{position:"absolute",[v]:0,[h]:0,width:18,height:18,
448
- [`border${v==="top"?"Top":"Bottom"}`]:`2px solid ${C}50`,
449
- [`border${h==="left"?"Left":"Right"}`]:`2px solid ${C}50`}} />;
450
- })}
451
- <div style={{position:"absolute",bottom:5,left:"50%",transform:"translateX(-50%)",
452
- fontSize:8,color:`${C}50`,letterSpacing:2}}>X β†’</div>
453
- <div style={{position:"absolute",left:5,top:"50%",transform:"translateY(-50%) rotate(-90deg)",
454
- fontSize:8,color:`${C}50`,letterSpacing:2}}>Y β†’</div>
 
 
 
 
 
 
 
 
 
 
 
 
455
  </div>
456
 
457
- {/* Stats row */}
458
- <div style={{display:"flex",gap:5,flexWrap:"wrap"}}>
459
- <Stat label="Min E" value={stats.min.toFixed(2)} color={C} />
460
- <Stat label="Max E" value={stats.max.toFixed(2)} color={M} />
461
- <Stat label="ΞΌ" value={stats.mean.toFixed(2)} />
462
- <Stat label="σ²" value={stats.variance.toFixed(2)} color={A} />
463
- <Stat label="S" value={stats.entropy.toFixed(2)} color={V} />
 
464
  </div>
465
 
466
- {/* Energy Histogram */}
467
- <Panel>
468
- <PanelHeader>⬑ Energy Distribution</PanelHeader>
469
- <Histogram histogram={stats.histogram} bins={stats.bins} colorMode={colorMode} />
470
- <div style={{display:"flex",justifyContent:"space-between",marginTop:4}}>
471
- <span style={{fontSize:8,color:"#444"}}>{stats.min.toFixed(1)}</span>
472
- <span style={{fontSize:8,color:"#444"}}>Energy β†’</span>
473
- <span style={{fontSize:8,color:"#444"}}>{stats.max.toFixed(1)}</span>
474
  </div>
475
  </Panel>
 
 
 
 
476
  </div>
477
 
478
- {/* Right: Controls */}
479
- <div style={{flex:"0 0 280px",minWidth:250,display:"flex",flexDirection:"column",gap:10}}>
480
- {/* Tabs */}
481
- <div style={{display:"flex",gap:2,background:"var(--panel)",borderRadius:6,padding:2,
482
- border:"1px solid var(--border)"}}>
483
  {tabs.map(t=>(
484
  <button key={t.id} onClick={()=>setActiveTab(t.id)} style={{
485
- flex:1,padding:"7px 0",fontSize:9,letterSpacing:1,borderRadius:4,
486
- background:activeTab===t.id?`${C}15`:"transparent",
487
- border:activeTab===t.id?`1px solid ${C}30`:"1px solid transparent",
488
- color:activeTab===t.id?C:"#555"
489
  }}>{t.label}</button>
490
  ))}
491
  </div>
492
 
493
- {/* DYNAMICS TAB */}
494
- {activeTab==="params" && <>
495
- <Panel>
496
- <PanelHeader>⟁ Hamiltonian Coupling</PanelHeader>
497
- <Slider label="Ξ· Recursive Coupling" value={params.eta} min={0} max={1} step={0.01} onChange={up("eta")} />
498
- <Slider label="Ξ³ Attractor Sharpening" value={params.gamma} min={0} max={2} step={0.01} onChange={up("gamma")} color={M} />
499
- <Slider label="Ξ² Noise Amplitude" value={params.beta} min={0} max={0.5} step={0.01} onChange={up("beta")} color={A} />
500
- <Slider label="Οƒ Exploration" value={params.sigma} min={0} max={0.3} step={0.01} onChange={up("sigma")} color={V} />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
501
  </Panel>
502
- <Panel>
503
- <PanelHeader>⟁ Evolution</PanelHeader>
504
- <Slider label="Iterations" value={params.iterations} min={1} max={50} step={1} onChange={up("iterations")} />
505
- <Slider label="Seed" value={params.seed} min={1} max={9999} step={1} onChange={up("seed")} color={V} />
506
  </Panel>
507
  </>}
508
 
509
- {/* PHASE TAB */}
510
- {activeTab==="phase" && <>
511
- <Panel>
512
- <PanelHeader>⟁ Trajectory Config</PanelHeader>
513
- <Slider label="Trajectory Count" value={params.trajectoryCount} min={0} max={80} step={1} onChange={up("trajectoryCount")} />
514
- <Slider label="Trajectory Length" value={params.trajectoryLength} min={10} max={1000} step={10} onChange={up("trajectoryLength")} color={M} />
515
  </Panel>
516
- <Panel>
517
- <PanelHeader>⟁ Visualization</PanelHeader>
518
- <div style={{display:"flex",gap:4,marginBottom:10}}>
519
  {colors.map(c=>(
520
  <button key={c.id} onClick={()=>setColorMode(c.id)} style={{
521
- flex:1,padding:"6px 0",fontSize:9,letterSpacing:1,borderRadius:4,
522
- background:colorMode===c.id?`${C}18`:"rgba(255,255,255,0.02)",
523
- border:`1px solid ${colorMode===c.id?C+"50":"var(--border)"}`,
524
- color:colorMode===c.id?C:"#666"
525
- }}>{c.label}</button>
526
  ))}
527
  </div>
528
- <div style={{display:"flex",gap:6}}>
529
- <button onClick={()=>setShowTrails(v=>!v)} style={{
530
- flex:1,padding:"7px 0",fontSize:9,letterSpacing:1,borderRadius:5,
531
- background:showTrails?`${C}12`:"rgba(255,255,255,0.02)",
532
- border:`1px solid ${showTrails?C+"35":"var(--border)"}`,
533
- color:showTrails?C:"#555"
534
- }}>{showTrails?"β—‰":"β—‹"} TRAILS</button>
535
- <button onClick={()=>setShowGrid(v=>!v)} style={{
536
- flex:1,padding:"7px 0",fontSize:9,letterSpacing:1,borderRadius:5,
537
- background:showGrid?`${C}12`:"rgba(255,255,255,0.02)",
538
- border:`1px solid ${showGrid?C+"35":"var(--border)"}`,
539
- color:showGrid?C:"#555"
540
- }}>{showGrid?"β—‰":"β—‹"} GRID</button>
541
  </div>
542
  </Panel>
543
  </>}
544
 
545
- {/* ANALYSIS TAB */}
546
- {activeTab==="analysis" && <>
547
- <Panel>
548
- <PanelHeader>⟁ Bifurcation Diagram</PanelHeader>
549
- <div style={{display:"flex",gap:3,marginBottom:8}}>
550
  {bifParams.map(b=>(
551
- <button key={b.id} onClick={()=>setBifParam(b.id)} style={{
552
- flex:1,padding:"5px 0",fontSize:10,borderRadius:4,
553
- background:bifParam===b.id?`${C}18`:"rgba(255,255,255,0.02)",
554
- border:`1px solid ${bifParam===b.id?C+"50":"var(--border)"}`,
555
- color:bifParam===b.id?C:"#666"
556
- }}>{b.label}</button>
557
  ))}
558
  </div>
559
- <BifurcationDiagram potential={potential} params={params} paramKey={bifParam} />
560
- <div style={{fontSize:8,color:"#444",marginTop:4,textAlign:"center"}}>
561
- Energy samples vs {bifParam} sweep
 
 
 
 
562
  </div>
563
  </Panel>
564
- <Panel>
565
- <PanelHeader>⟁ Field Metrics</PanelHeader>
566
- <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:6}}>
567
- <div style={{background:"rgba(0,255,255,0.04)",borderRadius:5,padding:8,textAlign:"center"}}>
568
- <div style={{fontSize:8,color:"#555",letterSpacing:1}}>RANGE</div>
569
- <div style={{fontSize:12,color:C,fontWeight:700,marginTop:2}}>
570
- {(stats.max-stats.min).toFixed(2)}
571
- </div>
572
- </div>
573
- <div style={{background:"rgba(255,0,255,0.04)",borderRadius:5,padding:8,textAlign:"center"}}>
574
- <div style={{fontSize:8,color:"#555",letterSpacing:1}}>STD DEV</div>
575
- <div style={{fontSize:12,color:M,fontWeight:700,marginTop:2}}>
576
- {Math.sqrt(Math.max(0,stats.variance)).toFixed(2)}
577
  </div>
578
- </div>
579
- <div style={{background:"rgba(139,92,246,0.04)",borderRadius:5,padding:8,textAlign:"center"}}>
580
- <div style={{fontSize:8,color:"#555",letterSpacing:1}}>ENTROPY</div>
581
- <div style={{fontSize:12,color:V,fontWeight:700,marginTop:2}}>{stats.entropy.toFixed(3)}</div>
582
- </div>
583
- <div style={{background:"rgba(245,158,11,0.04)",borderRadius:5,padding:8,textAlign:"center"}}>
584
- <div style={{fontSize:8,color:"#555",letterSpacing:1}}>CV</div>
585
- <div style={{fontSize:12,color:A,fontWeight:700,marginTop:2}}>
586
- {stats.mean !== 0 ? (Math.sqrt(Math.max(0,stats.variance))/Math.abs(stats.mean)).toFixed(3) : "β€”"}
587
- </div>
588
- </div>
589
  </div>
590
  </Panel>
591
- <Panel>
592
- <PanelHeader>⟁ Equation</PanelHeader>
593
- <div style={{fontSize:9,color:"#888",lineHeight:1.7,padding:"4px 0"}}>
594
- <span style={{color:C}}>H<sub>t</sub></span>(x,y) = H<sub>0</sub>(x,y) +
595
- <span style={{color:C}}> Ξ·</span>Β·H<sub>t-1</sub>Β·<span style={{color:M}}>Οƒ</span>(
596
- <span style={{color:M}}>Ξ³</span>Β·H<sub>t-1</sub>) +
597
- <span style={{color:V}}> Ξ΅</span>·𝒩(0, 1+<span style={{color:A}}>Ξ²</span>|H<sub>t-1</sub>|)
598
  </div>
599
  </Panel>
600
  </>}
601
 
602
- {/* Action Buttons β€” always visible */}
603
- <div style={{display:"flex",flexDirection:"column",gap:6,marginTop:4}}>
604
- <button onClick={()=>{setAnimStep(0);setAnimating(true);}} disabled={animating} style={{
605
- padding:"11px 0",fontSize:11,fontWeight:800,letterSpacing:3,
606
- background:animating?"rgba(255,255,255,0.03)":`linear-gradient(135deg,${C}20,${M}20)`,
607
- border:`1px solid ${animating?"rgba(255,255,255,0.08)":C+"45"}`,
608
- borderRadius:7,color:animating?"#555":C,
609
- cursor:animating?"not-allowed":"pointer",
610
- boxShadow:animating?"none":`0 0 20px ${C}12`,
611
- textTransform:"uppercase"
612
- }}>{animating?`β—Ž EVOLVING t=${animStep}…`:"β–Ά EVOLVE FIELD"}</button>
613
-
614
- <div style={{display:"flex",gap:6}}>
615
- <button onClick={()=>up("seed")(Math.floor(Math.random()*9999))} style={{
616
- flex:1,padding:"9px 0",fontSize:10,letterSpacing:1,
617
- background:`${M}08`,border:`1px solid ${M}25`,borderRadius:6,color:M,
618
- textTransform:"uppercase"
619
- }}>⟳ RANDOMIZE</button>
620
- <button onClick={exportPNG} style={{
621
- flex:1,padding:"9px 0",fontSize:10,letterSpacing:1,
622
- background:`${G}08`,border:`1px solid ${G}25`,borderRadius:6,color:G,
623
- textTransform:"uppercase"
624
- }}>– EXPORT PNG</button>
625
  </div>
626
  </div>
627
 
628
- {/* Footer */}
629
- <div style={{marginTop:"auto",paddingTop:10,borderTop:"1px solid var(--border)",
630
- fontSize:7,color:"#2a2a2a",letterSpacing:2,textAlign:"center",lineHeight:1.8}}>
 
 
 
 
 
 
 
 
 
 
 
631
  ZKAEDI PRIME FRAMEWORK<br/>
632
- RECURSIVE HAMILTONIAN DYNAMICS<br/>
633
- <span style={{color:`${C}30`}}>PUBLISH THE OUTPUT</span>
634
- {" Β· "}
635
- <span style={{color:`${M}30`}}>PROTECT THE ENGINE</span>
636
  </div>
637
  </div>
638
  </div>
 
 
639
  </div>
640
  );
641
  }
642
 
643
- ReactDOM.createRoot(document.getElementById("root")).render(<App />);
644
  </script>
645
  </body>
646
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8"/>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1.0"/>
6
  <title>πŸ”± ZKAEDI PRIME β€” Phase Portrait Visualizer</title>
7
+ <meta name="description" content="Interactive visualization of recursively coupled Hamiltonian dynamics with phase portraits, bifurcation diagrams, and Lyapunov analysis."/>
8
+ <link rel="preconnect" href="https://fonts.googleapis.com"/>
9
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;700;800&family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet"/>
10
  <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
11
  <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
12
  <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.9/babel.min.js"></script>
13
  <style>
14
+ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
15
+ :root{--c:#00ffff;--m:#ff00ff;--dk:#06060c;--pn:rgba(255,255,255,0.02);--bd:rgba(255,255,255,0.07);--v:#8b5cf6;--g:#10b981;--a:#f59e0b;--r:#ef4444}
16
+ html,body,#root{height:100%;background:var(--dk);color:#c0c0c0;overflow-x:hidden}
17
+ body{font-family:'JetBrains Mono',monospace;font-size:12px}
18
+ ::-webkit-scrollbar{width:5px}::-webkit-scrollbar-track{background:var(--dk)}::-webkit-scrollbar-thumb{background:rgba(0,255,255,0.2);border-radius:3px}
19
+ @keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
20
+ @keyframes scan{0%{top:-2px}100%{top:100%}}
21
+ @keyframes glowPulse{0%,100%{box-shadow:0 0 15px rgba(0,255,255,.1)}50%{box-shadow:0 0 30px rgba(0,255,255,.25)}}
22
+ @keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
23
+ @keyframes recording{0%,100%{opacity:1}50%{opacity:.3}}
24
+ button{font-family:inherit;cursor:pointer;transition:all .2s}
25
+ button:hover{filter:brightness(1.3)}button:active{transform:scale(.97)}
26
+ .toast{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);padding:10px 20px;border-radius:6px;background:rgba(0,255,255,.15);border:1px solid rgba(0,255,255,.3);color:var(--c);font-size:11px;letter-spacing:1px;z-index:200;animation:fadeIn .3s;backdrop-filter:blur(10px)}
27
+ .kbd{display:inline-block;padding:1px 5px;border-radius:3px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.12);font-size:9px;color:#888;margin:0 2px;font-family:inherit}
28
+ .fullscreen-host{position:fixed;inset:0;z-index:300;background:var(--dk);overflow:auto}
 
 
29
  </style>
30
  </head>
31
  <body>
32
  <div id="root"></div>
33
  <script type="text/babel">
34
+ const{useState,useEffect,useRef,useCallback}=React;
 
 
 
 
 
35
 
36
+ const C="#00ffff",M="#ff00ff",V="#8b5cf6",G="#10b981",A="#f59e0b",R="#ef4444";
37
+ const sigmoid=x=>1/(1+Math.exp(-Math.max(-20,Math.min(20,x))));
38
 
39
+ class RNG{
40
+ constructor(s){this.s=s|0}
41
+ next(){this.s=(this.s*1664525+1013904223)&0xffffffff;return(this.s>>>0)/0xffffffff}
42
+ gauss(){const u=this.next()||1e-4,v=this.next();return Math.sqrt(-2*Math.log(u))*Math.cos(2*Math.PI*v)}
 
 
43
  }
44
 
45
+ function genField(res,p){
46
+ const{eta,gamma,beta,sigma,iterations,seed,potential}=p;
47
+ const rng=new RNG(seed),n=res,H=new Float32Array(n*n),Hb=new Float32Array(n*n);
48
+ for(let i=0;i<n;i++)for(let j=0;j<n;j++){
49
+ const x=(i/n-.5)*6,y=(j/n-.5)*6;let v=0;
50
+ switch(potential){
51
+ case"double_well":v=(x*x-1)*(x*x-1)+y*y;break;
52
+ case"spiral":{const r=Math.sqrt(x*x+y*y),th=Math.atan2(y,x);v=r*r-3*Math.cos(th*3+r*2);break}
53
+ case"chaotic":v=Math.sin(x*2)*Math.cos(y*3)+Math.sin(x*y)*2;break;
54
+ case"crystal":v=Math.cos(x*Math.PI)+Math.cos(y*Math.PI)+.5*Math.cos((x+y)*Math.PI)+.5*Math.cos((x-y)*Math.PI);break;
55
+ case"volcano":{const r2=x*x+y*y;v=-Math.exp(-r2*.3)*4+Math.exp(-((r2-3)*(r2-3))*.5)*2;break}
56
+ case"soliton":v=1/Math.cosh(x)*1/Math.cosh(y)+Math.sin(x*y*.5);break;
57
+ default:{const r2=x*x+y*y;v=(r2-4)*(r2-4)*.1-Math.exp(-r2*.5)*3}
 
 
 
58
  }
59
+ Hb[i*n+j]=v;H[i*n+j]=v;
60
  }
61
+ for(let t=0;t<iterations;t++){
62
+ for(let idx=0;idx<n*n;idx++){
63
+ const sig=sigmoid(gamma*H[idx]);
64
+ const noise=rng.gauss()*(1+beta*Math.abs(H[idx]));
65
+ H[idx]=Hb[idx]+eta*H[idx]*sig+sigma*noise;
66
  }
67
  }
68
+ return{H,Hb,size:n};
69
  }
70
 
71
+ function fieldGrad(H,n,i,j){
72
+ const g=(ci,cj)=>H[Math.max(0,Math.min(n-1,ci))*n+Math.max(0,Math.min(n-1,cj))];
73
+ return[(g(i+1,j)-g(i-1,j))*.5,(g(i,j+1)-g(i,j-1))*.5];
74
  }
75
 
76
+ function trace(H,n,si,sj,steps,dt){
77
+ const pts=[];let fi=si,fj=sj;
78
+ for(let s=0;s<steps;s++){
79
+ if(fi<1||fi>=n-1||fj<1||fj>=n-1)break;
80
+ pts.push([fi,fj]);
81
+ const[dx,dy]=fieldGrad(H,n,Math.floor(fi),Math.floor(fj));
82
+ const mag=Math.sqrt(dx*dx+dy*dy)+1e-8;
83
+ fi-=(dy/mag)*dt;fj+=(dx/mag)*dt;
 
84
  }
85
  return pts;
86
  }
87
 
88
+ function eColor(val,mn,mx,mode){
89
+ const t=Math.max(0,Math.min(1,(val-mn)/(mx-mn+1e-10)));
90
+ if(mode==="cyan_magenta")return[Math.floor(t*255),Math.floor((1-t)*255),255];
91
+ if(mode==="plasma")return[Math.floor(Math.min(255,t*510)),Math.floor(Math.max(0,(t-.3)*400)),Math.floor((1-t)*255)];
92
+ if(mode==="thermal")return[Math.floor(t*255),Math.floor(t<.5?t*2*200:200-(t-.5)*2*200),Math.floor((1-t)*100)];
93
+ return[Math.floor(t*255),Math.floor(t*t*180),Math.floor((1-t*t)*200+t*55)];
94
  }
95
 
96
+ function computeStats(H){
97
+ let mn=Infinity,mx=-Infinity,sum=0,sq=0;
98
+ for(let i=0;i<H.length;i++){if(H[i]<mn)mn=H[i];if(H[i]>mx)mx=H[i];sum+=H[i];sq+=H[i]*H[i]}
99
+ const mean=sum/H.length,vari=sq/H.length-mean*mean;
100
+ const bins=60,hist=new Float32Array(bins);
101
+ for(let i=0;i<H.length;i++){const b=Math.floor(((H[i]-mn)/(mx-mn+1e-10))*(bins-1));hist[b]++}
102
+ let ent=0;for(let b=0;b<bins;b++){const p=hist[b]/H.length;if(p>0)ent-=p*Math.log2(p)}
103
+ const std=Math.sqrt(Math.max(0,vari));
104
+ let m3=0,m4=0;
105
+ if(std>1e-10){for(let i=0;i<H.length;i++){const d=(H[i]-mean)/std;m3+=d*d*d;m4+=d*d*d*d}m3/=H.length;m4/=H.length}
106
+ return{min:mn,max:mx,mean,variance:vari,entropy:ent,histogram:hist,bins,skewness:m3,kurtosis:m4-3};
107
  }
108
 
109
+ // URL State encode/decode
110
+ function encodeState(params,potential,colorMode){
111
+ const s={e:params.eta,g:params.gamma,b:params.beta,s:params.sigma,i:params.iterations,
112
+ d:params.seed,t:params.trajectoryCount,l:params.trajectoryLength,p:potential,c:colorMode};
113
+ return btoa(JSON.stringify(s));
114
+ }
115
+ function decodeState(hash){
116
+ try{const s=JSON.parse(atob(hash));
117
+ return{params:{eta:s.e,gamma:s.g,beta:s.b,sigma:s.s,iterations:s.i,seed:s.d,
118
+ trajectoryCount:s.t,trajectoryLength:s.l},potential:s.p,colorMode:s.c}}catch(e){return null}
 
 
 
 
 
 
 
 
119
  }
120
 
121
+ const PRESETS=[
122
+ {name:"Quantum Tunneling",icon:"\u269B",pot:"double_well",eta:.45,gamma:1.2,beta:.15,sigma:.08,iter:20,seed:777,desc:"Probability leaking between wells"},
123
+ {name:"Spiral Galaxy",icon:"\uD83C\uDF00",pot:"spiral",eta:.6,gamma:.4,beta:.05,sigma:.12,iter:25,seed:2024,desc:"Galactic arm formation"},
124
+ {name:"Chaos Edge",icon:"\u26A1",pot:"chaotic",eta:.8,gamma:1.8,beta:.3,sigma:.15,iter:30,seed:42,desc:"Right at the bifurcation boundary"},
125
+ {name:"Crystal Growth",icon:"\uD83D\uDC8E",pot:"crystal",eta:.35,gamma:.5,beta:.02,sigma:.03,iter:15,seed:1337,desc:"Self-organizing lattice"},
126
+ {name:"Calm Sea",icon:"\uD83C\uDF0A",pot:"mexican_hat",eta:.15,gamma:.1,beta:.01,sigma:.01,iter:5,seed:100,desc:"Pure potential, minimal noise"},
127
+ {name:"Volcanic Eruption",icon:"\uD83C\uDF0B",pot:"volcano",eta:.7,gamma:1.5,beta:.25,sigma:.2,iter:35,seed:666,desc:"Catastrophic energy release"},
128
+ {name:"Soliton Wave",icon:"\u3030",pot:"soliton",eta:.5,gamma:.6,beta:.08,sigma:.06,iter:18,seed:314,desc:"Standing wave interference"},
129
+ {name:"Deep Noise",icon:"\uD83D\uDCE1",pot:"chaotic",eta:.3,gamma:.2,beta:.4,sigma:.25,iter:40,seed:999,desc:"Noise-dominated exploration"},
130
+ {name:"Phase Lock",icon:"\uD83D\uDD12",pot:"double_well",eta:.9,gamma:2.0,beta:.01,sigma:.01,iter:50,seed:555,desc:"Extreme attractor sharpening"},
131
+ {name:"Emergence",icon:"\uD83E\uDDEC",pot:"crystal",eta:.55,gamma:.8,beta:.12,sigma:.1,iter:22,seed:1618,desc:"Order from recursive feedback"},
132
+ ];
133
+
134
+ function Slider({label,value,min,max,step,onChange,color}){
135
+ const pct=((value-min)/(max-min))*100;
136
+ return(<div style={{marginBottom:7}}>
137
+ <div style={{display:"flex",justifyContent:"space-between",marginBottom:2}}>
138
+ <span style={{color:color||C,fontSize:9,letterSpacing:1,textTransform:"uppercase"}}>{label}</span>
139
+ <span style={{color:"#fff",fontSize:9}}>{typeof value==="number"?value.toFixed(step<1?2:0):value}</span>
140
  </div>
141
+ <div style={{position:"relative",height:4,background:"rgba(255,255,255,.05)",borderRadius:3}}>
142
+ <div style={{position:"absolute",left:0,top:0,height:"100%",width:`${pct}%`,borderRadius:3,
143
+ background:`linear-gradient(90deg,${color||C},${M})`,boxShadow:`0 0 6px ${(color||C)}20`}}/>
144
+ <input type="range" min={min} max={max} step={step} value={value}
145
+ onChange={e=>onChange(parseFloat(e.target.value))}
146
+ style={{position:"absolute",top:-8,left:0,width:"100%",height:20,opacity:0,cursor:"pointer",margin:0}}/>
147
+ </div>
148
+ </div>);
149
  }
150
 
151
+ function Stat({label,value,color}){
152
+ return(<div style={{background:"var(--pn)",border:"1px solid var(--bd)",borderRadius:5,padding:"5px 8px",flex:1,minWidth:55}}>
153
+ <div style={{fontSize:7,color:"#555",letterSpacing:1,textTransform:"uppercase"}}>{label}</div>
154
+ <div style={{fontSize:12,color:color||C,fontWeight:700}}>{value}</div>
155
+ </div>);
156
  }
157
 
158
+ function Panel({children,title,style:s}){
159
+ return(<div style={{background:"var(--pn)",border:"1px solid var(--bd)",borderRadius:8,padding:12,...s}}>
160
+ {title&&<div style={{fontSize:8,color:"#555",letterSpacing:2,marginBottom:8,textTransform:"uppercase",
161
+ borderBottom:"1px solid var(--bd)",paddingBottom:5}}>{title}</div>}
162
+ {children}
163
+ </div>);
164
  }
165
 
166
+ function Histogram({histogram,bins,colorMode}){
167
+ if(!histogram)return null;
168
+ const maxVal=Math.max(...histogram);const w=260,h=65;
169
+ return(<svg width="100%" viewBox={`0 0 ${w} ${h}`} style={{display:"block"}}>
170
+ {Array.from({length:bins},(_,i)=>{
171
+ const bh=(histogram[i]/maxVal)*h*.88;const t=i/bins;
172
+ const[r,g,b]=eColor(t,0,1,colorMode);
173
+ return<rect key={i} x={i*(w/bins)} y={h-bh} width={w/bins-.4} height={bh} fill={`rgb(${r},${g},${b})`} opacity={.7}/>;
174
+ })}
175
+ </svg>);
 
 
 
 
 
 
 
176
  }
177
 
178
+ function BifurcationDiagram({potential,params,paramKey}){
179
+ const cvRef=useRef(null);const w=260,h=100;
180
+ useEffect(()=>{
181
+ const cv=cvRef.current;if(!cv)return;const ctx=cv.getContext("2d");
182
+ ctx.fillStyle="rgba(6,6,12,.95)";ctx.fillRect(0,0,w,h);
183
+ const samples=50,probes=6,rng=new RNG(params.seed);
184
+ const ranges={eta:[0,1],gamma:[0,2],beta:[0,.5],sigma:[0,.3]};
185
+ const[lo,hi]=ranges[paramKey]||[0,1];
186
+ let all=[];
187
+ for(let s=0;s<samples;s++){
188
+ const pv=lo+(s/samples)*(hi-lo);
189
+ const p={...params,potential,[paramKey]:pv,iterations:Math.min(params.iterations,12)};
190
+ const{H}=genField(35,p);const vals=[];
191
+ for(let k=0;k<probes;k++)vals.push(H[Math.floor(rng.next()*H.length)]);
192
+ all.push({x:s,vals});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  }
194
+ const flat=all.flatMap(a=>a.vals);const mn=Math.min(...flat),mx=Math.max(...flat);
195
+ all.forEach(({x,vals})=>vals.forEach(v=>{
196
+ const px=(x/samples)*w,py=h-((v-mn)/(mx-mn+1e-10))*h*.88-h*.05;
197
+ const t=(v-mn)/(mx-mn+1e-10);
198
+ ctx.fillStyle=`rgba(0,${Math.floor(255*(1-t))},255,.45)`;ctx.fillRect(px,py,2,2);
199
+ }));
200
+ const curPct=(params[paramKey]-lo)/(hi-lo);
201
+ ctx.strokeStyle="rgba(255,0,255,.5)";ctx.lineWidth=1;ctx.setLineDash([3,3]);
202
+ ctx.beginPath();ctx.moveTo(curPct*w,0);ctx.lineTo(curPct*w,h);ctx.stroke();ctx.setLineDash([]);
203
+ ctx.fillStyle="rgba(0,255,255,.25)";ctx.font="7px 'JetBrains Mono'";
204
+ ctx.fillText(paramKey+" \u2192",w-40,h-2);
205
+ },[potential,params,paramKey]);
206
+ return<canvas ref={cvRef} width={w} height={h} style={{width:"100%",borderRadius:4,border:"1px solid var(--bd)"}}/>;
207
+ }
208
 
209
+ function LyapunovMini({params,potential}){
210
+ const cvRef=useRef(null);const w=260,h=80;
211
+ useEffect(()=>{
212
+ const cv=cvRef.current;if(!cv)return;const ctx=cv.getContext("2d");
213
+ ctx.fillStyle="rgba(6,6,12,.95)";ctx.fillRect(0,0,w,h);
214
+ const steps=40,eps=.005;
215
+ for(let s=0;s<steps;s++){
216
+ const etaVal=s/steps;
217
+ const p1={...params,potential,eta:etaVal,iterations:Math.min(params.iterations,10)};
218
+ const p2={...params,potential,eta:etaVal+eps,iterations:Math.min(params.iterations,10)};
219
+ const f1=genField(25,p1),f2=genField(25,p2);
220
+ let diff=0;for(let i=0;i<f1.H.length;i++)diff+=(f1.H[i]-f2.H[i])*(f1.H[i]-f2.H[i]);
221
+ diff=Math.sqrt(diff/f1.H.length);
222
+ const lyap=diff>0?Math.log(diff/eps):0;
223
+ const norm=Math.max(0,Math.min(1,(lyap+5)/10));
224
+ const px=(s/steps)*w,bh=norm*h*.85;
225
+ ctx.fillStyle=lyap>0?`rgba(255,0,${Math.floor(255*(1-norm))},${.4+norm*.4})`:`rgba(0,${Math.floor(255*norm)},255,.5)`;
226
+ ctx.fillRect(px,h-bh,w/steps-.5,bh);
227
+ }
228
+ const pct=params.eta;
229
+ ctx.strokeStyle="rgba(255,255,0,.5)";ctx.lineWidth=1;ctx.setLineDash([2,2]);
230
+ ctx.beginPath();ctx.moveTo(pct*w,0);ctx.lineTo(pct*w,h);ctx.stroke();ctx.setLineDash([]);
231
+ const zeroY=h-(5/10)*h*.85;
232
+ ctx.strokeStyle="rgba(255,255,255,.12)";ctx.lineWidth=.5;ctx.setLineDash([4,4]);
233
+ ctx.beginPath();ctx.moveTo(0,zeroY);ctx.lineTo(w,zeroY);ctx.stroke();ctx.setLineDash([]);
234
+ ctx.fillStyle="rgba(255,255,255,.2)";ctx.font="7px 'JetBrains Mono'";ctx.fillText("\u03BB=0",w-28,zeroY-2);
235
+ },[params,potential]);
236
+ return<canvas ref={cvRef} width={w} height={h} style={{width:"100%",borderRadius:4,border:"1px solid var(--bd)"}}/>;
237
+ }
238
 
239
+ function Surface3D({H,size,colorMode}){
240
+ const cvRef=useRef(null);const w=260,h=150;
241
+ useEffect(()=>{
242
+ if(!H)return;const cv=cvRef.current;if(!cv)return;
243
+ const ctx=cv.getContext("2d");
244
+ ctx.fillStyle="rgba(6,6,12,.98)";ctx.fillRect(0,0,w,h);
245
+ const step=4,rows=Math.floor(size/step),cols=Math.floor(size/step);
246
+ let mn=Infinity,mx=-Infinity;
247
+ for(let i=0;i<H.length;i++){if(H[i]<mn)mn=H[i];if(H[i]>mx)mx=H[i]}
248
+ const sc=.4,ox=w/2,oy=h*.3;
249
+ const proj=(i,j,v)=>{
250
+ const x=(j-cols/2)*3*sc,z=(i-rows/2)*3*sc;
251
+ const val=(v-mn)/(mx-mn+1e-10);const yy=-val*40*sc;
252
+ return[ox+(x-z)*.866,oy+(x+z)*.5+yy,val];
253
+ };
254
+ for(let i=0;i<rows-1;i++)for(let j=0;j<cols-1;j++){
255
+ const v00=H[(i*step)*size+(j*step)];
256
+ const[x0,y0,t0]=proj(i,j,v00);
257
+ const[x1,y1]=proj(i+1,j,H[((i+1)*step)*size+(j*step)]);
258
+ const[x2,y2]=proj(i+1,j+1,H[((i+1)*step)*size+((j+1)*step)]);
259
+ const[x3,y3]=proj(i,j+1,H[(i*step)*size+((j+1)*step)]);
260
+ const[r,g,b]=eColor(t0,0,1,colorMode);
261
+ ctx.fillStyle=`rgba(${r},${g},${b},.3)`;ctx.strokeStyle=`rgba(${r},${g},${b},.12)`;ctx.lineWidth=.5;
262
+ ctx.beginPath();ctx.moveTo(x0,y0);ctx.lineTo(x1,y1);ctx.lineTo(x2,y2);ctx.lineTo(x3,y3);ctx.closePath();
263
+ ctx.fill();ctx.stroke();
264
+ }
265
+ },[H,size,colorMode]);
266
+ return<canvas ref={cvRef} width={w} height={h} style={{width:"100%",borderRadius:4,border:"1px solid var(--bd)"}}/>;
267
+ }
268
 
269
+ function Toast({message,onDone}){
270
+ useEffect(()=>{const t=setTimeout(onDone,2500);return()=>clearTimeout(t)},[onDone]);
271
+ return<div className="toast">{message}</div>;
272
  }
273
 
274
+ // ═══════════════════════════════════════
275
  // MAIN APP
276
+ // ═══════════════════════════════════════
277
+ function App(){
278
+ const canvasRef=useRef(null),trajRef=useRef(null),animRef=useRef(null),compareRef=useRef(null),autoRef=useRef(null);
279
+ const fpsRef=useRef({frames:0,last:performance.now()});
280
+
281
+ const[animating,setAnimating]=useState(false),[animStep,setAnimStep]=useState(0);
282
+ const[showTrails,setShowTrails]=useState(true),[showGrid,setShowGrid]=useState(true),[showContours,setShowContours]=useState(true);
283
+ const[colorMode,setColorMode]=useState("cyan_magenta"),[potential,setPotential]=useState("double_well");
284
+ const[bifParam,setBifParam]=useState("eta"),[activeTab,setActiveTab]=useState("presets");
285
+ const[stats,setStats]=useState({min:0,max:0,mean:0,variance:0,entropy:0,histogram:null,bins:60,skewness:0,kurtosis:0});
286
+ const[fieldData,setFieldData]=useState(null),[fieldSize,setFieldSize]=useState(0);
287
+ const[toast,setToast]=useState(null),[fps,setFps]=useState(0),[renderTime,setRenderTime]=useState(0);
288
+ const[isFullscreen,setIsFullscreen]=useState(false),[compareMode,setCompareMode]=useState(false);
289
+ const[autoDiscovery,setAutoDiscovery]=useState(false);
290
+
291
+ const[params,setParams]=useState({eta:.40,gamma:.30,beta:.10,sigma:.05,iterations:12,seed:42,trajectoryCount:24,trajectoryLength:300});
292
+ const res=180,cvSize=520;
293
+
294
+ useEffect(()=>{const hash=window.location.hash.slice(1);
295
+ if(hash){const st=decodeState(hash);if(st){setParams(st.params);setPotential(st.potential);setColorMode(st.colorMode)}}
296
+ },[]);
297
+
298
+ const showToast=useCallback(msg=>setToast(msg),[]);
299
+
300
+ const render=useCallback((overrideIter)=>{
301
+ const t0=performance.now();
302
+ const cv=canvasRef.current,tv=trajRef.current;if(!cv||!tv)return;
303
+ const ctx=cv.getContext("2d"),tctx=tv.getContext("2d"),cw=cv.width,ch=cv.height;
304
+ const p={...params,potential,iterations:overrideIter??params.iterations};
305
+ const{H,size}=genField(res,p);const st=computeStats(H);
306
+ setStats(st);setFieldData(H);setFieldSize(size);
307
+ const{min:mn,max:mx}=st;const scale=cw/size;
308
+
309
+ const img=ctx.createImageData(cw,ch);
310
+ for(let i=0;i<size;i++)for(let j=0;j<size;j++){
311
+ const[r,g,b]=eColor(H[i*size+j],mn,mx,colorMode);
312
+ const px=Math.floor(j*scale),py=Math.floor(i*scale),pw=Math.ceil(scale),ph=Math.ceil(scale);
313
+ for(let dy=0;dy<ph&&py+dy<ch;dy++)for(let dx=0;dx<pw&&px+dx<cw;dx++){
314
+ const idx=((py+dy)*cw+(px+dx))*4;img.data[idx]=r;img.data[idx+1]=g;img.data[idx+2]=b;img.data[idx+3]=220;
 
 
 
 
 
 
 
 
315
  }
316
  }
317
  ctx.putImageData(img,0,0);
318
 
319
+ if(showGrid){ctx.strokeStyle="rgba(0,255,255,.04)";ctx.lineWidth=.5;const gs=cw/12;
 
 
 
320
  for(let g=0;g<=12;g++){ctx.beginPath();ctx.moveTo(g*gs,0);ctx.lineTo(g*gs,ch);ctx.stroke();
321
+ ctx.beginPath();ctx.moveTo(0,g*gs);ctx.lineTo(cw,g*gs);ctx.stroke()}}
322
+ if(showContours){const levels=10;
323
+ for(let l=0;l<levels;l++){const th=mn+(l/levels)*(mx-mn);
324
+ for(let i=0;i<size-1;i++)for(let j=0;j<size-1;j++){
325
+ const v00=H[i*size+j],v10=H[(i+1)*size+j],v01=H[i*size+j+1];
326
+ if((v00<th)!==(v10<th)||(v00<th)!==(v01<th)){
327
+ ctx.fillStyle="rgba(255,255,255,.03)";
328
+ ctx.fillRect(Math.floor(j*scale),Math.floor(i*scale),Math.ceil(scale),Math.ceil(scale))}}}}
329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  tctx.clearRect(0,0,cw,ch);
331
+ if(showTrails){const rng=new RNG(params.seed+1000);
 
332
  for(let t=0;t<params.trajectoryCount;t++){
333
+ const si=Math.floor(rng.next()*(size-4))+2,sj=Math.floor(rng.next()*(size-4))+2;
334
+ const pts=trace(H,size,si,sj,params.trajectoryLength,.8);if(pts.length<3)continue;
 
335
  for(let pi=1;pi<pts.length;pi++){
336
+ const alpha=1-(pi/pts.length);const hue=(t/params.trajectoryCount)*60+160;
337
+ tctx.strokeStyle=`hsla(${hue},100%,70%,${alpha*.6})`;tctx.lineWidth=1.5*alpha+.3;
338
+ tctx.beginPath();tctx.moveTo(pts[pi-1][1]*scale,pts[pi-1][0]*scale);
339
+ tctx.lineTo(pts[pi][1]*scale,pts[pi][0]*scale);tctx.stroke();
 
 
 
 
340
  }
341
+ tctx.shadowBlur=4;tctx.fillStyle=C;tctx.shadowColor=C;
 
342
  tctx.beginPath();tctx.arc(pts[0][1]*scale,pts[0][0]*scale,2.5,0,Math.PI*2);tctx.fill();
343
+ if(pts.length>2){const last=pts[pts.length-1];tctx.fillStyle=M;tctx.shadowColor=M;
344
+ tctx.beginPath();tctx.arc(last[1]*scale,last[0]*scale,2,0,Math.PI*2);tctx.fill()}
 
 
 
345
  tctx.shadowBlur=0;
346
+ }}
347
+
348
+ if(compareMode&&compareRef.current){
349
+ const cctx=compareRef.current.getContext("2d");
350
+ const p2={...params,potential,iterations:1,seed:params.seed};
351
+ const f2=genField(res,p2);const st2=computeStats(f2.H);
352
+ const img2=cctx.createImageData(cw,ch);
353
+ for(let i=0;i<size;i++)for(let j=0;j<size;j++){
354
+ const[r,g,b]=eColor(f2.H[i*size+j],st2.min,st2.max,colorMode);
355
+ const px=Math.floor(j*scale),py=Math.floor(i*scale),pw=Math.ceil(scale),ph=Math.ceil(scale);
356
+ for(let dy=0;dy<ph&&py+dy<ch;dy++)for(let dx=0;dx<pw&&px+dx<cw;dx++){
357
+ const idx=((py+dy)*cw+(px+dx))*4;img2.data[idx]=r;img2.data[idx+1]=g;img2.data[idx+2]=b;img2.data[idx+3]=220;
358
+ }
359
  }
360
+ cctx.putImageData(img2,0,0);
361
  }
 
362
 
363
+ fpsRef.current.frames++;const now=performance.now();
364
+ if(now-fpsRef.current.last>=1000){setFps(fpsRef.current.frames);fpsRef.current.frames=0;fpsRef.current.last=now}
365
+ setRenderTime(Math.round(now-t0));
366
+ },[params,potential,colorMode,showTrails,showGrid,showContours,compareMode]);
367
+
368
+ useEffect(()=>{render()},[render]);
369
 
370
  useEffect(()=>{
371
+ if(!animating){if(animRef.current)cancelAnimationFrame(animRef.current);return}
372
  let step=1;const mx=50;
373
+ const go=()=>{if(step>mx){setAnimating(false);setAnimStep(0);return}
374
+ setAnimStep(step);render(step);step++;animRef.current=requestAnimationFrame(()=>setTimeout(go,45))};
375
+ go();return()=>{if(animRef.current)cancelAnimationFrame(animRef.current)};
 
 
376
  },[animating]);
377
 
378
+ useEffect(()=>{
379
+ if(!autoDiscovery){if(autoRef.current)clearInterval(autoRef.current);return}
380
+ let idx=0;
381
+ autoRef.current=setInterval(()=>{
382
+ const pr=PRESETS[idx%PRESETS.length];
383
+ setParams(p=>({...p,eta:pr.eta,gamma:pr.gamma,beta:pr.beta,sigma:pr.sigma,iterations:pr.iter,seed:pr.seed}));
384
+ setPotential(pr.pot);showToast(`${pr.icon} ${pr.name}`);idx++;
385
+ },3000);
386
+ return()=>clearInterval(autoRef.current);
387
+ },[autoDiscovery]);
388
+
389
+ useEffect(()=>{
390
+ const handler=e=>{if(e.target.tagName==="INPUT")return;
391
+ switch(e.key.toLowerCase()){
392
+ case" ":e.preventDefault();if(!animating){setAnimStep(0);setAnimating(true)}break;
393
+ case"r":setParams(p=>({...p,seed:Math.floor(Math.random()*9999)}));break;
394
+ case"f":setIsFullscreen(f=>!f);break;case"t":setShowTrails(t=>!t);break;
395
+ case"g":setShowGrid(g=>!g);break;case"s":shareState();break;case"e":exportPNG();break;
396
+ case"d":setAutoDiscovery(d=>!d);break;case"c":setCompareMode(c=>!c);break;
397
+ case"1":setActiveTab("presets");break;case"2":setActiveTab("params");break;
398
+ case"3":setActiveTab("phase");break;case"4":setActiveTab("analysis");break;
399
+ }};
400
+ window.addEventListener("keydown",handler);return()=>window.removeEventListener("keydown",handler);
401
+ },[animating]);
 
 
 
402
 
403
+ const up=key=>val=>setParams(p=>({...p,[key]:val}));
404
+
405
+ const shareState=useCallback(()=>{
406
+ const hash=encodeState(params,potential,colorMode);
407
+ const url=window.location.origin+window.location.pathname+"#"+hash;
408
+ navigator.clipboard.writeText(url).then(()=>showToast("\u2197 Link copied!"))
409
+ .catch(()=>{window.location.hash=hash;showToast("URL updated")});
410
+ },[params,potential,colorMode]);
411
+
412
+ const exportPNG=useCallback(()=>{
413
+ const cv=canvasRef.current,tv=trajRef.current;if(!cv)return;
414
+ const exp=document.createElement("canvas");exp.width=cv.width;exp.height=cv.height;
415
+ const ectx=exp.getContext("2d");ectx.drawImage(cv,0,0);if(tv)ectx.drawImage(tv,0,0);
416
+ ectx.fillStyle="rgba(0,255,255,.35)";ectx.font="bold 9px 'JetBrains Mono'";
417
+ ectx.fillText("ZKAEDI PRIME",8,cv.height-8);
418
+ const link=document.createElement("a");link.download=`zkaedi_${potential}_s${params.seed}.png`;
419
+ link.href=exp.toDataURL("image/png");link.click();showToast("\u2913 PNG exported!");
420
+ },[potential,params.seed]);
421
+
422
+ const applyPreset=useCallback(pr=>{
423
+ setParams(p=>({...p,eta:pr.eta,gamma:pr.gamma,beta:pr.beta,sigma:pr.sigma,iterations:pr.iter,seed:pr.seed}));
424
+ setPotential(pr.pot);showToast(`${pr.icon} ${pr.name}`);
425
+ },[]);
426
+
427
+ const potentials=[
428
+ {id:"double_well",label:"DOUBLE WELL",icon:"\u2297"},{id:"spiral",label:"SPIRAL",icon:"\uD83C\uDF00"},
429
+ {id:"chaotic",label:"CHAOTIC",icon:"\u26A1"},{id:"crystal",label:"CRYSTAL",icon:"\uD83D\uDC8E"},
430
+ {id:"mexican_hat",label:"SOMBRERO",icon:"\uD83C\uDFA9"},{id:"volcano",label:"VOLCANO",icon:"\uD83C\uDF0B"},
431
+ {id:"soliton",label:"SOLITON",icon:"\u3030"},
432
+ ];
433
+ const colors=[{id:"cyan_magenta",label:"PRIME"},{id:"plasma",label:"PLASMA"},{id:"inferno",label:"INFERNO"},{id:"thermal",label:"THERMAL"}];
434
+ const tabs=[{id:"presets",label:"\u2605 PRESETS"},{id:"params",label:"DYNAMICS"},{id:"phase",label:"PHASE"},{id:"analysis",label:"ANALYSIS"}];
435
+ const bifParams=[{id:"eta",label:"\u03B7"},{id:"gamma",label:"\u03B3"},{id:"beta",label:"\u03B2"},{id:"sigma",label:"\u03C3"}];
436
+
437
+ return(
438
+ <div className={isFullscreen?"fullscreen-host":""} style={{minHeight:"100vh",display:"flex",flexDirection:"column"}}>
439
+ <div style={{position:"fixed",inset:0,background:"repeating-linear-gradient(0deg,rgba(0,255,255,.01) 0px,transparent 1px,transparent 3px)",pointerEvents:"none",zIndex:100}}/>
440
+
441
+ <header style={{padding:"10px 16px",borderBottom:"1px solid rgba(0,255,255,.1)",
442
+ background:"linear-gradient(180deg,rgba(0,255,255,.03) 0%,transparent 100%)",
443
+ display:"flex",alignItems:"center",justifyContent:"space-between",flexWrap:"wrap",gap:8}}>
444
+ <div style={{display:"flex",alignItems:"center",gap:8}}>
445
+ <div style={{width:30,height:30,borderRadius:6,background:`linear-gradient(135deg,${C}20,${M}20)`,
446
+ border:`1px solid ${C}30`,display:"flex",alignItems:"center",justifyContent:"center",fontSize:14,
447
+ animation:"glowPulse 3s infinite"}}>πŸ”±</div>
448
  <div>
449
+ <div style={{fontSize:12,fontWeight:800,letterSpacing:3,color:C,fontFamily:"'Orbitron',monospace",
450
+ textShadow:`0 0 20px ${C}40`}}>ZKAEDI PRIME</div>
451
+ <div style={{fontSize:7,letterSpacing:2,color:"#444"}}>PHASE PORTRAIT VISUALIZER v2.0</div>
452
  </div>
453
  </div>
454
+ <div style={{display:"flex",gap:5,alignItems:"center",flexWrap:"wrap"}}>
455
+ {animating&&<div style={{fontSize:8,color:M,letterSpacing:2,animation:"pulse 1s infinite"}}>\u25C9 t={animStep}</div>}
456
+ {autoDiscovery&&<div style={{fontSize:8,color:A,letterSpacing:1,animation:"pulse 2s infinite"}}>\u25C9 AUTO</div>}
457
+ <div style={{fontSize:7,padding:"2px 6px",borderRadius:3,background:`${C}08`,border:`1px solid ${C}18`,color:C}}>{res}\u00D7{res}</div>
458
+ <div style={{fontSize:7,padding:"2px 6px",borderRadius:3,background:`${G}08`,border:`1px solid ${G}18`,color:G}}>{renderTime}ms</div>
459
+ <div style={{fontSize:7,padding:"2px 6px",borderRadius:3,background:`${V}08`,border:`1px solid ${V}18`,color:V}}>{fps}fps</div>
 
 
 
460
  </div>
461
  </header>
462
 
463
+ <div style={{display:"flex",flex:1,padding:10,gap:10,flexWrap:"wrap",alignItems:"flex-start"}}>
464
+ {/* LEFT */}
465
+ <div style={{flex:"1 1 520px",display:"flex",flexDirection:"column",gap:8,maxWidth:compareMode?1100:580}}>
466
+ <div style={{display:"flex",gap:3,flexWrap:"wrap"}}>
 
 
 
467
  {potentials.map(p=>(
468
  <button key={p.id} onClick={()=>setPotential(p.id)} style={{
469
+ padding:"4px 8px",fontSize:8,letterSpacing:1,
470
+ background:potential===p.id?`${C}15`:"rgba(255,255,255,.02)",
471
+ border:`1px solid ${potential===p.id?C+"45":"rgba(255,255,255,.05)"}`,
472
+ borderRadius:4,color:potential===p.id?C:"#555"
 
473
  }}>{p.icon} {p.label}</button>
474
  ))}
475
  </div>
476
 
477
+ <div style={{display:"flex",gap:8}}>
478
+ <div style={{position:"relative",width:compareMode?"50%":"100%",maxWidth:cvSize,aspectRatio:"1",
479
+ borderRadius:8,overflow:"hidden",border:`1px solid rgba(0,255,255,.12)`,
480
+ boxShadow:`0 0 40px rgba(0,255,255,.05),inset 0 0 60px rgba(0,0,0,.5)`}}>
481
+ <canvas ref={canvasRef} width={cvSize} height={cvSize} style={{width:"100%",height:"100%",display:"block"}}/>
482
+ <canvas ref={trajRef} width={cvSize} height={cvSize}
483
+ style={{position:"absolute",top:0,left:0,width:"100%",height:"100%"}}/>
484
+ {animating&&<div style={{position:"absolute",left:0,right:0,height:2,
485
+ background:`linear-gradient(90deg,transparent,${C}60,transparent)`,
486
+ animation:"scan 2s linear infinite",pointerEvents:"none"}}/>}
487
+ {["top-left","top-right","bottom-left","bottom-right"].map(c=>{
488
+ const[v,h]=c.split("-");
489
+ return<div key={c} style={{position:"absolute",[v]:0,[h]:0,width:16,height:16,
490
+ [`border${v==="top"?"Top":"Bottom"}`]:`2px solid ${C}40`,
491
+ [`border${h==="left"?"Left":"Right"}`]:`2px solid ${C}40`}}/>;
492
+ })}
493
+ <div style={{position:"absolute",top:6,left:8,fontSize:7,color:`${C}50`}}>t={params.iterations}</div>
494
+ <div style={{position:"absolute",top:6,right:8,fontSize:7,color:`${M}50`}}>EVOLVED</div>
495
+ </div>
496
+
497
+ {compareMode&&(
498
+ <div style={{position:"relative",width:"50%",maxWidth:cvSize,aspectRatio:"1",
499
+ borderRadius:8,overflow:"hidden",border:`1px solid rgba(255,0,255,.12)`,
500
+ boxShadow:`0 0 40px rgba(255,0,255,.05),inset 0 0 60px rgba(0,0,0,.5)`}}>
501
+ <canvas ref={compareRef} width={cvSize} height={cvSize} style={{width:"100%",height:"100%",display:"block"}}/>
502
+ {["top-left","top-right","bottom-left","bottom-right"].map(c=>{
503
+ const[v,h]=c.split("-");
504
+ return<div key={c} style={{position:"absolute",[v]:0,[h]:0,width:16,height:16,
505
+ [`border${v==="top"?"Top":"Bottom"}`]:`2px solid ${M}40`,
506
+ [`border${h==="left"?"Left":"Right"}`]:`2px solid ${M}40`}}/>;
507
+ })}
508
+ <div style={{position:"absolute",top:6,left:8,fontSize:7,color:`${M}50`}}>t=1</div>
509
+ <div style={{position:"absolute",top:6,right:8,fontSize:7,color:`${M}50`}}>BASE</div>
510
+ </div>
511
+ )}
512
  </div>
513
 
514
+ <div style={{display:"flex",gap:4,flexWrap:"wrap"}}>
515
+ <Stat label="Min" value={stats.min.toFixed(2)} color={C}/>
516
+ <Stat label="Max" value={stats.max.toFixed(2)} color={M}/>
517
+ <Stat label="\u03BC" value={stats.mean.toFixed(2)}/>
518
+ <Stat label="\u03C3\u00B2" value={stats.variance.toFixed(2)} color={A}/>
519
+ <Stat label="S" value={stats.entropy.toFixed(2)} color={V}/>
520
+ <Stat label="Skew" value={stats.skewness.toFixed(2)} color={G}/>
521
+ <Stat label="Kurt" value={stats.kurtosis.toFixed(2)} color={R}/>
522
  </div>
523
 
524
+ <Panel title="\u2B21 Energy Distribution">
525
+ <Histogram histogram={stats.histogram} bins={stats.bins} colorMode={colorMode}/>
526
+ <div style={{display:"flex",justifyContent:"space-between",marginTop:2}}>
527
+ <span style={{fontSize:7,color:"#444"}}>{stats.min.toFixed(1)}</span>
528
+ <span style={{fontSize:7,color:"#444"}}>{stats.max.toFixed(1)}</span>
 
 
 
529
  </div>
530
  </Panel>
531
+
532
+ <Panel title="\u25C7 Isometric Surface">
533
+ <Surface3D H={fieldData} size={fieldSize} colorMode={colorMode}/>
534
+ </Panel>
535
  </div>
536
 
537
+ {/* RIGHT */}
538
+ <div style={{flex:"0 0 275px",minWidth:245,display:"flex",flexDirection:"column",gap:8}}>
539
+ <div style={{display:"flex",gap:2,background:"var(--pn)",borderRadius:5,padding:2,border:"1px solid var(--bd)"}}>
 
 
540
  {tabs.map(t=>(
541
  <button key={t.id} onClick={()=>setActiveTab(t.id)} style={{
542
+ flex:1,padding:"6px 0",fontSize:8,letterSpacing:1,borderRadius:3,
543
+ background:activeTab===t.id?`${C}12`:"transparent",
544
+ border:activeTab===t.id?`1px solid ${C}25`:"1px solid transparent",
545
+ color:activeTab===t.id?C:"#444"
546
  }}>{t.label}</button>
547
  ))}
548
  </div>
549
 
550
+ {activeTab==="presets"&&(
551
+ <div style={{display:"flex",flexDirection:"column",gap:4,maxHeight:520,overflowY:"auto"}}>
552
+ {PRESETS.map((pr,i)=>(
553
+ <button key={i} onClick={()=>applyPreset(pr)} style={{
554
+ padding:"8px 10px",textAlign:"left",borderRadius:6,
555
+ background:"var(--pn)",border:"1px solid var(--bd)",animation:`fadeIn .3s ${i*.04}s both`
556
+ }}>
557
+ <div style={{display:"flex",alignItems:"center",gap:8}}>
558
+ <span style={{fontSize:16}}>{pr.icon}</span>
559
+ <div>
560
+ <div style={{fontSize:10,color:C,fontWeight:700,letterSpacing:1}}>{pr.name}</div>
561
+ <div style={{fontSize:8,color:"#555",marginTop:1}}>{pr.desc}</div>
562
+ <div style={{fontSize:7,color:"#333",marginTop:2}}>
563
+ {pr.pot} | \u03B7={pr.eta} \u03B3={pr.gamma} \u03B2={pr.beta} \u03C3={pr.sigma}
564
+ </div>
565
+ </div>
566
+ </div>
567
+ </button>
568
+ ))}
569
+ </div>
570
+ )}
571
+
572
+ {activeTab==="params"&&<>
573
+ <Panel title="\u27C1 Hamiltonian Coupling">
574
+ <Slider label="\u03B7 Recursive Coupling" value={params.eta} min={0} max={1} step={.01} onChange={up("eta")}/>
575
+ <Slider label="\u03B3 Attractor Sharpening" value={params.gamma} min={0} max={2} step={.01} onChange={up("gamma")} color={M}/>
576
+ <Slider label="\u03B2 Noise Amplitude" value={params.beta} min={0} max={.5} step={.01} onChange={up("beta")} color={A}/>
577
+ <Slider label="\u03C3 Exploration" value={params.sigma} min={0} max={.3} step={.01} onChange={up("sigma")} color={V}/>
578
  </Panel>
579
+ <Panel title="\u27C1 Evolution">
580
+ <Slider label="Iterations" value={params.iterations} min={1} max={50} step={1} onChange={up("iterations")}/>
581
+ <Slider label="Seed" value={params.seed} min={1} max={9999} step={1} onChange={up("seed")} color={V}/>
 
582
  </Panel>
583
  </>}
584
 
585
+ {activeTab==="phase"&&<>
586
+ <Panel title="\u27C1 Trajectories">
587
+ <Slider label="Count" value={params.trajectoryCount} min={0} max={80} step={1} onChange={up("trajectoryCount")}/>
588
+ <Slider label="Length" value={params.trajectoryLength} min={10} max={1000} step={10} onChange={up("trajectoryLength")} color={M}/>
 
 
589
  </Panel>
590
+ <Panel title="\u27C1 Visualization">
591
+ <div style={{display:"flex",gap:3,marginBottom:8,flexWrap:"wrap"}}>
 
592
  {colors.map(c=>(
593
  <button key={c.id} onClick={()=>setColorMode(c.id)} style={{
594
+ flex:1,padding:"5px 0",fontSize:8,letterSpacing:1,borderRadius:3,
595
+ background:colorMode===c.id?`${C}15`:"rgba(255,255,255,.02)",
596
+ border:`1px solid ${colorMode===c.id?C+"45":"var(--bd)"}`,
597
+ color:colorMode===c.id?C:"#555"}}>{c.label}</button>
 
598
  ))}
599
  </div>
600
+ <div style={{display:"flex",gap:4}}>
601
+ {[["TRAILS",showTrails,()=>setShowTrails(t=>!t)],
602
+ ["GRID",showGrid,()=>setShowGrid(g=>!g)],
603
+ ["ISO",showContours,()=>setShowContours(c=>!c)]
604
+ ].map(([l,v,fn])=>(
605
+ <button key={l} onClick={fn} style={{flex:1,padding:"6px 0",fontSize:8,letterSpacing:1,borderRadius:4,
606
+ background:v?`${C}10`:"rgba(255,255,255,.02)",border:`1px solid ${v?C+"30":"var(--bd)"}`,color:v?C:"#444"
607
+ }}>{v?"\u25C9":"\u25CB"} {l}</button>
608
+ ))}
 
 
 
 
609
  </div>
610
  </Panel>
611
  </>}
612
 
613
+ {activeTab==="analysis"&&<>
614
+ <Panel title="\u27C1 Bifurcation Diagram">
615
+ <div style={{display:"flex",gap:2,marginBottom:6}}>
 
 
616
  {bifParams.map(b=>(
617
+ <button key={b.id} onClick={()=>setBifParam(b.id)} style={{flex:1,padding:"4px 0",fontSize:9,borderRadius:3,
618
+ background:bifParam===b.id?`${C}15`:"rgba(255,255,255,.02)",
619
+ border:`1px solid ${bifParam===b.id?C+"45":"var(--bd)"}`,color:bifParam===b.id?C:"#555"}}>{b.label}</button>
 
 
 
620
  ))}
621
  </div>
622
+ <BifurcationDiagram potential={potential} params={params} paramKey={bifParam}/>
623
+ <div style={{fontSize:7,color:"#444",marginTop:3,textAlign:"center"}}><span style={{color:`${M}60`}}>|</span> = current value</div>
624
+ </Panel>
625
+ <Panel title="\u03BB Lyapunov Exponent vs \u03B7">
626
+ <LyapunovMini params={params} potential={potential}/>
627
+ <div style={{fontSize:7,color:"#444",marginTop:3}}>
628
+ <span style={{color:`${R}80`}}>\u25A0</span> chaotic <span style={{color:`${C}80`,marginLeft:6}}>\u25A0</span> stable <span style={{color:`${A}80`,marginLeft:6}}>|</span> current
629
  </div>
630
  </Panel>
631
+ <Panel title="\u27C1 Metrics">
632
+ <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:4}}>
633
+ {[["RANGE",(stats.max-stats.min).toFixed(2),C],["STD DEV",Math.sqrt(Math.max(0,stats.variance)).toFixed(2),M],
634
+ ["ENTROPY",stats.entropy.toFixed(3),V],["CV",stats.mean!==0?(Math.sqrt(Math.max(0,stats.variance))/Math.abs(stats.mean)).toFixed(3):"\u2014",A]
635
+ ].map(([l,v,c])=>(
636
+ <div key={l} style={{background:`${c}08`,borderRadius:4,padding:6,textAlign:"center"}}>
637
+ <div style={{fontSize:7,color:"#444",letterSpacing:1}}>{l}</div>
638
+ <div style={{fontSize:11,color:c,fontWeight:700,marginTop:1}}>{v}</div>
 
 
 
 
 
639
  </div>
640
+ ))}
 
 
 
 
 
 
 
 
 
 
641
  </div>
642
  </Panel>
643
+ <Panel title="\u27C1 Equation">
644
+ <div style={{fontSize:8,color:"#777",lineHeight:1.8}}>
645
+ <span style={{color:C}}>H<sub>t</sub></span>(x,y) = H<sub>0</sub> + <span style={{color:C}}>\u03B7</span>\u00B7H<sub>t-1</sub>\u00B7<span style={{color:M}}>\u03C3</span>(<span style={{color:M}}>\u03B3</span>\u00B7H<sub>t-1</sub>) + <span style={{color:V}}>\u03B5</span>\u00B7\uD835\uDCA9(0,1+<span style={{color:A}}>\u03B2</span>|H<sub>t-1</sub>|)
 
 
 
 
646
  </div>
647
  </Panel>
648
  </>}
649
 
650
+ <div style={{display:"flex",flexDirection:"column",gap:5,marginTop:2}}>
651
+ <button onClick={()=>{setAnimStep(0);setAnimating(true)}} disabled={animating} style={{
652
+ padding:"10px 0",fontSize:10,fontWeight:800,letterSpacing:3,
653
+ background:animating?"rgba(255,255,255,.03)":`linear-gradient(135deg,${C}18,${M}18)`,
654
+ border:`1px solid ${animating?"rgba(255,255,255,.06)":C+"40"}`,borderRadius:6,
655
+ color:animating?"#444":C,cursor:animating?"not-allowed":"pointer",textTransform:"uppercase"
656
+ }}>{animating?`\u25CE t=${animStep}\u2026`:"\u25B6 EVOLVE FIELD"}</button>
657
+
658
+ <div style={{display:"flex",gap:4}}>
659
+ <button onClick={()=>up("seed")(Math.floor(Math.random()*9999))} style={{flex:1,padding:"8px 0",fontSize:9,letterSpacing:1,background:`${M}08`,border:`1px solid ${M}22`,borderRadius:5,color:M}}>\u27F3 RANDOM</button>
660
+ <button onClick={shareState} style={{flex:1,padding:"8px 0",fontSize:9,letterSpacing:1,background:`${G}08`,border:`1px solid ${G}22`,borderRadius:5,color:G}}>\u2197 SHARE</button>
661
+ <button onClick={exportPNG} style={{flex:1,padding:"8px 0",fontSize:9,letterSpacing:1,background:`${V}08`,border:`1px solid ${V}22`,borderRadius:5,color:V}}>\u2913 PNG</button>
662
+ </div>
663
+ <div style={{display:"flex",gap:4}}>
664
+ <button onClick={()=>setAutoDiscovery(d=>!d)} style={{flex:1,padding:"8px 0",fontSize:9,letterSpacing:1,
665
+ background:autoDiscovery?`${A}15`:`${A}08`,border:`1px solid ${autoDiscovery?A+"45":A+"22"}`,borderRadius:5,
666
+ color:autoDiscovery?A:"#666"}}>{autoDiscovery?"\u25A0 STOP":"\u25B6 DISCOVER"}</button>
667
+ <button onClick={()=>setCompareMode(c=>!c)} style={{flex:1,padding:"8px 0",fontSize:9,letterSpacing:1,
668
+ background:compareMode?`${C}15`:`${C}08`,border:`1px solid ${compareMode?C+"45":C+"22"}`,borderRadius:5,
669
+ color:compareMode?C:"#666"}}>{compareMode?"\u25C9":"\u25CB"} COMPARE</button>
670
+ <button onClick={()=>setIsFullscreen(f=>!f)} style={{flex:1,padding:"8px 0",fontSize:9,letterSpacing:1,
671
+ background:"rgba(255,255,255,.03)",border:"1px solid var(--bd)",borderRadius:5,color:"#666"
672
+ }}>{isFullscreen?"\u2715 EXIT":"\u26F6 FULL"}</button>
673
  </div>
674
  </div>
675
 
676
+ <Panel title="\u2328 SHORTCUTS" style={{marginTop:2}}>
677
+ <div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:3,fontSize:8,color:"#555"}}>
678
+ {[["Space","Evolve"],["R","Random"],["S","Share"],["E","Export"],
679
+ ["F","Fullscreen"],["T","Trails"],["G","Grid"],["D","Discover"],
680
+ ["C","Compare"],["1-4","Tabs"]
681
+ ].map(([k,d])=>(
682
+ <div key={k} style={{display:"flex",alignItems:"center",gap:4}}>
683
+ <span className="kbd">{k}</span><span>{d}</span>
684
+ </div>
685
+ ))}
686
+ </div>
687
+ </Panel>
688
+
689
+ <div style={{paddingTop:6,borderTop:"1px solid var(--bd)",fontSize:7,color:"#222",letterSpacing:2,textAlign:"center",lineHeight:1.8}}>
690
  ZKAEDI PRIME FRAMEWORK<br/>
691
+ <span style={{color:`${C}25`}}>PUBLISH THE OUTPUT</span> \u00B7 <span style={{color:`${M}25`}}>PROTECT THE ENGINE</span>
 
 
 
692
  </div>
693
  </div>
694
  </div>
695
+
696
+ {toast&&<Toast message={toast} onDone={()=>setToast(null)}/>}
697
  </div>
698
  );
699
  }
700
 
701
+ ReactDOM.createRoot(document.getElementById("root")).render(<App/>);
702
  </script>
703
  </body>
704
  </html>