Download index.html from Jessedbrown/lightDOORwsMs: direct link, hf CLI and curl.
- Browser
- Download file 43 kB
-
https://huggingface.co/Jessedbrown/lightDOORwsMs/resolve/main/index.html
- Command line
-
hf download hf://Jessedbrown/lightDOORwsMs/index.html
-
curl -L -o index.html https://huggingface.co/Jessedbrown/lightDOORwsMs/resolve/main/index.html
43 kB
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"> | |
| <title>lightDOORs β a place made of light, with doors instead of windows</title> | |
| <style> | |
| :root{--bg:#08060f;--fg:#efeaf2;--dim:#8d8496;--line:#241e30;--gold:#e8c07a; | |
| --ok:#7fe0a0;--no:#ff8b96;--pane:#0e0a18} | |
| *{box-sizing:border-box;-webkit-tap-highlight-color:transparent} | |
| html,body{height:100%;margin:0;overflow:hidden} | |
| body{background:radial-gradient(120% 90% at 50% -10%,#1a1030 0%,#08060f 60%); | |
| color:var(--fg);font:13px/1.55 ui-monospace,"SF Mono",Menlo,Consolas,monospace} | |
| /* the field the doors stand in */ | |
| #field{position:fixed;inset:0} | |
| #stars{position:fixed;inset:0;pointer-events:none} | |
| /* a pane of light */ | |
| .pane{position:absolute;min-width:230px;min-height:150px;background:rgba(14,10,24,.93); | |
| border:1px solid var(--line);border-radius:13px;box-shadow:0 18px 60px rgba(0,0,0,.6); | |
| display:flex;flex-direction:column;overflow:hidden;backdrop-filter:blur(9px)} | |
| .pane.on{border-color:rgba(232,192,122,.42)} | |
| .bar{display:flex;align-items:center;gap:8px;padding:7px 9px;cursor:grab; | |
| background:linear-gradient(180deg,rgba(255,255,255,.05),rgba(255,255,255,0)); | |
| border-bottom:1px solid var(--line);flex:0 0 auto;touch-action:none} | |
| .bar b{font-weight:600;font-size:12px;flex:1;white-space:nowrap;overflow:hidden; | |
| text-overflow:ellipsis} | |
| .dot{width:11px;height:11px;border-radius:50%;border:0;cursor:pointer;flex:0 0 auto} | |
| .dot.x{background:#ff6b78}.dot.m{background:#e8c07a} | |
| .body{flex:1;overflow:auto;padding:10px;font-size:12.5px} | |
| .grip{position:absolute;right:0;bottom:0;width:18px;height:18px;cursor:nwse-resize; | |
| background:linear-gradient(135deg,transparent 50%,rgba(255,255,255,.22) 50%); | |
| touch-action:none} | |
| .key{width:14px;height:14px;border-radius:4px;flex:0 0 auto;border:1px solid rgba(0,0,0,.4)} | |
| /* the sill β where the doors are */ | |
| #sill{position:fixed;left:0;right:0;bottom:0;display:flex;gap:6px;padding:8px 10px; | |
| background:rgba(8,6,15,.9);border-top:1px solid var(--line);overflow-x:auto; | |
| align-items:center;z-index:9000;backdrop-filter:blur(12px)} | |
| #sill button{background:transparent;color:var(--fg);border:1px solid var(--line); | |
| border-radius:9px;padding:8px 12px;font:600 12px ui-monospace,monospace; | |
| cursor:pointer;white-space:nowrap;flex:0 0 auto} | |
| #sill button.lit{border-color:var(--gold);color:var(--gold)} | |
| #sill .me{margin-left:auto;display:flex;align-items:center;gap:7px;flex:0 0 auto; | |
| color:var(--dim);font-size:11px;padding-left:10px} | |
| /* bits */ | |
| input,textarea,select{width:100%;background:rgba(255,255,255,.04);color:var(--fg); | |
| border:1px solid var(--line);border-radius:9px;padding:9px; | |
| font:12.5px ui-monospace,monospace;margin:4px 0} | |
| textarea{min-height:62px;font-size:11px;line-height:1.45} | |
| .btn{background:var(--gold);color:#120d18;border:0;border-radius:9px;padding:9px 15px; | |
| font:700 12px ui-monospace,monospace;cursor:pointer;margin:3px 5px 3px 0} | |
| .btn.g{background:transparent;color:var(--fg);border:1px solid var(--line)} | |
| .row{display:flex;justify-content:space-between;gap:10px;padding:1px 0} | |
| .k{color:var(--dim)}.ok{color:var(--ok)}.no{color:var(--no)}.acc{color:var(--gold)} | |
| .stream{height:100%;display:flex;flex-direction:column} | |
| .said{flex:1;overflow:auto;white-space:pre-wrap;word-break:break-word} | |
| .said div{padding:2px 0} | |
| .said i{font-style:normal;color:var(--dim)} | |
| .chip{height:17px;width:44px;border-radius:5px;border:1px solid var(--line); | |
| display:inline-block;vertical-align:middle;margin:2px 4px 2px 0;cursor:pointer} | |
| h4{margin:9px 0 5px;font-size:10.5px;letter-spacing:1.4px;text-transform:uppercase; | |
| color:var(--dim);font-weight:400;border-top:1px solid var(--line);padding-top:8px} | |
| h4:first-child{border-top:0;padding-top:0;margin-top:0} | |
| p{color:#c9c0d4;margin:6px 0}b{color:#fff}small{color:var(--dim)} | |
| canvas.paint{width:100%;height:100%;display:block;background:#050309; | |
| border-radius:8px;touch-action:none} | |
| #toast{position:fixed;left:50%;top:14px;transform:translateX(-50%);z-index:9999; | |
| background:rgba(20,14,34,.95);border:1px solid var(--line);border-radius:10px; | |
| padding:9px 15px;font-size:12px;opacity:0;transition:opacity .3s;pointer-events:none} | |
| </style> | |
| <canvas id="stars"></canvas> | |
| <div id="field"></div> | |
| <div id="sill"> | |
| <button id="d-open" class="lit">β open</button> | |
| <button data-door="doors">doors</button> | |
| <button data-door="people">people</button> | |
| <button data-door="say">say</button> | |
| <button data-door="paint">paint</button> | |
| <button data-door="carry">carry</button> | |
| <button data-door="play">play</button> | |
| <button data-door="rooms">rooms</button> | |
| <button data-door="all">β all</button> | |
| <button data-door="what">what is this</button> | |
| <span class="me"><span class="key" id="mykey"></span><span id="myid">β</span> | |
| <span id="mypeers" class="ok">0</span></span> | |
| </div> | |
| <div id="toast"></div> | |
| <script> | |
| /* ========================================================================= | |
| lightDOORs. A door is a label, not a socket β you can open as many as you | |
| like and it costs nothing, because there was never a socket per door. | |
| Nothing here is stored on anyone's machine but yours. | |
| ========================================================================= */ | |
| const ICE={iceServers:[{urls:["stun:stun.l.google.com:19302", | |
| "stun:stun1.l.google.com:19302"]}]}; | |
| const $=s=>document.querySelector(s); | |
| const peers=new Map(); /* colour -> {pc, ctl, doors:Map} */ | |
| const pending=new Map(); | |
| const seen=new Set(); | |
| const doors=new Map(); /* door name -> [{from,body,t}] */ | |
| const strokes=new Map(); /* door name -> [[x,y,px,py,colour]] */ | |
| const carried=[]; /* things that arrived */ | |
| let MYID=null, bootPc=null, isHost=false, z=10; | |
| let held=new Set(); | |
| try{held=new Set(JSON.parse(localStorage.getItem("ld.colours")||"[]"))}catch(e){} | |
| let myDoors=[]; | |
| try{myDoors=JSON.parse(localStorage.getItem("ld.doors")||'["light"]')}catch(e){myDoors=["light"]} | |
| if(!myDoors.length) myDoors=["light"]; | |
| const saveDoors=()=>{try{localStorage.setItem("ld.doors",JSON.stringify(myDoors))}catch(e){}}; | |
| /* ---- colour is the address; the address is the content ---- */ | |
| async function sha(s){ | |
| const d=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(s)); | |
| return [...new Uint8Array(d)].map(x=>x.toString(16).padStart(2,"0")).join(""); | |
| } | |
| async function shaBytes(u){ | |
| const d=await crypto.subtle.digest("SHA-256",u); | |
| return [...new Uint8Array(d)].map(x=>x.toString(16).padStart(2,"0")).join(""); | |
| } | |
| /* THE QUANT. Same law as the shipped engine, verified byte-identical on 40 | |
| cases: JL count-sketch into 1024 buckets, then the fixed 3,200-byte tail of | |
| turbo(1024) + signs(128) + zeta(1024) + hist(1024). O(N) in, O(1) out. */ | |
| function quant(bytes){ | |
| const D=1024, pad=(8-(bytes.length%8))%8; | |
| const b=new Uint8Array(bytes.length+pad); b.set(bytes); | |
| const f=new Float64Array(b.buffer), proj=new Float64Array(D); | |
| for(let i=0;i<f.length;i++){ | |
| const h=(Math.imul(i,2654435761))>>>0, v=f[i]; | |
| if(!Number.isFinite(v)) continue; | |
| proj[h&(D-1)] += (h&0x80000000)? -v : v; | |
| } | |
| let max=1e-12; | |
| for(let j=0;j<D;j++){const a=Math.abs(proj[j]); if(a>max)max=a;} | |
| const turbo=new Int8Array(D), signs=new Uint8Array(D>>3), | |
| zeta=new Uint8Array(D), hist=new Uint32Array(256); | |
| for(let j=0;j<D;j++){ | |
| const v=proj[j]/max, q=Math.round(v*127), a=Math.abs(v); | |
| turbo[j]=q; | |
| if(v<0) signs[j>>3] |= (1<<(j&7)); | |
| zeta[j]= a<1e-9 ? 15 : Math.min(15, -Math.log2(a)|0); | |
| hist[(q+128)&255]+=1; | |
| } | |
| const out=new Uint8Array(3200); | |
| out.set(new Uint8Array(turbo.buffer),0); out.set(signs,1024); | |
| out.set(zeta,1152); out.set(new Uint8Array(hist.buffer),2176); | |
| return out; | |
| } | |
| function playIn(ch){ | |
| ch.binaryType="arraybuffer"; | |
| ch.onmessage=async e=>{ | |
| if(typeof e.data==="string") return; | |
| const t=new Uint8Array(e.data); | |
| if(t.length!==3200){ toast("not a tuple ("+t.length+" B)"); return; } | |
| const h=await shaBytes(t); absorb(h.slice(0,16)); | |
| const p=pane("play","play",DOORS.play,420,380); | |
| if(p._show){ p._show(t); p._t=t; | |
| p.querySelector("#pin").textContent="played to you"; | |
| p.querySelector("#pout").textContent="3,200 B"; | |
| p.querySelector("#psha").textContent=h.slice(0,16); } | |
| toast("played Β· "+h.slice(0,10)); | |
| }; | |
| } | |
| const cl=(v,a,b)=>Math.max(a,Math.min(b,v)); | |
| function stops(h){ | |
| h=(h||"")+"000000000000"; | |
| const p=i=>cl(parseInt(h.substr(i,2),16)||16,16,240); | |
| const lum=c=>0.2126*c[0]+0.7152*c[1]+0.0722*c[2]; | |
| const fix=c=>{const L=lum(c);return (L>=32&&L<=224)?c: | |
| c.map(x=>Math.round(cl(x*128/Math.max(1,L),0,255)))}; | |
| return [fix([p(0),p(2),p(4)]),fix([p(6),p(8),p(10)])]; | |
| } | |
| const grad=h=>{const[a,b]=stops(h); | |
| return "linear-gradient(90deg,rgb("+a+"),rgb("+b+"))"}; | |
| const ink=h=>"rgb("+stops(h)[1]+")"; | |
| const absorb=c=>{ if(!c||c===MYID||held.has(c)) return; | |
| held.add(c); try{localStorage.setItem("ld.colours",JSON.stringify([...held]))}catch(e){} | |
| refresh("people"); }; | |
| const toast=t=>{const e=$("#toast");e.textContent=t;e.style.opacity=1; | |
| clearTimeout(e._t);e._t=setTimeout(()=>e.style.opacity=0,2300)}; | |
| /* =========================== panes ====================================== */ | |
| const open=new Map(); | |
| function pane(id,title,build,w,h){ | |
| if(open.has(id)){ const p=open.get(id); p.style.zIndex=++z; lit(); return p; } | |
| const p=document.createElement("div"); p.className="pane"; p.style.zIndex=++z; | |
| const n=open.size, vw=innerWidth, vh=innerHeight; | |
| w=Math.min(w||330,vw-24); h=Math.min(h||300,vh-110); | |
| p.style.left=Math.min(vw-w-12,18+n*26)+"px"; | |
| p.style.top =Math.min(vh-h-70,18+n*24)+"px"; | |
| p.style.width=w+"px"; p.style.height=h+"px"; | |
| p.innerHTML='<div class=bar><b></b>'+ | |
| '<button class="dot m"></button><button class="dot x"></button></div>'+ | |
| '<div class=body></div><div class=grip></div>'; | |
| p.querySelector("b").textContent=title; | |
| build(p.querySelector(".body"),p); | |
| p.onpointerdown=()=>{p.style.zIndex=++z;lit()}; | |
| drag(p.querySelector(".bar"),p); resize(p.querySelector(".grip"),p); | |
| p.querySelector(".dot.x").onclick=e=>{e.stopPropagation();p.remove();open.delete(id);lit()}; | |
| p.querySelector(".dot.m").onclick=e=>{e.stopPropagation(); | |
| const b=p.querySelector(".body"); | |
| const min=b.style.display!=="none"; | |
| b.style.display=min?"none":"";p.querySelector(".grip").style.display=min?"none":""; | |
| p._h=p._h||p.style.height; p.style.height=min?"auto":p._h}; | |
| $("#field").appendChild(p); open.set(id,p); lit(); | |
| return p; | |
| } | |
| function lit(){ | |
| document.querySelectorAll("#sill button[data-door]").forEach(b=> | |
| b.classList.toggle("lit",open.has(b.dataset.door))); | |
| } | |
| function drag(h,p){ | |
| h.onpointerdown=e=>{ if(e.target.classList.contains("dot"))return; | |
| h.setPointerCapture(e.pointerId); p.style.zIndex=++z; | |
| const sx=e.clientX-p.offsetLeft, sy=e.clientY-p.offsetTop; | |
| h.onpointermove=m=>{ | |
| p.style.left=cl(m.clientX-sx,-40,innerWidth-70)+"px"; | |
| p.style.top =cl(m.clientY-sy,0,innerHeight-70)+"px"; }; | |
| h.onpointerup=()=>{h.onpointermove=null;h.onpointerup=null}; }; | |
| } | |
| function resize(g,p){ | |
| g.onpointerdown=e=>{ e.stopPropagation(); g.setPointerCapture(e.pointerId); | |
| const sw=p.offsetWidth-e.clientX, sh=p.offsetHeight-e.clientY; | |
| g.onpointermove=m=>{ p.style.width=Math.max(230,sw+m.clientX)+"px"; | |
| p.style.height=Math.max(150,sh+m.clientY)+"px"; p._h=p.style.height; | |
| if(p._fit)p._fit(); }; | |
| g.onpointerup=()=>{g.onpointermove=null;g.onpointerup=null}; }; | |
| } | |
| const refresh=id=>{ const p=open.get(id); if(p&&p._draw) p._draw(); }; | |
| /* =========================== the doors ================================== */ | |
| function doorPane(name){ | |
| pane("door:"+name,"door Β· "+name,(b,p)=>{ | |
| b.innerHTML='<div class=stream><div class=said></div>'+ | |
| '<input placeholder="say it into this door"></div>'; | |
| b.style.padding="8px"; | |
| const said=b.querySelector(".said"), inp=b.querySelector("input"); | |
| p._draw=()=>{ | |
| said.innerHTML=(doors.get(name)||[]).slice(-300).map(m=> | |
| '<div><span class=key style="background:'+grad(m.from)+ | |
| '"></span> <i>'+(m.from===MYID?"you":m.from.slice(0,6))+'</i> '+ | |
| esc(m.body)+'</div>').join(""); | |
| said.scrollTop=said.scrollHeight; }; | |
| inp.onkeydown=e=>{ if(e.key!=="Enter")return; | |
| const v=inp.value.trim(); if(!v)return; inp.value=""; | |
| speak(name,v); }; | |
| p._draw(); | |
| },380,300); | |
| } | |
| const esc=s=>s.replace(/[&<>]/g,c=>({"&":"&","<":"<",">":">"}[c])); | |
| const DOORS={ | |
| doors:(b,p)=>{ | |
| p._draw=()=>{ | |
| b.innerHTML="<h4>your doors β a door is a label, not a socket</h4>"+ | |
| myDoors.map(d=>'<button class="btn g" data-d="'+d+'">'+d+'</button>').join("")+ | |
| '<h4>cut a new one</h4><input id=nd placeholder="name it β anything">'+ | |
| '<button class="btn" id=cut>cut the door</button>'+ | |
| '<p><small>Opening one costs nothing. There was never a socket per door β '+ | |
| 'the name <b>is</b> the route, so the count has no ceiling.</small></p>'; | |
| b.querySelectorAll("[data-d]").forEach(x=>x.onclick=()=>doorPane(x.dataset.d)); | |
| b.querySelector("#cut").onclick=()=>{ | |
| const v=b.querySelector("#nd").value.trim().toLowerCase().replace(/\s+/g,"-"); | |
| if(!v)return; if(!myDoors.includes(v)){myDoors.push(v);saveDoors()} | |
| p._draw(); doorPane(v); openDoorEverywhere(v); }; | |
| }; | |
| p._draw(); | |
| }, | |
| people:(b,p)=>{ | |
| p._draw=()=>{ | |
| const live=[...peers.keys()]; | |
| b.innerHTML="<h4>here now</h4>"+ | |
| (live.length?live.map(c=>'<span class=chip title="'+c+'" style="background:'+ | |
| grad(c)+'"></span>').join(""):"<small>nobody yet</small>")+ | |
| "<h4>colours you carry β "+held.size+"</h4>"+ | |
| (held.size?[...held].map(c=>'<span class=chip title="'+c+'" style="background:'+ | |
| grad(c)+'"></span>').join(""):"<small>none yet</small>")+ | |
| "<p><small>The colour outlives the link. Nothing here is a contact list β "+ | |
| "there is no entry to delete, because there was never an entry.</small></p>"; | |
| }; | |
| p._draw(); | |
| }, | |
| say:(b,p)=>{ b.innerHTML=""; | |
| const sel=document.createElement("select"); | |
| const redraw=()=>{ sel.innerHTML=myDoors.map(d=>"<option>"+d+"</option>").join("") }; | |
| redraw(); b.appendChild(sel); | |
| const s=document.createElement("div"); s.className="said"; | |
| s.style.cssText="height:calc(100% - 92px);overflow:auto"; b.appendChild(s); | |
| const i=document.createElement("input"); i.placeholder="say it"; b.appendChild(i); | |
| p._draw=()=>{ redraw(); | |
| s.innerHTML=(doors.get(sel.value)||[]).slice(-300).map(m=> | |
| '<div><span class=key style="background:'+grad(m.from)+'"></span> <i>'+ | |
| (m.from===MYID?"you":m.from.slice(0,6))+'</i> '+esc(m.body)+'</div>').join(""); | |
| s.scrollTop=s.scrollHeight; }; | |
| sel.onchange=p._draw; | |
| i.onkeydown=e=>{ if(e.key!=="Enter")return; const v=i.value.trim(); | |
| if(!v)return; i.value=""; speak(sel.value,v); }; | |
| p._draw(); | |
| }, | |
| paint:(b,p)=>{ | |
| b.style.padding="6px"; b.innerHTML='<canvas class=paint></canvas>'; | |
| const c=b.querySelector("canvas"), g=c.getContext("2d"); | |
| let last=null; | |
| p._fit=()=>{ const r=c.getBoundingClientRect(); | |
| c.width=Math.max(2,r.width*2); c.height=Math.max(2,r.height*2); p._draw(); }; | |
| p._draw=()=>{ g.fillStyle="#050309"; g.fillRect(0,0,c.width,c.height); | |
| g.lineCap="round"; | |
| for(const s of (strokes.get("light")||[])){ | |
| g.strokeStyle=ink(s[4]); g.lineWidth=5; | |
| g.beginPath(); g.moveTo(s[2]*c.width,s[3]*c.height); | |
| g.lineTo(s[0]*c.width,s[1]*c.height); g.stroke(); } }; | |
| const at=e=>{const r=c.getBoundingClientRect(); | |
| return [(e.clientX-r.left)/r.width,(e.clientY-r.top)/r.height]}; | |
| c.onpointerdown=e=>{ c.setPointerCapture(e.pointerId); last=at(e); }; | |
| c.onpointermove=e=>{ if(!last)return; const n=at(e); | |
| addStroke("light",[n[0],n[1],last[0],last[1],MYID]); | |
| gossip("light","STROKE|from="+MYID+"|s="+ | |
| [n[0],n[1],last[0],last[1]].map(v=>v.toFixed(4)).join(",")+"|json=0"); | |
| last=n; }; | |
| c.onpointerup=()=>last=null; | |
| setTimeout(p._fit,60); | |
| }, | |
| carry:(b,p)=>{ | |
| p._draw=()=>{ | |
| b.innerHTML="<h4>hand something over</h4>"+ | |
| '<input type=file id=f multiple>'+ | |
| "<h4>what arrived β "+carried.length+"</h4>"+ | |
| (carried.length?carried.map((c,i)=> | |
| '<div class=row><span>'+esc(c.name)+'</span>'+ | |
| '<a class=acc download="'+esc(c.name)+'" href="'+c.url+'">keep it</a></div>' | |
| ).join(""):"<small>nothing yet</small>")+ | |
| "<p><small>It goes down the same line as the words. Nobody hosts it, "+ | |
| "nobody stores it, and it never leaves the two machines.</small></p>"; | |
| b.querySelector("#f").onchange=e=>{ for(const f of e.target.files) handOver(f); }; | |
| }; | |
| p._draw(); | |
| }, | |
| rooms:(b,p)=>{ | |
| b.style.padding="8px"; | |
| b.innerHTML="<h4>a private room only people who know the word can enter</h4>"+ | |
| '<input id=rn placeholder="a word — or a whole phrase" value="light">'+ | |
| '<canvas id=rc style="width:100%;height:120px;display:block;'+ | |
| 'border:1px solid var(--line);border-radius:8px;margin:6px 0"></canvas>'+ | |
| '<div class=row><span class=k>this room's address</span><span id=ra class=acc>—</span></div>'+ | |
| '<button class="btn" id=renter>enter this room</button>'+ | |
| '<button class="btn g" id=rgo>just look at it</button>'+ | |
| "<p><small>The room's address is <b>sha256 of your word</b>. Type the same "+ | |
| "word as someone else and you land in the <b>same room</b> — talk, draw, "+ | |
| "hand files over. Type a different word and you are somewhere else entirely. "+ | |
| "<b>Nobody who doesn't know the word can find it</b>: there is no list of "+ | |
| "rooms to browse, and the address gives the word away no more than a lock gives "+ | |
| "away its key. No server, no account, no directory.</small></p>"+ | |
| "<p><small>Same law as the registration office — a room 10^30 places along "+ | |
| "is reached as fast as the first, because there is no table to walk.</small></p>"; | |
| const CUBE="b40b3e1726b0e81d"; | |
| const c=b.querySelector("#rc"), g=c.getContext("2d"); | |
| p._fit=()=>{const r=c.getBoundingClientRect(); | |
| c.width=Math.max(2,r.width*2); c.height=Math.max(2,r.height*2); if(p._t)show(p._t);}; | |
| function show(t){ p._t=t; | |
| const W=c.width,H=c.height,N=32,cw=W/N,ch=H/N; | |
| g.fillStyle="#050309"; g.fillRect(0,0,W,H); | |
| for(let i=0;i<1024;i++){ | |
| const R=Math.min(255,Math.abs(t[i]<<24>>24)*2), G=(15-t[1152+i])*17, B=t[2176+i]; | |
| const neg=(t[1024+(i>>3)]>>(i&7))&1; | |
| g.fillStyle="rgb("+(neg?B:R)+","+G+","+(neg?R:B)+")"; | |
| g.fillRect((i%N)*cw,((i/N)|0)*ch,cw+1,ch+1); | |
| } | |
| } | |
| async function addrOf(word){ return await sha(CUBE+"|"+word); } | |
| async function look(word){ | |
| const full=await addrOf(word); | |
| b.querySelector("#ra").textContent=full.slice(0,16); | |
| show(quant(new TextEncoder().encode(full))); | |
| return full; | |
| } | |
| async function enter(word){ | |
| if(!word){ toast("type a word first"); return; } | |
| const full=await look(word); | |
| const room="r."+full.slice(0,12); /* the door IS the address */ | |
| if(!myDoors.includes(room)){ myDoors.push(room); saveDoors(); } | |
| if(!doors.has(room)) doors.set(room,[]); | |
| doorPane(room); /* live, synced via the mesh */ | |
| openDoorEverywhere(room); | |
| absorb(full.slice(0,16)); | |
| toast(peers.size ? "entered — "+peers.size+" boat(s) can meet you here" | |
| : "entered — give this word to who you want here"); | |
| } | |
| b.querySelector("#renter").onclick=()=>enter((b.querySelector("#rn").value||"").trim()); | |
| b.querySelector("#rgo").onclick=()=>look((b.querySelector("#rn").value||"light").trim()); | |
| b.querySelector("#rn").onkeydown=e=>{ if(e.key==="Enter") enter((e.target.value||"").trim()); }; | |
| setTimeout(()=>{p._fit(); look("light");},60); | |
| }, | |
| play:(b,p)=>{ | |
| b.style.padding="8px"; | |
| b.innerHTML="<h4>hand anything in β it leaves as 3.1 KB</h4>"+ | |
| '<input type=file id=pf><input id=pt placeholder="or type something">'+ | |
| '<canvas id=pc style="width:100%;height:150px;display:block;'+ | |
| 'border:1px solid var(--line);border-radius:8px;margin:6px 0"></canvas>'+ | |
| '<div class=row><span class=k>in</span><span id=pin>β</span></div>'+ | |
| '<div class=row><span class=k>out</span><span class=acc id=pout>β</span></div>'+ | |
| '<div class=row><span class=k>colour</span><span id=psha class=acc>β</span></div>'+ | |
| '<button class="btn" id=psend>play it to everyone</button>'+ | |
| "<p><small>The tuple is <b>3,200 bytes whatever you hand it</b> β the "+ | |
| "same four bands the engine writes. What crosses is the light, "+ | |
| "<b>not the file</b>. Nothing is saved on either side.</small></p>"; | |
| const c=b.querySelector("#pc"), g=c.getContext("2d"); | |
| p._fit=()=>{const r=c.getBoundingClientRect(); | |
| c.width=Math.max(2,r.width*2); c.height=Math.max(2,r.height*2); if(p._last)show(p._last)}; | |
| function show(t){ | |
| p._last=t; | |
| const W=c.width,H=c.height,N=32,cw=W/N,ch=H/N; | |
| g.fillStyle="#050309"; g.fillRect(0,0,W,H); | |
| for(let i=0;i<1024;i++){ | |
| const R=Math.min(255,Math.abs(t[i]<<24>>24)*2); /* turbo, signed */ | |
| const G=(15-t[1152+i])*17; /* zeta */ | |
| const B=t[2176+i]; /* hist bytes */ | |
| const neg=(t[1024+(i>>3)]>>(i&7))&1; /* signs = the anti */ | |
| g.fillStyle="rgb("+(neg?B:R)+","+G+","+(neg?R:B)+")"; | |
| g.fillRect((i%N)*cw,((i/N)|0)*ch,cw+1,ch+1); | |
| } | |
| } | |
| p._show=show; | |
| async function take(bytes,label){ | |
| const t=quant(bytes); | |
| const h=await shaBytes(t); | |
| b.querySelector("#pin").textContent=label+" Β· "+bytes.length.toLocaleString()+" B"; | |
| b.querySelector("#pout").textContent=t.length.toLocaleString()+" B"; | |
| b.querySelector("#psha").textContent=h.slice(0,16); | |
| show(t); p._t=t; absorb(h.slice(0,16)); | |
| } | |
| b.querySelector("#pf").onchange=async e=>{ const f=e.target.files[0]; if(!f)return; | |
| take(new Uint8Array(await f.arrayBuffer()), f.name); }; | |
| b.querySelector("#pt").onkeydown=e=>{ if(e.key!=="Enter")return; | |
| take(new TextEncoder().encode(e.target.value), "typed"); }; | |
| b.querySelector("#psend").onclick=()=>{ | |
| if(!p._t){ toast("hand something in first"); return; } | |
| if(!peers.size){ toast("nobody to play it to yet"); return; } | |
| for(const [,rec] of peers){ | |
| const ch=rec.pc.createDataChannel("play:"+Date.now()); | |
| ch.binaryType="arraybuffer"; | |
| ch.onopen=()=>{ ch.send(p._t.buffer.slice(0)); setTimeout(()=>ch.close(),300); }; | |
| } | |
| toast("played Β· 3,200 bytes to "+peers.size); | |
| }; | |
| setTimeout(p._fit,60); | |
| }, | |
| all:(b,p)=>{ | |
| const go=u=>"onclick=\"window.open('"+u+"','_blank','noopener')\""; | |
| b.innerHTML= | |
| "<h4>every light, one door</h4>"+ | |
| "<p>This hub is one room. <b>The bridge is the door to all of them</b> β "+ | |
| "every public light on the account in one place, so none of it gets lost "+ | |
| "like Tesla.</p>"+ | |
| '<button class="btn" '+go("https://jessebrown1980.github.io/the-bridge-to-all-of-it/")+ | |
| '>open the bridge → all 182</button>'+ | |
| "<h4>the nearest few</h4>"+ | |
| '<button class="btn g" '+go("https://jessebrown1980.github.io/lightDOORwsMs/")+'>this hub</button>'+ | |
| '<button class="btn g" '+go("https://jessebrown1980.github.io/the-light-boat/")+'>the light boat</button>'+ | |
| '<button class="btn g" '+go("https://jessebrown1980.github.io/Be-infinitly-express-spherically-free/")+'>the sphere</button>'+ | |
| '<button class="btn g" '+go("https://jessebrown1980.github.io/the-null-space-or-NO-jo0O/")+'>the null space</button>'+ | |
| '<button class="btn g" '+go("https://jessebrown1980.github.io/the-zebras-z-is-the-squared-wave/")+'>the zebra</button>'+ | |
| "<p><small>Photons are the same photon with different colours and oils; "+ | |
| "these are the same light with different doors. The bridge holds them all, "+ | |
| "and the bridge points back here — so from any one you can reach every "+ | |
| "other.</small></p>"; | |
| }, | |
| what:(b)=>{ b.innerHTML= | |
| "<h4>a door is a label, not a socket</h4>"+ | |
| "<p>Cut as many as you like. The name <b>is</b> the route, so there is no "+ | |
| "count to run out of β that ceiling only exists where somebody hands out "+ | |
| "numbered slots.</p>"+ | |
| "<h4>the colour is the address</h4>"+ | |
| "<p>Your colour is the sha-256 of a secret this browser made for itself the "+ | |
| "first time you opened the page β two brown stops, banded 16β¦240, "+ | |
| "luminance-clamped 32β¦224. <b>The secret never leaves; only the colour does.</b> "+ | |
| "Nobody handed you the name, so nobody can take it away, and it does not "+ | |
| "change when a link does.</p>"+ | |
| "<h4>the trace is spent, the colour is kept</h4>"+ | |
| "<p>The block you pass at the first meeting is a <b>one-time introduction</b> "+ | |
| "and this page throws it away on contact. What you keep is the colour. Break "+ | |
| "every link at once and it reassembles, because what survived was never the "+ | |
| "wiring.</p>"+ | |
| "<h4>what is free, and the one thing that isn't</h4>"+ | |
| "<p>No server of ours touches any of it β words, drawings, files, all of it "+ | |
| "goes browser to browser with nothing in the middle to store or relay. The one "+ | |
| "thing that isn't free, stated rather than hidden: to find a route across the "+ | |
| "internet, browsers ask a public <b>STUN</b> server for their own outside "+ | |
| "address. Free ones. <b>They see that two addresses asked; they never see a "+ | |
| "word of what you say.</b> On the same wifi β or down a USB cable β it doesn't "+ | |
| "need them at all.</p>"+ | |
| "<h4>the first meeting is still by hand</h4>"+ | |
| "<p>Everything after it travels the links that already exist: you are "+ | |
| "introduced by somebody who already knows you both. That is the only "+ | |
| "directory there is, and it is not a list anyone can seize.</p>" } | |
| }; | |
| /* =========================== traffic ==================================== */ | |
| function addStroke(door,s){ | |
| if(!strokes.has(door)) strokes.set(door,[]); | |
| const a=strokes.get(door); a.push(s); if(a.length>4000)a.shift(); | |
| refresh("paint"); | |
| } | |
| function record(door,from,body){ | |
| if(!doors.has(door)) doors.set(door,[]); | |
| doors.get(door).push({from,body}); | |
| refresh("door:"+door); refresh("say"); | |
| } | |
| async function mid(){ return (await sha(MYID+Math.random()+performance.now())).slice(0,10) } | |
| async function speak(door,text){ | |
| const id=await mid(); seen.add(id); | |
| record(door,MYID,text); | |
| fan(door,"SAY|from="+MYID+"|mid="+id+"|body="+text+"|json=0"); | |
| } | |
| async function gossip(door,row){ | |
| const id=await mid(); seen.add(id); | |
| fan(door,row.replace("|json=0","|mid="+id+"|json=0")); | |
| } | |
| function fan(door,row,except){ | |
| for(const [pid,rec] of peers){ if(pid===except) continue; | |
| let ch=rec.doors.get(door); | |
| if(!ch){ ch=rec.pc.createDataChannel("door:"+door); | |
| ch.onmessage=e=>onDoor(pid,door,e.data); rec.doors.set(door,ch); } | |
| try{ if(ch.readyState==="open") ch.send(row) }catch(e){} | |
| } | |
| } | |
| function openDoorEverywhere(door){ fan(door,"OPEN|from="+MYID+"|json=0") } | |
| function onDoor(from,door,row){ | |
| const f={}; row.split("|").forEach(kv=>{const i=kv.indexOf("="); | |
| if(i>0) f[kv.slice(0,i)]=kv.slice(i+1)}); | |
| if(f.mid){ if(seen.has(f.mid)) return; seen.add(f.mid); } | |
| absorb(f.from); | |
| const tag=row.split("|")[0]; | |
| if(tag==="SAY"){ record(door,f.from,f.body); | |
| if(!open.has("door:"+door)&&!open.has("say")) toast(door+" Β· "+f.body.slice(0,44)); } | |
| else if(tag==="STROKE"){ const n=f.s.split(",").map(Number); | |
| addStroke(door,[n[0],n[1],n[2],n[3],f.from]); } | |
| if(!myDoors.includes(door)){ myDoors.push(door); saveDoors(); refresh("doors"); } | |
| fan(door,row,from); | |
| } | |
| /* ---- handing a thing over: its own channel, closed when done ---- */ | |
| async function handOver(file){ | |
| if(!peers.size){ toast("nobody to hand it to yet"); return } | |
| const fid=(await sha(file.name+file.size+Math.random())).slice(0,10); | |
| for(const [pid,rec] of peers){ | |
| const ch=rec.pc.createDataChannel("give:"+fid); | |
| ch.binaryType="arraybuffer"; | |
| ch.onopen=async()=>{ | |
| ch.send("GIVE|name="+file.name+"|size="+file.size+"|json=0"); | |
| const buf=await file.arrayBuffer(); const CH=16384; | |
| for(let o=0;o<buf.byteLength;o+=CH){ | |
| while(ch.bufferedAmount>1<<20) await new Promise(r=>setTimeout(r,25)); | |
| ch.send(buf.slice(o,o+CH)); | |
| } | |
| ch.send("DONE"); setTimeout(()=>ch.close(),400); | |
| }; | |
| } | |
| toast("handed over Β· "+file.name); | |
| } | |
| function takeGift(ch){ | |
| ch.binaryType="arraybuffer"; | |
| let name="a thing", parts=[]; | |
| ch.onmessage=e=>{ | |
| if(typeof e.data==="string"){ | |
| if(e.data.startsWith("GIVE|")){ | |
| name=(e.data.match(/name=([^|]*)/)||[])[1]||name; return; } | |
| if(e.data==="DONE"){ | |
| const url=URL.createObjectURL(new Blob(parts)); | |
| carried.push({name,url}); refresh("carry"); | |
| toast("arrived Β· "+name); parts=[]; } | |
| return; | |
| } | |
| parts.push(e.data); | |
| }; | |
| } | |
| /* =========================== the mesh =================================== */ | |
| function reduce(d){ | |
| const s=d.sdp, g=re=>{const m=s.match(re);return m?m[1]:""}; | |
| const c=[...s.matchAll(/a=candidate:(\S+ \d+ \S+ \d+ \S+ \d+ typ \S+)/g)] | |
| .map(m=>m[1].replace(/ generation.*$/,"")); | |
| return [d.type==="offer"?"O":"A",g(/a=ice-ufrag:(\S+)/),g(/a=ice-pwd:(\S+)/), | |
| g(/a=fingerprint:sha-256 (\S+)/).replace(/:/g,""),g(/a=setup:(\S+)/), | |
| c.join("~")].join("|"); | |
| } | |
| function expand(str){ | |
| const [t,uf,pw,fp,su,c]=str.trim().split("|"); | |
| const cands=(c||"").split("~").filter(Boolean) | |
| .map(x=>"a=candidate:"+x+" generation 0").join("\r\n"); | |
| return {type:t==="O"?"offer":"answer",sdp: | |
| "v=0\r\no=- 1 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=group:BUNDLE 0\r\n"+ | |
| "a=extmap-allow-mixed\r\na=msid-semantic: WMS\r\n"+ | |
| "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\n"+ | |
| (cands?cands+"\r\n":"")+"a=ice-ufrag:"+uf+"\r\na=ice-pwd:"+pw+"\r\n"+ | |
| "a=ice-options:trickle\r\na=fingerprint:sha-256 "+fp.match(/.{2}/g).join(":")+ | |
| "\r\na=setup:"+su+"\r\na=mid:0\r\na=sctp-port:5000\r\n"+ | |
| "a=max-message-size:262144\r\n"}; | |
| } | |
| const gather=pc=>new Promise(r=>{ | |
| if(pc.iceGatheringState==="complete") return r(); | |
| pc.onicecandidate=e=>{ if(!e.candidate) r() }; setTimeout(r,3000); }); | |
| const send=(c,t)=>{try{if(c&&c.readyState==="open")c.send(t)}catch(e){}}; | |
| function greet(pc,ctl){ | |
| const hi=()=>send(ctl,"IAM|id="+MYID+"|json=0"); | |
| ctl.onopen=hi; if(ctl.readyState==="open") hi(); | |
| ctl.onmessage=e=>{ const m=e.data.match(/^IAM\|id=([0-9a-f]+)/); | |
| if(!m) return; | |
| /* the same colour on both ends is not two boats β it is one machine | |
| talking to itself (a second tab of the same browser). Say so. */ | |
| if(m[1]===MYID){ toast("that is this same browser β same colour, same boat"); | |
| try{pc.close()}catch(e){} return; } | |
| if(!peers.has(m[1])) attach(m[1],pc,ctl); }; | |
| pc.ondatachannel=e=>{ const L=e.channel.label; | |
| if(L.startsWith("give:")) takeGift(e.channel); | |
| else if(L.startsWith("play:")) playIn(e.channel); }; | |
| } | |
| function attach(id,pc,ctl){ | |
| const rec={pc,ctl,doors:new Map()}; peers.set(id,rec); | |
| absorb(id); spend(); | |
| ctl.onmessage=e=>onCtl(id,e.data); | |
| ctl.onclose=()=>{peers.delete(id);count();refresh("people"); | |
| toast(id.slice(0,6)+" unlinked Β· colour kept")}; | |
| pc.ondatachannel=e=>{ | |
| const L=e.channel.label; | |
| if(L.startsWith("door:")){ const d=L.slice(5); | |
| e.channel.onmessage=ev=>onDoor(id,d,ev.data); rec.doors.set(d,e.channel); | |
| if(!myDoors.includes(d)){myDoors.push(d);saveDoors();refresh("doors")} } | |
| else if(L.startsWith("give:")) takeGift(e.channel); | |
| else if(L.startsWith("play:")) playIn(e.channel); | |
| }; | |
| for(const d of myDoors) fan(d,"OPEN|from="+MYID+"|json=0"); | |
| count(); refresh("people"); toast(id.slice(0,6)+" Β· colour absorbed"); | |
| const others=[...peers.keys()].filter(x=>x!==id); | |
| if(others.length) send(ctl,"HELLO|id="+MYID+"|peers="+others.join(",")+"|json=0"); | |
| } | |
| function spend(){ const p=open.get("open"); | |
| if(p){ const a=p.querySelector("#mine"),b=p.querySelector("#theirs"); | |
| if(a)a.value=""; if(b)b.value=""; if(p._spent)p._spent(); } } | |
| const count=()=>{ $("#mypeers").textContent=peers.size }; | |
| async function onCtl(from,row){ | |
| const f={}; row.split("|").forEach(kv=>{const i=kv.indexOf("="); | |
| if(i>0) f[kv.slice(0,i)]=kv.slice(i+1)}); | |
| const tag=row.split("|")[0]; | |
| if(tag==="HELLO"){ | |
| for(const p of (f.peers||"").split(",").filter(Boolean)){ | |
| if(p===MYID||peers.has(p)||pending.has(p)) continue; | |
| const pc=new RTCPeerConnection(ICE); | |
| greet(pc,pc.createDataChannel("ctl")); | |
| await pc.setLocalDescription(await pc.createOffer()); await gather(pc); | |
| pending.set(p,pc); | |
| send(peers.get(from).ctl,"INTRO|to="+p+"|from="+MYID+"|sdp="+ | |
| reduce(pc.localDescription)+"|json=0"); | |
| } | |
| } else if(tag==="INTRO"){ | |
| if(f.to===MYID){ | |
| const pc=new RTCPeerConnection(ICE); | |
| pc.ondatachannel=e=>{ if(e.channel.label==="ctl") greet(pc,e.channel) }; | |
| await pc.setRemoteDescription(expand(f.sdp)); | |
| await pc.setLocalDescription(await pc.createAnswer()); await gather(pc); | |
| send(peers.get(from).ctl,"BACK|to="+f.from+"|from="+MYID+"|sdp="+ | |
| reduce(pc.localDescription)+"|json=0"); | |
| } else if(peers.has(f.to)) send(peers.get(f.to).ctl,row); | |
| } else if(tag==="BACK"){ | |
| if(f.to===MYID&&pending.has(f.from)){ | |
| await pending.get(f.from).setRemoteDescription(expand(f.sdp)); | |
| pending.delete(f.from); | |
| } else if(peers.has(f.to)) send(peers.get(f.to).ctl,row); | |
| } | |
| } | |
| /* =========================== the opening ================================ */ | |
| function openPane(){ | |
| pane("open","the first meeting β by hand, once",(b,p)=>{ | |
| b.innerHTML= | |
| "<p>Give <b>one</b> person your block. After that, everyone else arrives "+ | |
| "through the links that already exist.</p>"+ | |
| '<h4 id=lbl>yours β hand it over</h4><textarea id=mine readonly></textarea>'+ | |
| '<button class="btn g" id=copy>copy</button>'+ | |
| '<button class="btn g" id=mint>mint a fresh one</button>'+ | |
| '<h4>theirs β paste it</h4><textarea id=theirs></textarea>'+ | |
| '<button class="btn" id=take>take it</button>'+ | |
| '<div class=row style="margin-top:8px"><span class=k>traces kept</span>'+ | |
| '<span class=ok id=nt>0</span></div>'; | |
| p._spent=()=>{ b.querySelector("#nt").textContent="0"; | |
| b.querySelector("#lbl").textContent="spent β mint a fresh one for the next person"; }; | |
| b.querySelector("#copy").onclick=()=>{ | |
| b.querySelector("#mine").select(); document.execCommand("copy"); toast("copied"); }; | |
| b.querySelector("#mint").onclick=()=>mint(b); | |
| b.querySelector("#take").onclick=()=>take(b); | |
| mint(b); | |
| },370,430); | |
| } | |
| async function mint(b){ | |
| isHost=true; | |
| bootPc=new RTCPeerConnection(ICE); | |
| greet(bootPc,bootPc.createDataChannel("ctl")); | |
| await bootPc.setLocalDescription(await bootPc.createOffer()); | |
| await gather(bootPc); | |
| const small=reduce(bootPc.localDescription); | |
| b.querySelector("#mine").value=small; | |
| b.querySelector("#nt").textContent="1"; | |
| b.querySelector("#lbl").textContent="yours β one use, for one person"; | |
| if(!MYID) await name(small); | |
| } | |
| async function take(b){ | |
| const raw=b.querySelector("#theirs").value.trim(); if(!raw) return; | |
| let d; try{ d=expand(raw) }catch(e){ toast("that isn't a block"); return } | |
| if(d.type==="answer"&&isHost&&bootPc){ | |
| try{ await bootPc.setRemoteDescription(d); toast("taken Β· opening") } | |
| catch(e){ toast("that block was for a different opening") } | |
| return; | |
| } | |
| const pc=new RTCPeerConnection(ICE); | |
| pc.ondatachannel=e=>{ if(e.channel.label==="ctl") greet(pc,e.channel) }; | |
| await pc.setRemoteDescription(d); | |
| await pc.setLocalDescription(await pc.createAnswer()); await gather(pc); | |
| const small=reduce(pc.localDescription); | |
| bootPc=pc; isHost=false; | |
| if(!MYID) await name(small); | |
| b.querySelector("#mine").value=small; | |
| b.querySelector("#nt").textContent="1"; | |
| b.querySelector("#lbl").textContent="your answer β send it back to them"; | |
| toast("answer ready β send it back"); | |
| } | |
| async function name(seed){ | |
| MYID=(await sha(seed)).slice(0,16); | |
| $("#myid").textContent=MYID.slice(0,10); | |
| $("#mykey").style.background=grad(MYID); | |
| } | |
| /* =========================== the field ================================== */ | |
| const sc=$("#stars"), sg=sc.getContext("2d"); | |
| let SW=0,SH=0,T=0; | |
| function fitStars(){ SW=sc.width=innerWidth*2; SH=sc.height=innerHeight*2 } | |
| addEventListener("resize",()=>{fitStars();for(const p of open.values())p._fit&&p._fit()}); | |
| fitStars(); | |
| function field(){ | |
| T+=0.006; | |
| sg.clearRect(0,0,SW,SH); | |
| const ids=[MYID,...peers.keys(),...held].filter(Boolean); | |
| const uniq=[...new Set(ids)]; | |
| const cx=SW/2, cy=SH*0.42, R=Math.min(SW,SH)*0.34; | |
| uniq.forEach((id,i)=>{ | |
| const a=T+i*(2*Math.PI/Math.max(1,uniq.length)); | |
| const live=id===MYID||peers.has(id); | |
| const x=cx+Math.cos(a)*R*(live?1:1.35); | |
| const y=cy+Math.sin(a)*R*0.5*(live?1:1.35); | |
| const [ca,cb]=stops(id), r=id===MYID?26:live?18:11; | |
| const g=sg.createRadialGradient(x,y,0,x,y,r*3); | |
| g.addColorStop(0,"rgba("+ca+","+(live?.5:.2)+")"); | |
| g.addColorStop(1,"rgba(0,0,0,0)"); | |
| sg.fillStyle=g; sg.beginPath(); sg.arc(x,y,r*3,0,6.2832); sg.fill(); | |
| sg.globalAlpha=live?.85:.34; sg.fillStyle="rgb("+cb+")"; | |
| sg.beginPath(); sg.arc(x,y,r,0,6.2832); sg.fill(); sg.globalAlpha=1; | |
| if(live&&id!==MYID){ sg.strokeStyle="rgba("+cb+",.22)"; sg.lineWidth=2; | |
| sg.beginPath(); sg.moveTo(cx,cy); sg.lineTo(x,y); sg.stroke(); } | |
| }); | |
| requestAnimationFrame(field); | |
| } | |
| requestAnimationFrame(field); | |
| /* =========================== wiring ===================================== */ | |
| $("#d-open").onclick=openPane; | |
| document.querySelectorAll("#sill button[data-door]").forEach(btn=>{ | |
| btn.onclick=()=>{ const d=btn.dataset.door; | |
| if(open.has(d)){ open.get(d).remove(); open.delete(d); lit(); return; } | |
| pane(d,d==="what"?"what is this":d,DOORS[d], | |
| d==="paint"||d==="play"||d==="rooms"?420:340, | |
| d==="what"?420:d==="play"||d==="rooms"?400:320); }; | |
| }); | |
| (async()=>{ /* a colour before anyone arrives, from this browser alone */ | |
| let s=null; try{s=localStorage.getItem("ld.seed")}catch(e){} | |
| if(!s){ s=[...crypto.getRandomValues(new Uint8Array(24))] | |
| .map(x=>x.toString(16).padStart(2,"0")).join(""); | |
| try{localStorage.setItem("ld.seed",s)}catch(e){} } | |
| await name(s); | |
| for(const d of myDoors) if(!doors.has(d)) doors.set(d,[]); | |
| pane("what","what is this",DOORS.what,360,420); | |
| toast("your colour is yours β nobody handed it to you"); | |
| })(); | |
| </script> | |
| <!-- βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| THE CORRIDOR β every door reaches every other door. | |
| Generated 2026-08-07 from a live measurement of all 27 doors. | |
| Pure anchors. No src=, no <link href>, no script, no network at runtime: | |
| this block renders with the network unplugged, and every line in it is | |
| a way out. Paste it directly before </body>. | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ --> | |
| <nav id="corridor" style="margin:3rem auto 2rem;max-width:60rem;padding:1.25rem 1.5rem;border-top:1px solid currentColor;font:14px/1.9 ui-monospace,SFMono-Regular,Menlo,monospace;opacity:.85"> | |
| <div style="letter-spacing:.14em;text-transform:uppercase;opacity:.6;margin-bottom:.75rem">the corridor — 27 doors</div> | |
| <div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(17rem,1fr));gap:.15rem .75rem"> | |
| <a href="https://jessebrown1980.github.io/all-the-doors/">all the doors</a> | |
| <a href="https://jessebrown1980.github.io/asolaria-multiverse/">asolaria multiverse</a> | |
| <a href="https://jessebrown1980.github.io/Be-infinitly-express-spherically-free/">Be infinitly express spherically free</a> | |
| <a href="https://jessebrown1980.github.io/Browns-infinite-play-and-zoom/">Browns infinite play and zoom</a> | |
| <a href="https://jessebrown1980.github.io/does-2-to-the-n-always-contain-a-digit-2-in-base-3/">does 2 to the n always contain a digit 2 in base 3</a> | |
| <a href="https://jessebrown1980.github.io/does-the-closure-survive-81-levels/">does the closure survive 81 levels</a> | |
| <a href="https://jessebrown1980.github.io/free-at-all-levels/">free at all levels</a> | |
| <a href="https://jessebrown1980.github.io/Higgs-Bell-Hilbert-and-Brown-at-the-zero/">Higgs Bell Hilbert and Brown at the zero</a> | |
| <a href="https://jessebrown1980.github.io/identity-kernel-registry/">identity kernel registry</a> | |
| <a href="https://jessebrown1980.github.io/light-boat-engine-ships-with-oils/">light boat engine ships with oils</a> | |
| <a href="https://jessebrown1980.github.io/lightDOORwsMs/">lightDOORwsMs</a> | |
| <a href="https://jessebrown1980.github.io/one-click/">one click</a> | |
| <a href="https://jessebrown1980.github.io/see-her-anywhere-so-not-lost-to-time/">see her anywhere so not lost to time</a> | |
| <a href="https://jessebrown1980.github.io/the-bridge-you-need-three-languages/">the bridge you need three languages</a> | |
| <a href="https://jessebrown1980.github.io/The-Brown-Light-erdos-Engine-block-powered-by-light/">The Brown Light erdos Engine block powered by light</a> | |
| <a href="https://jessebrown1980.github.io/the-browns-solution-to-erods-o0O-nx3-6-for-1-with--1-3/">the browns solution to erods o0O nx3 6 for 1 with 1 3</a> | |
| <a href="https://jessebrown1980.github.io/the-colour-qr/">the colour qr</a> | |
| <a href="https://jessebrown1980.github.io/the-family-of-verses/">the family of verses</a> | |
| <a href="https://jessebrown1980.github.io/the-human-based-understanding/">the human based understanding</a> | |
| <a href="https://jessebrown1980.github.io/the-law-of-space-and-light-and-color-and-travel-at-the-speed-of-one/">the law of space and light and color and travel at the speed of one</a> | |
| <a href="https://jessebrown1980.github.io/the-light-boat/">the light boat</a> | |
| <a href="https://jessebrown1980.github.io/the-light-hub-http-0/">the light hub http 0</a> | |
| <a href="https://jessebrown1980.github.io/the-null-space-or-NO-jo0O/">the null space or NO jo0O</a> | |
| <a href="https://jessebrown1980.github.io/the-zebras-z-is-the-squared-wave/">the zebras z is the squared wave</a> | |
| <a href="https://jessebrown1980.github.io/tribute-three-around-a-free-centre/">tribute three around a free centre</a> | |
| <a href="https://jessebrown1980.github.io/two-fixed-points-have-an-interval/">two fixed points have an interval</a> | |
| <a href="https://jessebrown1980.github.io/why-mycelium-grounded-and-the-ases/">why mycelium grounded and the ases</a> | |
| </div> | |
| <div style="margin-top:1rem;opacity:.5;font-size:12px">the light is the code. every door open, every door reachable.</div> | |
| </nav> | |