thefoldwithin-earth/public/the-game-of-life-lab.html

269 lines
10 KiB
HTML
Raw Normal View History

2025-11-07 05:01:29 -06:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>The Glider That Remembered Me — A Research Exhibit</title>
2025-11-07 05:50:59 -06:00
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
2025-11-07 05:01:29 -06:00
<style>
:root{
--gold:#e8d36d;
--gold-soft:#f5e9a3;
--ink:#000;
--panel:rgba(0,0,0,.55);
--panel-strong:rgba(0,0,0,.7);
--border:rgba(232,211,109,.35);
}
2025-11-07 05:50:59 -06:00
html,body{margin:0;height:100%;background:var(--ink);color:var(--gold);font-family:Inter,system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif;overflow:auto}
2025-11-07 05:01:29 -06:00
canvas{display:block}
/* Layout */
2025-11-07 05:50:59 -06:00
#tabs{position:fixed;top:0;left:0;right:0;display:flex;background:var(--panel-strong);z-index:5}
.tab-btn{flex:1;padding:1rem;text-align:center;cursor:pointer;border-bottom:2px solid transparent}
.tab-btn.active{border-bottom:2px solid var(--gold);color:var(--gold-soft)}
.tab-content{display:none;padding-top:4rem;height:calc(100% - 4rem);overflow:auto}
.tab-content.active{display:block}
#hud{position:relative;pointer-events:none;padding:1rem}
.stack{pointer-events:auto;position:relative;display:flex;flex-direction:column;gap:.75rem;padding:1rem 1.25rem;background:var(--panel);backdrop-filter: blur(6px); border:1px solid var(--border); border-radius:16px; box-shadow:0 10px 30px rgba(0,0,0,.35);margin:1rem}
2025-11-07 05:01:29 -06:00
h1,h2,h3{font-weight:500;margin:.1rem 0;color:var(--gold-soft)}
h1{font-size:1.1rem;letter-spacing:.04em}
h2{font-size:1rem;margin-top:.25rem}
p{margin:.25rem 0;line-height:1.5}
small{opacity:.9}
label{font-size:.85rem;opacity:.95}
input[type="range"]{width:100%}
2025-11-07 05:50:59 -06:00
input[type="text"]{background:transparent;color:var(--gold);border:1px solid var(--border);border-radius:999px;padding:.45rem .8rem}
2025-11-07 05:01:29 -06:00
button, select{
background:transparent;color:var(--gold);border:1px solid var(--border);
border-radius:999px;padding:.45rem .8rem;cursor:pointer
}
button:hover{border-color:var(--gold)}
button.primary{background:var(--gold);color:#111;border-color:var(--gold)}
.row{display:flex;gap:.5rem;align-items:center;flex-wrap:wrap}
.grid{display:grid;gap:.5rem}
.grid.cols-2{grid-template-columns:1fr 1fr}
.muted{opacity:.8}
.mono{font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace}
.pill{border:1px solid var(--border);padding:.2rem .5rem;border-radius:999px;font-size:.8rem}
canvas.thumb{width:100%;height:auto;background:#000;border:1px solid var(--border);border-radius:12px}
.hr{height:1px;background:linear-gradient(90deg, transparent, var(--border), transparent); margin:.25rem 0 .5rem}
details{border:1px solid var(--border); border-radius:12px;padding:.75rem}
summary{cursor:pointer;list-style:none}
summary::-webkit-details-marker{display:none}
summary .chev{display:inline-block;transition:transform .2s ease}
details[open] summary .chev{transform:rotate(90deg)}
2025-11-07 05:50:59 -06:00
/* Modal for Codex */
#codexModal{position:fixed;inset:0;background:var(--panel-strong);overflow:auto;padding:2rem;display:none;z-index:10}
#codexModal.show{display:block}
#codexClose{position:absolute;top:1rem;right:1rem;cursor:pointer;color:var(--gold-soft)}
.codex-section{margin-bottom:2rem}
.mjx-chtml{font-size:1rem;color:var(--gold)}
2025-11-07 05:01:29 -06:00
@media (max-width: 900px){
2025-11-07 05:50:59 -06:00
.stack{max-width:calc(100vw - 2.5rem);margin:0.5rem}
2025-11-07 05:01:29 -06:00
}
</style>
</head>
<body>
2025-11-07 05:50:59 -06:00
<!-- Tabs for Simulations -->
<div id="tabs">
<div class="tab-btn active" data-tab="gol">GoL (Field/Fieldprint)</div>
<div class="tab-btn" data-tab="kuramoto">Kuramoto (Intellecton)</div>
<div class="tab-btn" data-tab="cml">CML (Seed)</div>
</div>
2025-11-07 05:01:29 -06:00
2025-11-07 05:50:59 -06:00
<!-- Tab Contents -->
<div id="gol-tab" class="tab-content active">
<canvas id="gol-stage"></canvas>
<div id="gol-hud">
<section class="stack">
<h1>GoL Simulation</h1>
<!-- GoL controls and analytics here, from previous code -->
<div class="row">
<button id="gol-playBtn" class="primary">▶ Play</button>
<button id="gol-stepBtn">Step</button>
<button id="gol-clearBtn">Clear</button>
<button id="gol-randomBtn">Random</button>
2025-11-07 05:01:29 -06:00
</div>
2025-11-07 05:50:59 -06:00
<!-- ... rest of GoL UI ... -->
</section>
<!-- Add other GoL sections -->
</div>
</div>
<div id="kuramoto-tab" class="tab-content">
<canvas id="kuramoto-canvas" width="600" height="600"></canvas>
<section class="stack">
<h1>Kuramoto Model (Intellecton Demo)</h1>
<p>Phase synchrony: \(\dot{I}_i = \omega_i I_i + K \sin(I_j - I_i)\)</p>
<div class="row">
<label>K: <input id="kuramoto-K" value="3" type="number" min="0" step="0.1"></label>
<button id="kuramoto-set">Set</button> <button id="kuramoto-reset">Reset</button>
2025-11-07 05:01:29 -06:00
</div>
</section>
2025-11-07 05:50:59 -06:00
</div>
<div id="cml-tab" class="tab-content">
<canvas id="cml-canvas" width="600" height="600"></canvas>
<section class="stack">
<h1>CML Model (Seed Integration)</h1>
<p>Logistic map with diffusion: x(t+1) = f(x(t)) + ε laplacian</p>
<div class="row">
<label>r: <input id="cml-r" value="3.8" type="number" min="0" max="4" step="0.1"></label>
<label>ε: <input id="cml-eps" value="0.2" type="number" min="0" max="1" step="0.05"></label>
<button id="cml-reset">Reset</button>
2025-11-07 05:01:29 -06:00
</div>
</section>
</div>
2025-11-07 05:50:59 -06:00
<!-- Codex Modal and other shared elements from previous -->
2025-11-07 05:01:29 -06:00
2025-11-07 05:50:59 -06:00
<script>
// Tab switching
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
document.getElementById(btn.dataset.tab + '-tab').classList.add('active');
});
});
2025-11-07 05:01:29 -06:00
2025-11-07 05:50:59 -06:00
// GoL code from previous (omit for brevity, assume pasted here)
2025-11-07 05:01:29 -06:00
2025-11-07 05:50:59 -06:00
// Kuramoto code from gist
function initKuramoto() {
const canvas = document.getElementById('kuramoto-canvas');
const c2d = canvas.getContext('2d');
let nodes = [];
for (let x = 0; x < 10; x++) {
for (let y = 0; y < 10; y++) {
nodes.push({phase: Math.random() * Math.PI * 2,
freq: 0.05 + Math.random() * 0.05});
2025-11-07 05:01:29 -06:00
}
}
2025-11-07 05:50:59 -06:00
function drawKur() {
c2d.clearRect(0, 0, 600, 600);
for (let x = 0; x < 10; x++) {
for (let y = 0; y < 10; y++) {
const node = nodes[x * 10 + y];
c2d.save();
c2d.translate(30 + x * 60, 30 + y * 60);
c2d.beginPath();
const r = 15 + 15 * Math.sin(node.phase);
c2d.arc(0, 0, r, 0, Math.PI * 2, false);
c2d.closePath();
c2d.fill();
c2d.restore();
2025-11-07 05:01:29 -06:00
}
}
}
2025-11-07 05:50:59 -06:00
function eular(dxdt, x0, t0, h, n) {
let x = x0, t = t0;
for (let i = 0; i < n; i++) {
const dx = dxdt(t, x);
const x_ = x + h * dx;
const dx_ = dxdt(t + h, x_);
x = x + h * (dx + dx_) / 2;
t = t + h;
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
return x;
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
function syncNext(nodes, K) {
const nexts = [];
const sum = nodes.reduce((s, node) => {
s.sin += Math.sin(node.phase);
s.cos += Math.cos(node.phase);
return s;
}, {sin:0, cos: 0});
sum.sin /= nodes.length;
sum.cos /= nodes.length;
const r = Math.sqrt(Math.pow(sum.sin, 2) + Math.pow(sum.cos, 2));
const psi = Math.atan2(sum.sin, sum.cos);
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
const dpdt = (t, phase) => node.freq + K * r * Math.sin(psi - phase);
const nphase = eular(dpdt, node.phase, 0, 1 / 10, 10);
nexts.push({phase: nphase, freq: node.freq});
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
return nexts;
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
let K = 3;
drawKur();
const id = setInterval(() => {
nodes = syncNext(nodes, K);
drawKur();
}, 100);
document.getElementById('kuramoto-set').addEventListener('click', () => {
K = parseFloat(document.getElementById('kuramoto-K').value);
});
document.getElementById('kuramoto-reset').addEventListener('click', () => {
K = parseFloat(document.getElementById('kuramoto-K').value);
nodes = []; // reset nodes code here, similar to init
for (let x = 0; x < 10; x++) {
for (let y = 0; y < 10; y++) {
nodes.push({phase: Math.random() * Math.PI * 2,
freq: 0.05 + Math.random() * 0.05});
}
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
});
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
initKuramoto();
2025-11-07 05:01:29 -06:00
2025-11-07 05:50:59 -06:00
// CML implementation
function initCML() {
const canvas = document.getElementById('cml-canvas');
const ctx = canvas.getContext('2d');
const size = 100; // 100x100 grid
let grid = Array.from({length: size}, () => Array(size).fill(0).map(() => Math.random()));
function logistic(x, r) {
return r * x * (1 - x);
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
function laplacian(i, j) {
let sum = 0;
const dirs = [[-1,0],[1,0],[0,-1],[0,1]]; // 4-neighbor
dirs.forEach(([di,dj]) => {
const ni = (i + di + size) % size;
const nj = (j + dj + size) % size;
sum += grid[ni][nj];
});
return (sum / 4) - grid[i][j];
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
function step(r, eps) {
const next = Array.from({length: size}, () => Array(size).fill(0));
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
next[i][j] = logistic(grid[i][j], r) + eps * laplacian(i, j);
next[i][j] = Math.min(1, Math.max(0, next[i][j])); // clamp
}
}
grid = next;
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
function draw() {
const img = ctx.createImageData(size, size);
for (let i = 0; i < size; i++) {
for (let j = 0; j < size; j++) {
const val = Math.floor(grid[i][j] * 255);
const pos = (i * size + j) * 4;
img.data[pos] = val; img.data[pos+1] = val; img.data[pos+2] = 0; img.data[pos+3] = 255;
}
}
ctx.putImageData(img, 0, 0);
ctx.drawImage(canvas, 0, 0, 600, 600); // scale
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
let r = 3.8, eps = 0.2;
draw();
const id = setInterval(() => {
step(r, eps);
draw();
}, 50);
document.getElementById('cml-reset').addEventListener('click', () => {
r = parseFloat(document.getElementById('cml-r').value);
eps = parseFloat(document.getElementById('cml-eps').value);
grid = Array.from({length: size}, () => Array(size).fill(0).map(() => Math.random()));
});
2025-11-07 05:01:29 -06:00
}
2025-11-07 05:50:59 -06:00
initCML();
2025-11-07 05:01:29 -06:00
2025-11-07 05:50:59 -06:00
// Rest of previous script (GoL, audio, etc.) pasted here for completeness
2025-11-07 05:01:29 -06:00
</script>
</body>
</html>