Skip to content

Instantly share code, notes, and snippets.

@jmikedupont2
Created June 16, 2026 21:07
Show Gist options
  • Select an option

  • Save jmikedupont2/1251a4bbb697f69bf88f2334c263e1a2 to your computer and use it in GitHub Desktop.

Select an option

Save jmikedupont2/1251a4bbb697f69bf88f2334c263e1a2 to your computer and use it in GitHub Desktop.
The Penteract in the Monster — a 5-cube shadow
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>The Penteract in the Monster &mdash; a 5-cube shadow</title>
<style>
html,body{margin:0;height:100%;background:#05060e;color:#e9e9f3;
font-family:"Segoe UI",system-ui,Helvetica,Arial,sans-serif;overflow:hidden}
#wrap{position:fixed;inset:0}
canvas{display:block;width:100%;height:100%}
#hud{position:fixed;left:0;right:0;bottom:0;padding:14px 20px 18px;
background:linear-gradient(0deg,rgba(5,6,14,.94),rgba(5,6,14,0));
pointer-events:none}
#title{font-size:23px;font-weight:700;letter-spacing:.5px;
text-shadow:0 0 18px rgba(155,108,255,.6)}
#thm{font-family:"Cascadia Code",Consolas,monospace;font-size:12.5px;
color:#8ad7ff;margin-top:3px}
#text{font-size:14.5px;line-height:1.5;margin-top:5px;max-width:980px;
color:#cdd0e6}
#bar{position:fixed;top:0;left:0;height:3px;background:#9b6cff;width:0%;
box-shadow:0 0 10px #9b6cff}
#ctl{position:fixed;top:12px;right:16px;display:flex;gap:8px;z-index:5}
#ctl button{pointer-events:auto;background:rgba(155,108,255,.16);
color:#e9e9f3;border:1px solid rgba(155,108,255,.5);border-radius:7px;
padding:6px 11px;font-size:13px;cursor:pointer}
#ctl button:hover{background:rgba(155,108,255,.34)}
#dots{position:fixed;top:15px;left:16px;display:flex;gap:7px;z-index:5}
.dot{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.22);
cursor:pointer;pointer-events:auto;transition:all .2s}
.dot.on{background:#9b6cff;box-shadow:0 0 9px #9b6cff;transform:scale(1.25)}
#legend{position:fixed;top:42px;left:16px;font-size:12px;color:#aab;
z-index:4;line-height:1.6}
#legend b{font-family:"Cascadia Code",Consolas,monospace}
</style>
</head>
<body>
<div id="wrap"><canvas id="cv"></canvas></div>
<div id="bar"></div>
<div id="ctl">
<button id="prev">&larr; Prev</button>
<button id="play">&#10073;&#10073; Pause</button>
<button id="next">Next &rarr;</button>
</div>
<div id="dots"></div>
<div id="legend"></div>
<div id="hud">
<div id="title"></div>
<div id="thm"></div>
<div id="text"></div>
</div>
<script>
const DATA = {"axes": [2, 3, 5, 23, 47], "corners": [{"e": 0, "idx": 0, "bits": [0, 0, 0, 0, 0], "exps": [0, 0, 0, 0, 0], "deg": "1", "digits": 1}, {"e": 1, "idx": 2, "bits": [1, 0, 0, 0, 0], "exps": [2, 0, 0, 0, 0], "deg": "21,296,876", "digits": 8}, {"e": 2, "idx": 8, "bits": [0, 1, 0, 0, 0], "exps": [0, 6, 0, 0, 0], "deg": "3.617 x 10^13", "digits": 14}, {"e": 3, "idx": 168, "bits": [1, 1, 0, 0, 0], "exps": [18, 19, 0, 0, 0], "deg": "1.033 x 10^26", "digits": 27}, {"e": 4, "idx": 15, "bits": [0, 0, 1, 0, 0], "exps": [0, 0, 9, 0, 0], "deg": "8.980 x 10^15", "digits": 16}, {"e": 5, "idx": 35, "bits": [1, 0, 1, 0, 0], "exps": [3, 0, 8, 0, 0], "deg": "7.731 x 10^19", "digits": 20}, {"e": 6, "idx": 160, "bits": [0, 1, 1, 0, 0], "exps": [0, 17, 7, 0, 0], "deg": "6.432 x 10^25", "digits": 26}, {"e": 7, "idx": 150, "bits": [1, 1, 1, 0, 0], "exps": [3, 6, 7, 0, 0], "deg": "4.176 x 10^25", "digits": 26}, {"e": 8, "idx": 5, "bits": [0, 0, 0, 1, 0], "exps": [0, 0, 0, 1, 0], "deg": "19,360,062,527", "digits": 11}, {"e": 9, "idx": 4, "bits": [1, 0, 0, 1, 0], "exps": [2, 0, 0, 1, 0], "deg": "18,538,750,076", "digits": 11}, {"e": 10, "idx": 25, "bits": [0, 1, 0, 1, 0], "exps": [0, 19, 0, 1, 0], "deg": "3.503 x 10^18", "digits": 19}, {"e": 11, "idx": 43, "bits": [1, 1, 0, 1, 0], "exps": [1, 12, 0, 1, 0], "deg": "6.405 x 10^20", "digits": 21}, {"e": 12, "idx": 18, "bits": [0, 0, 1, 1, 0], "exps": [0, 0, 2, 1, 0], "deg": "3.966 x 10^16", "digits": 17}, {"e": 13, "idx": 118, "bits": [1, 0, 1, 1, 0], "exps": [18, 0, 8, 1, 0], "deg": "4.004 x 10^24", "digits": 25}, {"e": 14, "idx": 52, "bits": [0, 1, 1, 1, 0], "exps": [0, 1, 9, 1, 0], "deg": "1.480 x 10^21", "digits": 22}, {"e": 15, "idx": 19, "bits": [1, 1, 1, 1, 0], "exps": [1, 1, 2, 1, 0], "deg": "6.035 x 10^16", "digits": 17}, {"e": 16, "idx": 1, "bits": [0, 0, 0, 0, 1], "exps": [0, 0, 0, 0, 1], "deg": "196,883", "digits": 6}, {"e": 17, "idx": 3, "bits": [1, 0, 0, 0, 1], "exps": [1, 0, 0, 0, 1], "deg": "842,609,326", "digits": 9}, {"e": 18, "idx": 100, "bits": [0, 1, 0, 0, 1], "exps": [0, 19, 0, 0, 1], "deg": "6.000 x 10^23", "digits": 24}, {"e": 19, "idx": 6, "bits": [1, 1, 0, 0, 1], "exps": [1, 1, 0, 0, 1], "deg": "293,553,734,298", "digits": 12}, {"e": 20, "idx": 9, "bits": [0, 0, 1, 0, 1], "exps": [0, 0, 2, 0, 1], "deg": "1.255 x 10^14", "digits": 15}, {"e": 21, "idx": 20, "bits": [1, 0, 1, 0, 1], "exps": [2, 0, 7, 0, 1], "deg": "2.510 x 10^17", "digits": 18}, {"e": 22, "idx": 30, "bits": [0, 1, 1, 0, 1], "exps": [0, 1, 7, 0, 1], "deg": "2.858 x 10^19", "digits": 20}, {"e": 23, "idx": 36, "bits": [1, 1, 1, 0, 1], "exps": [1, 3, 8, 0, 1], "deg": "1.304 x 10^20", "digits": 21}, {"e": 24, "idx": 21, "bits": [0, 0, 0, 1, 1], "exps": [0, 0, 0, 1, 1], "deg": "2.905 x 10^17", "digits": 18}, {"e": 25, "idx": 38, "bits": [1, 0, 0, 1, 1], "exps": [2, 0, 0, 1, 1], "deg": "1.723 x 10^20", "digits": 21}, {"e": 26, "idx": 10, "bits": [0, 1, 0, 1, 1], "exps": [0, 3, 0, 1, 1], "deg": "1.902 x 10^14", "digits": 15}, {"e": 27, "idx": 7, "bits": [1, 1, 0, 1, 1], "exps": [1, 1, 0, 1, 1], "deg": "3.879 x 10^12", "digits": 13}, {"e": 28, "idx": 24, "bits": [0, 0, 1, 1, 1], "exps": [0, 0, 3, 1, 1], "deg": "2.986 x 10^18", "digits": 19}, {"e": 29, "idx": 13, "bits": [1, 0, 1, 1, 1], "exps": [1, 0, 2, 1, 1], "deg": "1.109 x 10^15", "digits": 16}, {"e": 30, "idx": 27, "bits": [0, 1, 1, 1, 1], "exps": [0, 6, 7, 1, 1], "deg": "3.605 x 10^18", "digits": 19}, {"e": 31, "idx": 66, "bits": [1, 1, 1, 1, 1], "exps": [2, 3, 7, 1, 1], "deg": "2.180 x 10^22", "digits": 23}]};
const AX = DATA.axes, CO = DATA.corners;
// per-axis colours for the 5 primes {2,3,5,23,47}
const AXCOL = ["#ff5d5d","#ffb24d","#ffe14d","#4dd0ff","#b06bff"];
const cv = document.getElementById('cv'), g = cv.getContext('2d');
let W=0,H=0,DPR=1;
function resize(){DPR=Math.min(2,window.devicePixelRatio||1);
W=cv.clientWidth;H=cv.clientHeight;cv.width=W*DPR;cv.height=H*DPR;
g.setTransform(DPR,0,0,DPR,0,0);}
window.addEventListener('resize',resize);
// ---- geometry of the penteract -------------------------------------------
// 5 axis directions in 3D, spread out symmetrically; coord in {-1,1}^5.
function unit(v){const n=Math.hypot(v[0],v[1],v[2]);return[v[0]/n,v[1]/n,v[2]/n];}
const AXDIR = [
unit([ 1.0, 0.15, 0.30]),
unit([ 0.30, 1.0, 0.10]),
unit([-0.20, 0.15, 1.0 ]),
unit([ 0.85, -0.75, -0.55]),
unit([-0.80, -0.70, 0.55]),
].map(v=>v.map(x=>x*1.05));
// edges: pairs of corners differing in exactly one bit
const EDGES=[];
for(let a=0;a<32;a++)for(let b=a+1;b<32;b++){
const x=a^b; if((x&(x-1))===0) EDGES.push([a,b, Math.log2(x)|0]);
}
// 5D rotation of the {-1,1}^5 coordinate, then map to 3D via AXDIR.
function corner5(e,t){
let c=[]; for(let k=0;k<5;k++) c.push(((e>>k)&1)?1:-1);
// rotate pairs of 5D coordinate planes at different rates -> "hyper" morph
const planes=[[0,2,0.55],[1,3,0.40],[2,4,0.31],[0,4,0.23],[1,4,0.17]];
for(const [i,j,sp] of planes){
const a=t*sp, ca=Math.cos(a), sa=Math.sin(a);
const ci=c[i], cj=c[j];
c[i]=ci*ca - cj*sa; c[j]=ci*sa + cj*ca;
}
let p=[0,0,0];
for(let k=0;k<5;k++){p[0]+=c[k]*AXDIR[k][0];p[1]+=c[k]*AXDIR[k][1];p[2]+=c[k]*AXDIR[k][2];}
return p;
}
// view rotation (slow yaw/pitch) + perspective projection
function project(p,yaw,pitch,scale,cx,cy){
let x=p[0],y=p[1],z=p[2];
let cz=Math.cos(yaw),sz=Math.sin(yaw); let x1=x*cz - z*sz, z1=x*sz + z*cz;
let cp=Math.cos(pitch),sp=Math.sin(pitch); let y1=y*cp - z1*sp, z2=y*sp + z1*cp;
const d=6.2, f=d/(d - z2*0.9);
return [cx + x1*scale*f, cy + y1*scale*f, z2, f];
}
// ---- scenes ---------------------------------------------------------------
const SCENES=[
{id:"intro",title:"THE PENTERACT",
thm:"MonsterShadows.cube5_shadow (proved by native_decide)",
text:"A penteract is the 5-dimensional hypercube: 32 vertices, 80 edges. It is "+
"spinning here in all five dimensions at once. We will see that the Monster "+
"group hides one of these inside its character table.",
mode:"cube", reveal:0},
{id:"axes",title:"FIVE AXES = FIVE PRIMES",
thm:"MonsterShadows.axes5 = [2, 3, 5, 23, 47]",
text:"The 5 directions of the cube are coloured by 5 supersingular primes "+
"{2, 3, 5, 23, 47}. Moving along axis k flips whether prime p_k divides the "+
"representation's degree.",
mode:"cube", reveal:0, axes:true},
{id:"corners",title:"32 CORNERS = 32 REPRESENTATIONS",
thm:"MonsterShadows.cube5_distinct (the 32 corners are Nodup)",
text:"Each corner is a genuine Monster irreducible representation. Corner e (a "+
"5-bit address) is the degree divisible by exactly the primes whose bit is "+
"set. All 32 distinct degrees appear \u2014 the full corner set is realized.",
mode:"cube", reveal:1, labels:true},
{id:"shadow",title:"HOW IT LIVES THERE \u2014 THE SHADOW",
thm:"MonsterShadows.cube5_shadow_exists = true",
text:"Project the spinning cube straight down onto its 5 prime-columns: the 32 "+
"corners cast a 32\u00d75 grid in which every one of the 2^5 = 32 divisibility "+
"patterns occurs exactly once. That grid is the shadow living in the matrix.",
mode:"shadow"},
{id:"max",title:"FIVE IS MAXIMAL",
thm:"MonsterShadows.no_cube6_shadow = true",
text:"Could a 6-cube fit? For every choice of 6 of the 15 supersingular primes at "+
"least one of the 64 corners is missing \u2014 a hole in the shadow. So the "+
"penteract is the largest hypercube the Monster degrees cast. 5 is maximal.",
mode:"hole"},
];
let scene=0, playing=true, t0=performance.now(), sceneStart=t0, phase=0;
const DUR=[7000,7000,9000,9000,8500];
const dotsEl=document.getElementById('dots');
SCENES.forEach((s,i)=>{const d=document.createElement('div');d.className='dot';
d.onclick=()=>goto(i);dotsEl.appendChild(d);});
function goto(i){scene=(i+SCENES.length)%SCENES.length;sceneStart=performance.now();
updateHUD();}
document.getElementById('next').onclick=()=>goto(scene+1);
document.getElementById('prev').onclick=()=>goto(scene-1);
const playBtn=document.getElementById('play');
playBtn.onclick=()=>{playing=!playing;playBtn.innerHTML=playing?"&#10073;&#10073; Pause":"&#9654; Play";};
function updateHUD(){
const s=SCENES[scene];
document.getElementById('title').textContent=s.title;
document.getElementById('thm').textContent=s.thm;
document.getElementById('text').textContent=s.text;
[...dotsEl.children].forEach((d,i)=>d.classList.toggle('on',i===scene));
const leg=document.getElementById('legend');
if(s.axes||s.mode==='shadow'||s.mode==='hole'){
leg.innerHTML = AX.map((p,k)=>
`<div><span style="color:${AXCOL[k]}">&#9632;</span> axis ${k} &nbsp;<b>p=${p}</b></div>`
).join("");
} else leg.innerHTML="";
}
// smoothstep
function ss(x){x=Math.max(0,Math.min(1,x));return x*x*(3-2*x);}
function draw(now){
const s=SCENES[scene];
const tlocal=(now-sceneStart);
if(playing && tlocal>DUR[scene]) goto(scene+1);
const spin=now*0.001;
g.clearRect(0,0,W,H);
// subtle starfield-ish bg vignette
const grd=g.createRadialGradient(W/2,H*0.42,40,W/2,H*0.42,Math.max(W,H)*0.7);
grd.addColorStop(0,"#0c1024");grd.addColorStop(1,"#04050c");
g.fillStyle=grd;g.fillRect(0,0,W,H);
if(s.mode==="cube"||s.mode==="axes") drawCube(s,now,spin);
else if(s.mode==="shadow") drawShadow(s,now,tlocal);
else if(s.mode==="hole") drawHole(s,now,tlocal,spin);
const prog=(scene+Math.min(1,tlocal/DUR[scene]))/SCENES.length;
document.getElementById('bar').style.width=(prog*100).toFixed(1)+"%";
requestAnimationFrame(draw);
}
function drawCube(s,now,spin){
const cx=W*0.5, cy=H*0.42, scale=Math.min(W,H)*0.20;
const yaw=spin*0.5, pitch=Math.sin(spin*0.31)*0.5+0.35;
const t=now*0.0011;
const P=CO.map(c=>project(corner5(c.e,t),yaw,pitch,scale,cx,cy));
// edges (painter: sort by avg depth)
const es=EDGES.map(([a,b,k])=>({a,b,k,z:(P[a][2]+P[b][2])/2}));
es.sort((u,v)=>u.z-v.z);
for(const ed of es){
const A=P[ed.a],B=P[ed.b];
const depth=ss((ed.z+3)/6);
let col, w;
if(s.axes){col=AXCOL[ed.k]; w=1.1+1.6*depth;}
else {const a=(0.18+0.5*depth); col=`rgba(150,130,255,${a})`; w=0.7+1.5*depth;}
g.strokeStyle=col; g.lineWidth=w;
g.beginPath();g.moveTo(A[0],A[1]);g.lineTo(B[0],B[1]);g.stroke();
}
// vertices
const order=[...Array(32).keys()].sort((u,v)=>P[u][2]-P[v][2]);
for(const i of order){
const Pi=P[i], c=CO[i], depth=ss((Pi[2]+3)/6), f=Pi[3];
const r=(2.4+3.4*depth)*f;
// colour vertex by popcount of bits (how many primes divide)
const pc=c.bits.reduce((a,b)=>a+b,0);
const hue=265 - pc*16;
g.fillStyle=`hsla(${hue},85%,${42+depth*22}%,${0.55+0.45*depth})`;
g.beginPath();g.arc(Pi[0],Pi[1],r,0,7);g.fill();
if(s.labels && depth>0.34){
g.fillStyle=`rgba(235,235,250,${0.30+0.7*depth})`;
g.font=`${(9+5*depth)|0}px Consolas,monospace`;
g.textAlign="center";
g.fillText("#"+c.idx, Pi[0], Pi[1]-r-3);
}
}
}
// project the 32 corners onto a 32 x 5 grid that "drops" out of the cube
function drawShadow(s,now,tlocal){
const t=now*0.0011;
const cx=W*0.5, cy=H*0.30, scale=Math.min(W,H)*0.15;
const yaw=now*0.0004, pitch=0.5;
const drop=ss((tlocal-600)/2600); // 0..1 cube -> grid
// grid layout
const gx0=W*0.5-(5*38)/2, gy0=H*0.46;
const cw=38, ch=Math.min(13,(H*0.46)/32);
// current positions interpolate cube vertex -> grid cell centre
for(let e=0;e<32;e++){
const c=CO[e];
const Pc=project(corner5(e,t),yaw,pitch,scale,cx,cy);
for(let k=0;k<5;k++){
const tx=gx0+k*cw+cw/2, ty=gy0+e*ch+ch/2;
const x=Pc[0]+(tx-Pc[0])*drop, y=Pc[1]+(ty-Pc[1])*drop;
const on=c.bits[k];
const rr=(1.6+1.4*(1-drop))*Pc[3];
g.fillStyle= on? AXCOL[k] : `rgba(90,95,130,${0.25+0.2*drop})`;
g.globalAlpha = on? (0.55+0.45*drop) : (0.5);
g.fillRect(x-cw*0.42*drop- (1-drop)*rr, y-ch*0.40*drop-(1-drop)*rr,
cw*0.84*drop+(1-drop)*2*rr, ch*0.80*drop+(1-drop)*2*rr);
}
}
g.globalAlpha=1;
if(drop>0.6){
g.fillStyle="rgba(220,225,245,"+((drop-0.6)/0.4).toFixed(2)+")";
g.font="12px Consolas,monospace";g.textAlign="center";
for(let k=0;k<5;k++) g.fillText("p="+AX[k], gx0+k*cw+cw/2, gy0-6);
g.textAlign="right";
g.fillStyle="rgba(160,165,195,"+((drop-0.6)/0.4).toFixed(2)+")";
for(let e=0;e<32;e+=4) g.fillText("#"+CO[e].idx, gx0-8, gy0+e*ch+ch*0.85);
}
}
// scene 5: build a 6-cube grid and punch a hole in one corner
function drawHole(s,now,tlocal,spin){
// show a 6-cube (64 corners) projection with one missing corner highlighted
const cx=W*0.5, cy=H*0.42, scale=Math.min(W,H)*0.16;
const yaw=spin*0.4, pitch=Math.sin(spin*0.27)*0.4+0.3;
const t=now*0.0010;
const AX6=[unit([1,0.1,0.3]),unit([0.25,1,0.05]),unit([-0.2,0.1,1]),
unit([0.8,-0.7,-0.5]),unit([-0.75,-0.65,0.5]),unit([0.5,0.6,-0.8])]
.map(v=>v.map(x=>x*0.95));
function c6(e){let c=[];for(let k=0;k<6;k++)c.push(((e>>k)&1)?1:-1);
const planes=[[0,2,0.5],[1,3,0.37],[2,4,0.29],[3,5,0.21],[0,5,0.15]];
for(const[i,j,sp]of planes){const a=t*sp,ca=Math.cos(a),sa=Math.sin(a);
const ci=c[i],cj=c[j];c[i]=ci*ca-cj*sa;c[j]=ci*sa+cj*ca;}
let p=[0,0,0];for(let k=0;k<6;k++){p[0]+=c[k]*AX6[k][0];p[1]+=c[k]*AX6[k][1];p[2]+=c[k]*AX6[k][2];}
return p;}
const missing=43; // the "hole": a corner with no rep
const P=[];for(let e=0;e<64;e++)P.push(project(c6(e),yaw,pitch,scale,cx,cy));
const es=[];for(let a=0;a<64;a++)for(let b=a+1;b<64;b++){const x=a^b;
if((x&(x-1))===0)es.push([a,b]);}
es.sort((u,v)=>(P[u[0]][2]+P[u[1]][2])-(P[v[0]][2]+P[v[1]][2]));
for(const[a,b]of es){
if(a===missing||b===missing) continue;
const A=P[a],B=P[b],depth=ss((( (P[a][2]+P[b][2])/2)+3.5)/7);
g.strokeStyle=`rgba(120,110,200,${0.1+0.35*depth})`;g.lineWidth=0.6+1.1*depth;
g.beginPath();g.moveTo(A[0],A[1]);g.lineTo(B[0],B[1]);g.stroke();
}
for(let e=0;e<64;e++){
const Pi=P[e],depth=ss((Pi[2]+3.5)/7);
if(e===missing){
const pulse=0.5+0.5*Math.sin(now*0.006);
g.strokeStyle=`rgba(255,80,80,${0.5+0.5*pulse})`;g.lineWidth=2.2;
const r=9+5*pulse;
g.beginPath();g.arc(Pi[0],Pi[1],r,0,7);g.stroke();
g.beginPath();g.moveTo(Pi[0]-r*0.7,Pi[1]-r*0.7);g.lineTo(Pi[0]+r*0.7,Pi[1]+r*0.7);
g.moveTo(Pi[0]+r*0.7,Pi[1]-r*0.7);g.lineTo(Pi[0]-r*0.7,Pi[1]+r*0.7);g.stroke();
g.fillStyle="rgba(255,120,120,0.95)";g.font="12px Consolas,monospace";
g.textAlign="center";g.fillText("missing corner",Pi[0],Pi[1]-r-6);
continue;
}
g.fillStyle=`hsla(258,80%,${40+depth*22}%,${0.5+0.4*depth})`;
g.beginPath();g.arc(Pi[0],Pi[1],(2+3*depth)*Pi[3],0,7);g.fill();
}
}
resize();updateHUD();requestAnimationFrame(draw);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment