This repository was archived by the owner on Aug 6, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
148 lines (147 loc) · 8.3 KB
/
Copy pathindex.html
File metadata and controls
148 lines (147 loc) · 8.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>The Tree and Its Shadow — Ultrametric Phase Diagram</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.7.0/p5.min.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{background:#0a0a14;overflow:hidden;font-family:'Georgia',serif}
canvas{display:block}
#story{position:fixed;top:24px;right:24px;width:300px;max-height:calc(100vh - 48px);
background:rgba(250,249,245,0.97);border-radius:14px;padding:22px;z-index:10;
box-shadow:0 8px 32px rgba(0,0,0,0.4);overflow-y:auto;
font-size:13px;line-height:1.6;color:#141413}
#story h2{font-size:18px;font-weight:400;margin-bottom:4px}
#story .byline{font-size:11px;color:#b0aea5;margin-bottom:16px}
#story .desc{font-size:12px;color:#555;margin-bottom:16px;line-height:1.7}
#story h3{font-size:14px;font-weight:600;margin:16px 0 10px}
.preset{display:block;width:100%;padding:10px 14px;margin-bottom:6px;border:1px solid #e8e6dc;
border-radius:8px;cursor:pointer;text-align:left;font-family:inherit;font-size:12px;
background:#faf9f5;transition:all 0.2s;color:#141413;line-height:1.4}
.preset:hover{background:#f0ede4;border-color:#d97757}
.preset.active{background:#d97757;color:#fff;border-color:#d97757}
.preset .reg{font-weight:600;font-size:13px}
.preset .ex{font-size:10px;color:#b0aea5;display:block;margin-top:2px}
.preset.active .ex{color:rgba(255,255,255,0.7)}
#phase{position:fixed;bottom:28px;right:28px;z-index:10;
font-size:14px;color:#b0aea5;background:rgba(10,10,20,0.85);
padding:10px 16px;border-radius:20px;border:1px solid rgba(255,255,255,0.1)}
#doi{position:fixed;bottom:28px;left:28px;z-index:10;font-size:10px;color:rgba(176,174,165,0.35)}
#doi a{color:inherit;text-decoration:none}
@media(max-width:700px){#story{top:8px;right:8px;width:260px;padding:14px;font-size:11px}
#story h2{font-size:15px}#doi{display:none}}
</style>
</head>
<body>
<div id="story">
<h2>The Tree and Its Shadow</h2>
<div class="byline">Rowan Brad Quni-Gudzinas · DOI: 10.5281/zenodo.20348370</div>
<div class="desc">
The ultrametric tree — every triangle is isosceles — appears wherever recursive distinction operates in isolation. <b style="color:#4a6faf">Blue branches</b> = tree. <b style="color:#d97757">Amber curves</b> = lateral connections. Click a preset to tour the five regimes from the paper.
</div>
<h3>Paper Regimes</h3>
<button class="preset active" data-preset="I" onclick="loadPreset('I',this)">
<span class="reg">I · Exact Tree</span>
<span class="ex">p-adic numbers, Bruhat-Tits tree, SK spin glass, perturbative RG</span>
</button>
<button class="preset" data-preset="II" onclick="loadPreset('II',this)">
<span class="reg">II · Approximate Tree</span>
<span class="ex">Metazoan phylogeny, Swadesh vocabulary, e⁺e⁻ jets at LEP</span>
</button>
<button class="preset" data-preset="III" onclick="loadPreset('III',this)">
<span class="reg">III · Tree + Lateral Edges</span>
<span class="ex">Bacteria swapping genes, languages borrowing words, LHC jets</span>
</button>
<button class="preset" data-preset="IV" onclick="loadPreset('IV',this)">
<span class="reg">IV · Network Dominance</span>
<span class="ex">Social networks, semantic memory (fire→hot→danger), spin glasses d=3</span>
</button>
<button class="preset" data-preset="V" onclick="loadPreset('V',this)">
<span class="reg">V · Flat / Non-Hierarchical</span>
<span class="ex">Peer-to-peer, distributed ledgers, thermal systems, random graphs</span>
</button>
<h3>Controls</h3>
<div style="font-size:11px;color:#b0aea5">
<span style="cursor:pointer;color:#d97757" onclick="randomize()">↻ Random seed</span>
</div>
</div>
<div id="phase">Regime I · Exact Tree · Every triangle is isosceles</div>
<div id="doi"><a href="https://doi.org/10.5281/zenodo.20348370">10.5281/zenodo.20348370</a></div>
<script>
const PRESETS = {
I: { depth:6, ratio:2, lateralProb:0.00, label:'Regime I · Exact Tree · Every triangle is isosceles' },
II: { depth:5, ratio:2, lateralProb:0.10, label:'Regime II · Approximate Tree · Metazoan phylogeny, core vocabulary' },
III: { depth:5, ratio:2, lateralProb:0.35, label:'Regime III · Tree + Lateral Edges · Bacteria HGT, borrowed words' },
IV: { depth:5, ratio:2, lateralProb:0.60, label:'Regime IV · Network Dominance · Social networks, spreading activation' },
V: { depth:4, ratio:3, lateralProb:0.85, label:'Regime V · Flat / Non-Hierarchical · Peer-to-peer, random graphs' },
};
let params = { seed:20348370, depth:6, ratio:2, lateralProb:0, growthSpeed:5 };
let treeNodes=[], treeEdges=[], lateralEdges=[];
let growthProgress=0, lateralProgress=0, currentPreset='I';
const TREE_R=[30,40,100], TREE_G=[40,80,150], TREE_B=[100,160,230];
function setup(){createCanvas(windowWidth,windowHeight);randomSeed(params.seed);noiseSeed(params.seed);resetAll()}
function draw(){
background(10,10,20);
let cx=width/2, cy=height*0.82, treeH=height*0.65, treeW=width*0.95;
let spd=params.growthSpeed/100;
growthProgress=min(growthProgress+spd,1);
if(growthProgress>=0.4)lateralProgress=min(lateralProgress+spd*0.5,1);
push();translate(cx,height*0.92);
let ls=lateralEdges.slice(0,floor(lateralProgress*lateralEdges.length));
for(let e of ls){let a=tn(e.from),b=tn(e.to);if(!a||!b)continue;
stroke(80,50,30,40);strokeWeight(0.4);line(a.x*treeW*0.5,a.y*30,b.x*treeW*0.5,b.y*30);}
pop();
push();translate(cx,cy);
let md=floor(growthProgress*params.depth);
drawTree(0,0,0,treeW,treeH,0,md);
pop();
push();translate(cx,cy);
for(let i=0;i<min(floor(lateralProgress*lateralEdges.length),lateralEdges.length);i++){
let e=lateralEdges[i],a=tn(e.from),b=tn(e.to);if(!a||!b)continue;
let mx=(a.x+b.x)/2*treeW,my=(-a.y-b.y)/2*treeH-15;
stroke(217,119,87,130);strokeWeight(1.2);noFill();
beginShape();vertex(a.x*treeW,-a.y*treeH);quadraticVertex(mx,my-8,b.x*treeW,-b.y*treeH);endShape();}
pop();
}
function drawTree(x,y,nid,tw,th,depth,md){
let n=tn(nid);if(!n||depth>md)return;
if(depth<md)for(let c of n.children){
let ch=tn(c);if(!ch)continue;
let t=depth/params.depth, r=lerp(TREE_R[0],TREE_R[2],t), g=lerp(TREE_G[0],TREE_G[2],t), b=lerp(TREE_B[0],TREE_B[2],t);
strokeWeight(map(depth,0,params.depth,2.5,0.7));stroke(r,g,b,200);
line(n.x*tw,-n.y*th,ch.x*tw,-ch.y*th);drawTree(x,y,c,tw,th,depth+1,md);}
let t=depth/params.depth;noStroke();
fill(lerp(TREE_R[0],TREE_R[2],t),lerp(TREE_G[0],TREE_G[2],t),lerp(TREE_B[0],TREE_B[2],t),220);
ellipse(n.x*tw,-n.y*th,map(depth,0,params.depth,6,2.5));
}
function tn(id){return treeNodes.find(n=>n.id===id)}
function windowResized(){resizeCanvas(windowWidth,windowHeight)}
function resetAll(){
treeNodes=[];treeEdges=[];lateralEdges=[];growthProgress=0;lateralProgress=0;
randomSeed(params.seed);noiseSeed(params.seed);
let root={id:'root',parent:null,depth:0,x:0,y:0,children:[]};treeNodes.push(root);
let queue=[root],nid=1;
for(let d=0;d<params.depth;d++){let nq=[];
for(let p of queue)for(let c=0;c<params.ratio;c++){
let ch={id:'n'+nid,parent:p.id,depth:d+1,x:0,y:0,children:[]};nid++;
p.children.push(ch.id);treeNodes.push(ch);treeEdges.push({from:p.id,to:ch.id,depth:d});nq.push(ch);}
queue=nq;}
assignPos(root,-0.95,0.95,0,params.depth);
let leaves=treeNodes.filter(n=>n.children.length===0),pairs=[];
for(let i=0;i<leaves.length;i++)for(let j=i+1;j<leaves.length;j++)pairs.push([leaves[i],leaves[j]]);
for(let i=pairs.length-1;i>0;i--){let j=floor(random(i+1));[pairs[i],pairs[j]]=[pairs[j],pairs[i]]}
for(let i=0;i<floor(pairs.length*params.lateralProb);i++)lateralEdges.push({from:pairs[i][0].id,to:pairs[i][1].id});
}
function assignPos(n,l,r,depth,md){n.x=(l+r)/2;n.y=depth/md;if(n.children.length===0)return;
let cw=(r-l)/n.children.length;for(let i=0;i<n.children.length;i++){let ch=tn(n.children[i]);if(ch)assignPos(ch,l+i*cw,l+(i+1)*cw,depth+1,md)}}
function loadPreset(p,btn){currentPreset=p;let pre=PRESETS[p];
params.depth=pre.depth;params.ratio=pre.ratio;params.lateralProb=pre.lateralProb;
document.getElementById('phase').textContent=pre.label;
document.querySelectorAll('.preset').forEach(b=>b.classList.remove('active'));
if(btn)btn.classList.add('active');resetAll();}
function randomize(){params.seed=floor(random(1,99999999));resetAll()}
</script>
</body>
</html>