Dial: rotate the ball about the ½–∞ strut; ξ, its odd partner and F(u) pulled back live

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FAqjv3n4yABxvJPJHFRhVA
This commit is contained in:
teahubxyzandClaude Fable 5.1 committed 2026-10-27 22:50:26 +00:00
1 parent 6be2c9db65
commit 9745a66415
1 file changed
+197
+197
View File
@@ -0,0 +1,197 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>The Zeta Dial</title>
<meta name="description" content="Turn the ball about the ½–∞ strut: s − ½ ↦ e^{iθ}(s − ½). A quarter turn is the Wick rotation, a half turn the functional equation, a full turn is −1 on the odd sheet.">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,500;1,500&family=JetBrains+Mono:wght@400;500&display=swap">
<style>
:root{--bg:#0b0a12;--panel:#15131f;--line:#2a2638;--fg:#e9e4f2;--muted:#9a93ad;--up:#f0b44c;--down:#e2559a;--teal:#4fd1c5;
--display:"Cormorant Garamond",Georgia,serif;--mono:"JetBrains Mono",ui-monospace,monospace;color-scheme:dark}
html,body{margin:0}
body{background:var(--bg);color:var(--fg);font-family:var(--mono);font-size:13.5px;line-height:1.6;
padding:calc(28px + env(safe-area-inset-top,0px)) max(16px,env(safe-area-inset-right,0px)) calc(48px + env(safe-area-inset-bottom,0px)) max(16px,env(safe-area-inset-left,0px))}
main{max-width:1040px;margin:0 auto;display:flex;flex-direction:column;gap:18px}
.eyebrow{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:var(--muted)} .eyebrow a{color:var(--muted)}
h1{font-family:var(--display);font-weight:500;font-size:clamp(30px,5.5vw,46px);line-height:1.05;margin:.15em 0 0;text-wrap:balance} h1 i{color:var(--down)}
.lede{color:var(--muted);max-width:70ch;margin:0}
.bar{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:center;padding:12px 14px;border:1px solid var(--line);border-radius:6px;background:var(--panel)}
.seg{display:inline-flex;border:1px solid var(--line);border-radius:5px;overflow:hidden}
.seg button{background:transparent;color:var(--fg);border:0;padding:6px 11px;font:inherit;cursor:pointer}
.seg button+button{border-left:1px solid var(--line)} .seg button[aria-pressed="true"]{background:var(--up);color:#1a1408}
label.k{color:var(--muted);font-size:11.5px;letter-spacing:.06em;text-transform:uppercase;margin-right:6px}
input[type=range]{accent-color:var(--up);width:260px;max-width:100%}
.panes{display:grid;grid-template-columns:1.2fr 1fr;gap:12px}
.pane{border:1px solid var(--line);border-radius:6px;background:var(--panel);overflow:hidden}
.pane h2{font-family:var(--display);font-weight:500;font-size:20px;margin:0;padding:8px 12px 0}
.pane p.sub{margin:0;padding:0 12px 6px;color:var(--muted);font-size:12px;min-height:2.6em}
.view{position:relative;aspect-ratio:1;background:#000} .view canvas{position:absolute;inset:0;width:100%;height:100%}
.busy{position:absolute;right:8px;top:6px;font-size:11px;color:var(--up);opacity:0;transition:opacity .2s} .busy.on{opacity:1}
.read{display:grid;grid-template-columns:auto 1fr;gap:4px 14px;padding:10px 12px 12px;font-variant-numeric:tabular-nums}
.read span.k{color:var(--muted)} .read .q{color:var(--up)}
.notes{display:grid;grid-template-columns:1.3fr 1fr;gap:14px}
.card{border:1px solid var(--line);border-radius:6px;background:var(--panel);padding:14px 16px;color:var(--muted);font-size:12.5px}
.card h3{font-family:var(--display);font-weight:500;font-size:19px;color:var(--fg);margin:0 0 6px} .card p{margin:0 0 8px} .card b{color:var(--fg);font-weight:500}
@media (max-width:760px){.panes,.notes{grid-template-columns:1fr}}
</style>
</head>
<body>
<main>
<header>
<div class="eyebrow">maths · <a href="../">two poles</a> · <a href="../zeta/">zeta ball</a> · <a href="../strut/">strut</a> · the dial</div>
<h1>The zeta <i>dial</i></h1>
</header>
<p class="lede">Turn the ball about the strut through ½ and ∞. On the plane that is a plain rotation about ½: <b>s − ½ ↦ e<sup>iθ</sup>(s − ½)</b>. A quarter turn carries the real line onto the critical line (the Wick rotation); a half turn is the functional equation s ↦ 1 − s; a full turn is the identity, with q = −1. The paint shows the function pulled back through the turn, so you watch cosh become cos.</p>
<div class="bar">
<span><label class="k">sheet</label>
<span class="seg"><button data-fn="0" aria-pressed="true">ξ(s) even</button><button data-fn="1" aria-pressed="false">(s−½)·ξ(s) odd</button><button data-fn="2" aria-pressed="false">F(u), u = (s−½)²</button></span></span>
<span><label class="k">θ</label><input type="range" id="th" min="0" max="720" step="1" value="0"> <span id="thRead">0°</span></span>
<span class="seg"><button data-stop="0">0</button><button data-stop="90">¼</button><button data-stop="180">½</button><button data-stop="270">¾</button><button data-stop="360">1</button><button data-stop="720">2</button></span>
<span><label class="k">height</label><input type="range" id="span" min="6" max="60" step="1" value="24" style="width:120px"> <span id="spanRead">±24</span></span>
</div>
<div class="panes">
<section class="pane">
<h2 id="title">ξ pulled back through the turn</h2>
<p class="sub" id="sub">white line: where the real axis has gone · gold: where the critical line has gone · dots: the zeros, carried along</p>
<div class="view"><canvas id="c"></canvas><canvas id="o"></canvas><span class="busy" id="b">painting</span></div>
</section>
<section class="pane">
<h2>The ball, from above the lamp</h2>
<p class="sub">looking down the ½–∞ strut: the equator is the rim, the real axis and the imaginary axis are the two diameters; the needle is θ</p>
<div class="view"><canvas id="d"></canvas></div>
<div class="read">
<span class="k">θ</span><span id="r1">0</span>
<span class="k">on the plane</span><span id="r2">s − ½ ↦ s − ½</span>
<span class="k">q = e^{iθ/2}</span><span class="q" id="r3">1</span>
<span class="k">even sheet sees</span><span id="r4">+1</span>
<span class="k">odd sheet sees</span><span id="r5">+1</span>
<span class="k">F sees</span><span id="r6">u ↦ u</span>
</div>
</section>
</div>
<section class="notes">
<div class="card">
<h3>Three sheets, one dial</h3>
<p><b>F(u).</b> Write v = s − ½. ξ(½ + v) = F(v²), and F has positive Taylor coefficients. F lives in the u = v² plane; the dial turns u at double speed, u ↦ e<sup>2iθ</sup>u. A half turn of the ball is a full turn of F. The zeros of ξ, at v = ±iγ, become the single points u = −γ² on F's negative real axis. RH, downstairs: every zero of F is real and negative.</p>
<p><b>ξ, even.</b> Take the square root: v = √u. Now the half turn exists as its own move, s ↦ 1 − s, and ξ doesn't notice it: ξ(1 − s) = ξ(s). The two roots ±iγ are the zero pair on the equator.</p>
<p><b>(s − ½)·ξ, odd.</b> Multiply by v. Same zeros, plus a new one at v = 0, the touching point ½. The half turn now shows up as a sign: this sheet sees s ↦ 1 − s as −1. A full turn is +1 again on both sheets; the −1 that a full turn carries in the quaternion reading, q = e<sup>iθ/2</sup>, is what the odd sheet shows at the half.</p>
<p>So the dial reads: 0 = nothing; ¼ = Wick, real axis onto the critical line, cosh onto cos; ½ = the functional equation (+1 even, −1 odd); 1 = home with q = −1; 2 = home with q = +1.</p>
</div>
<div class="card">
<h3>Reading the paint</h3>
<p>Hue is the argument of the value, brightness its size. At θ = 0 the real axis is a bright cosh ridge and the critical line a cos stripe with dark zeros. Drag θ: the ridge and the stripe rotate together about ½. At ¼ they have swapped places on the screen. At ½ the picture is back (even sheet) or back with every hue rotated by half a turn (odd sheet, a sign flip).</p>
<p>Zeros are the first 20 pairs on the critical line; on the F sheet they appear once each, on the negative real u-axis.</p>
</div>
</section>
</main>
<script type="text/plain" id="wsrc">
const TAU=2*Math.PI,LN2=Math.LN2,LNPI=Math.log(Math.PI),HALF_LN_TAU=0.5*Math.log(TAU);
const BC=[1/12,-1/720,1/30240,-1/1209600,1/47900160,-691/1307674368000,1/74724249600,-3617/10670622842880000,43867/5109094217170944000,-174611/802857662698291200000];
function zetaEM(sr,si,out){
const N=Math.min(560,Math.max(14,Math.ceil(Math.hypot(sr,si)/Math.PI)+12));let re=0,im=0;
for(let n=1;n<N;n++){const l=Math.log(n),m=Math.exp(-sr*l),t=-si*l;re+=m*Math.cos(t);im+=m*Math.sin(t);}
const lN=Math.log(N),m0=Math.exp(-sr*lN),t0=-si*lN,nr=m0*Math.cos(t0),ni=m0*Math.sin(t0);re+=nr/2;im+=ni/2;
const ar=N*nr,ai=N*ni,br=sr-1,bi=si,d=br*br+bi*bi;re+=(ar*br+ai*bi)/d;im+=(ai*br-ar*bi)/d;
let pr=sr,pi=si,sc=1/N;
for(let k=1;k<=BC.length;k++){const tr=pr*nr-pi*ni,ti=pr*ni+pi*nr,c=BC[k-1]*sc;re+=c*tr;im+=c*ti;
if(Math.abs(c)*Math.hypot(tr,ti)<1e-16*Math.hypot(re,im))break;
const xr=sr+2*k-1,yr=sr+2*k,qr=xr*yr-si*si,qi=si*(xr+yr);const npr=pr*qr-pi*qi;pi=pr*qi+pi*qr;pr=npr;sc/=N*N;}
out[0]=re;out[1]=im;}
function lnGamma(zr,zi,out){let accR=0,accI=0;while(zr<9){accR+=0.5*Math.log(zr*zr+zi*zi);accI+=Math.atan2(zi,zr);zr+=1;}
const lr=0.5*Math.log(zr*zr+zi*zi),li=Math.atan2(zi,zr);let r=(zr-0.5)*lr-zi*li-zr+HALF_LN_TAU,i=(zr-0.5)*li+zi*lr-zi;
const d=zr*zr+zi*zi,ir=zr/d,ii=-zi/d,i2r=ir*ir-ii*ii,i2i=2*ir*ii;let pr=ir,pi=ii;const cs=[1/12,-1/360,1/1260,-1/1680];
for(let j=0;j<4;j++){r+=cs[j]*pr;i+=cs[j]*pi;const t=pr*i2r-pi*i2i;pi=pr*i2i+pi*i2r;pr=t;}out[0]=r-accR;out[1]=i-accI;}
function logSin(x,y,out){if(y>15){out[0]=-LN2+y;out[1]=Math.PI/2-x;return;}if(y<-15){out[0]=-LN2-y;out[1]=x-Math.PI/2;return;}
const re=Math.sin(x)*Math.cosh(y),im=Math.cos(x)*Math.sinh(y);out[0]=0.5*Math.log(re*re+im*im);out[1]=Math.atan2(im,re);}
const T1=[0,0],T2=[0,0],T3=[0,0],T4=[0,0],T5=[0,0];
function logZeta(sr,si,out){if(sr>=0.5){zetaEM(sr,si,T1);out[0]=0.5*Math.log(T1[0]*T1[0]+T1[1]*T1[1]);out[1]=Math.atan2(T1[1],T1[0]);return;}
logSin(Math.PI*sr/2,Math.PI*si/2,T1);lnGamma(1-sr,-si,T2);zetaEM(1-sr,-si,T3);
out[0]=sr*LN2+(sr-1)*LNPI+T1[0]+T2[0]+0.5*Math.log(T3[0]*T3[0]+T3[1]*T3[1]);out[1]=si*(LN2+LNPI)+T1[1]+T2[1]+Math.atan2(T3[1],T3[0]);}
function logXi(sr,si,out){if(sr<0.5){sr=1-sr;si=-si;}const pr=0.5*(sr*(sr-1)-si*si),pi=0.5*si*(2*sr-1);
lnGamma(sr/2,si/2,T4);logZeta(sr,si,T5);out[0]=0.5*Math.log(pr*pr+pi*pi)-sr/2*LNPI+T4[0]+T5[0];out[1]=Math.atan2(pi,pr)-si/2*LNPI+T4[1]+T5[1];}
function hsv(h,s,v,buf,o){const i=Math.floor(h*6),f=h*6-i,p=v*(1-s),q=v*(1-f*s),t=v*(1-(1-f)*s);let r,g,b;
switch(((i%6)+6)%6){case 0:r=v;g=t;b=p;break;case 1:r=q;g=v;b=p;break;case 2:r=p;g=v;b=t;break;case 3:r=p;g=q;b=v;break;case 4:r=t;g=p;b=v;break;default:r=v;g=p;b=q;}
buf[o]=r*255;buf[o+1]=g*255;buf[o+2]=b*255;buf[o+3]=255;}
const L=[0,0];
// paint the chosen sheet at plane point (x,y) = v (sheets 0,1) or u (sheet 2), after the dial's pull-back
function paint(x,y,fn,th,buf,o){
let vr,vi;
if(fn===2){ // u-plane: u ↦ e^{2iθ}u, then v = sqrt(u)
const c=Math.cos(2*th),s=Math.sin(2*th),ur=x*c-y*s,ui=x*s+y*c,m=Math.hypot(ur,ui),a=Math.atan2(ui,ur)/2;
vr=Math.sqrt(m)*Math.cos(a);vi=Math.sqrt(m)*Math.sin(a);
}else{const c=Math.cos(th),s=Math.sin(th);vr=x*c-y*s;vi=x*s+y*c;}
if(Math.hypot(vr,vi)>400){buf[o]=buf[o+1]=buf[o+2]=40;buf[o+3]=255;return;}
logXi(0.5+vr,vi,L);
if(fn===1){L[0]+=0.5*Math.log(vr*vr+vi*vi+1e-300);L[1]+=Math.atan2(vi,vr);}
if(!isFinite(L[0])){const v=L[0]<0?0:255;buf[o]=buf[o+1]=buf[o+2]=v;buf[o+3]=255;return;}
let h=(L[1]/TAU)%1;if(h<0)h+=1;
const v=0.2+0.65*(0.5+Math.atan(0.18*L[0])/Math.PI);hsv(h,0.62,v,buf,o);}
onmessage=(e)=>{const m=e.data,n=m.n,buf=new Uint8ClampedArray(n*n*4);
for(let j=0;j<n;j++)for(let i=0;i<n;i++){const x=(i/(n-1)-0.5)*2*m.hw,y=-(j/(n-1)-0.5)*2*m.hw;paint(x,y,m.fn,m.th,buf,(j*n+i)*4);}
postMessage({id:m.id,n,buf},[buf.buffer]);};
</script>
<script>
(()=>{
const $=id=>document.getElementById(id);
const ZEROS=[14.134725,21.022040,25.010858,30.424876,32.935062,37.586178,40.918719,43.327073,48.005151,49.773832,52.970321,56.446248,59.347044,60.831779,65.112544,67.079811,69.546402,72.067158,75.704691,77.144840];
const st={fn:0,th:0,span:24};
const url=URL.createObjectURL(new Blob([$("wsrc").textContent],{type:"text/javascript"}));const W=new Worker(url);let id=0;
function draw(hi){const n=hi?400:150;const my=++id;$("b").classList.add("on");W.postMessage({id:my,n,fn:st.fn,th:st.th*Math.PI/180,hw:st.span});}
W.onmessage=(e)=>{if(e.data.id!==id)return;const c=$("c");c.width=c.height=e.data.n;c.getContext("2d").putImageData(new ImageData(e.data.buf,e.data.n,e.data.n),0,0);$("b").classList.remove("on");overlay();};
function toPx(x,y,S){return[(x/(2*st.span)+0.5)*S,(0.5-y/(2*st.span))*S];}
function overlay(){
const o=$("o"),S=o.clientWidth*devicePixelRatio;o.width=o.height=S;const g=o.getContext("2d");g.clearRect(0,0,S,S);
const th=-st.th*Math.PI/180; // the pull-back shows the axes rotated the other way
const rot=(x,y,a)=>[x*Math.cos(a)-y*Math.sin(a),x*Math.sin(a)+y*Math.cos(a)];
const line=(ax,ay,col)=>{g.strokeStyle=col;g.lineWidth=1.5*devicePixelRatio;g.beginPath();const[x1,y1]=toPx(-200*ax,-200*ay,S),[x2,y2]=toPx(200*ax,200*ay,S);g.moveTo(x1,y1);g.lineTo(x2,y2);g.stroke();};
if(st.fn===2){line(1,0,"rgba(230,230,230,.6)");line(0,1,"rgba(240,180,76,.85)");
g.fillStyle="#fff";for(const z of ZEROS){const[ux,uy]=rot(-z*z,0,2*th);const[x,y]=toPx(ux,uy,S);if(x>=0&&x<=S)g.beginPath(),g.arc(x,y,2.6*devicePixelRatio,0,7),g.fill();}
}else{
const[rx,ry]=rot(1,0,th),[cx,cy]=rot(0,1,th);line(rx,ry,"rgba(230,230,230,.6)");line(cx,cy,"rgba(240,180,76,.85)");
g.fillStyle="#fff";for(const z of ZEROS)for(const sg of[1,-1]){const[vx,vy]=rot(0,sg*z,th);const[x,y]=toPx(vx,vy,S);g.beginPath();g.arc(x,y,2.4*devicePixelRatio,0,7);g.fill();}
if(st.fn===1){const[x,y]=toPx(0,0,S);g.fillStyle="#e2559a";g.beginPath();g.arc(x,y,3.4*devicePixelRatio,0,7);g.fill();}
}
// the origin ½
const[x0,y0]=toPx(0,0,S);g.strokeStyle="#fff";g.lineWidth=1.2*devicePixelRatio;g.beginPath();g.arc(x0,y0,4*devicePixelRatio,0,7);g.stroke();
}
function dial(){
const c=$("d"),S=c.clientWidth*devicePixelRatio;c.width=c.height=S;const g=c.getContext("2d");g.clearRect(0,0,S,S);
const cx=S/2,cy=S/2,R=S*0.36,th=st.th*Math.PI/180;
g.strokeStyle="#f0b44c";g.lineWidth=3*devicePixelRatio;g.beginPath();g.arc(cx,cy,R,0,7);g.stroke(); // equator = rim
g.strokeStyle="rgba(230,230,230,.6)";g.lineWidth=1.5*devicePixelRatio;g.beginPath();g.moveTo(cx-R,cy);g.lineTo(cx+R,cy);g.stroke(); // real axis
g.strokeStyle="rgba(140,140,160,.6)";g.beginPath();g.moveTo(cx,cy-R);g.lineTo(cx,cy+R);g.stroke(); // imaginary axis
const dot=(x,y,col,r,fill=true)=>{g.beginPath();g.arc(x,y,r*devicePixelRatio,0,7);if(fill){g.fillStyle=col;g.fill();}else{g.strokeStyle=col;g.lineWidth=2*devicePixelRatio;g.stroke();}};
dot(cx+R,cy,"#000",7);dot(cx+R,cy,"#f0b44c",7,false);dot(cx-R,cy,"#fff",7); // ∞ (z=+1) and ½ (z=−1) on the rim
dot(cx,cy,"#4fd1c5",6); // the pole axis 0–∞ seen end-on
// needle: θ, and the double-speed hand for q
const nx=cx+0.92*R*Math.cos(-th),ny=cy+0.92*R*Math.sin(-th);
g.strokeStyle="#e2559a";g.lineWidth=2.5*devicePixelRatio;g.beginPath();g.moveTo(cx,cy);g.lineTo(nx,ny);g.stroke();
const qx=cx+0.5*R*Math.cos(-th/2),qy=cy+0.5*R*Math.sin(-th/2);
g.strokeStyle="#f0b44c";g.setLineDash([4*devicePixelRatio,3*devicePixelRatio]);g.beginPath();g.moveTo(cx,cy);g.lineTo(qx,qy);g.stroke();g.setLineDash([]);
g.fillStyle="#9a93ad";g.font=`${11*devicePixelRatio}px JetBrains Mono`;g.fillText("∞",cx+R+10*devicePixelRatio,cy+4*devicePixelRatio);g.fillText("½",cx-R-22*devicePixelRatio,cy+4*devicePixelRatio);
g.fillText("needle: θ · dashed: q = e^{iθ/2}",10*devicePixelRatio,S-10*devicePixelRatio);
const d=st.th;$("r1").textContent=d+"°";
$("r2").textContent=d%360===0?"s − ½ ↦ s − ½":d%360===180?"s − ½ ↦ −(s − ½) i.e. s ↦ 1 − s":d%360===90?"s − ½ ↦ i(s − ½) (Wick)":d%360===270?"s − ½ ↦ −i(s − ½)":"s − ½ ↦ e^{i·"+d+"°}(s − ½)";
const qr=Math.cos(th/2),qi=Math.sin(th/2);$("r3").textContent=(Math.abs(qr)<1e-9?"":qr.toFixed(3))+(Math.abs(qi)<1e-9?"":(qi>0&&Math.abs(qr)>=1e-9?" + ":qi<0?" − ":"")+Math.abs(qi).toFixed(3)+"i");
$("r4").textContent=d%360===0?"+1 (home)":d%360===180?"+1 (ξ(1−s) = ξ(s))":"—";
$("r5").textContent=d%360===0?"+1 (home)":d%360===180?"−1 (sign flip)":"—";
$("r6").textContent="u ↦ e^{i·"+(2*d)%360+"°}u"+((2*d)%360===0?" (home)":"");
}
function all(){draw(false);clearTimeout(all._t);all._t=setTimeout(()=>draw(true),220);dial();
$("title").textContent=st.fn===2?"F pulled back through the turn (u-plane)":st.fn===1?"(s − ½)·ξ pulled back through the turn":"ξ pulled back through the turn";
$("sub").textContent=st.fn===2?"white: the real u-axis · gold: the negative real axis, where F's zeros are (once each) · the dial turns u at double speed":"white line: where the real axis has gone · gold: where the critical line has gone · dots: the zeros, carried along"+(st.fn===1?" · pink: the new zero at ½":"");}
document.querySelectorAll("[data-fn]").forEach(b=>b.onclick=()=>{st.fn=+b.dataset.fn;document.querySelectorAll("[data-fn]").forEach(x=>x.setAttribute("aria-pressed",x===b));all();});
document.querySelectorAll("[data-stop]").forEach(b=>b.onclick=()=>{st.th=+b.dataset.stop;$("th").value=st.th;$("thRead").textContent=st.th+"°";all();});
$("th").oninput=e=>{st.th=+e.target.value;$("thRead").textContent=st.th+"°";dial();overlay();clearTimeout($("th")._t);$("th")._t=setTimeout(all,60);};
$("span").oninput=e=>{st.span=+e.target.value;$("spanRead").textContent="±"+st.span;clearTimeout($("span")._t);$("span")._t=setTimeout(all,120);};
addEventListener("resize",()=>{overlay();dial();});
all();
})();
</script>
</body>
</html>