memoscan
← all memos

Memo 0xe02a4fd1…7c01c4 on Ethereum

0xb81bf680…7cf2·#25,178,040·view on Etherscan
; (function(){ let seed=Date.now()>>>0;const bcRandom={currentBlockHash:null,setSeed(hash){seed=parseInt(hash.slice(2,10),16)>>>0;bcRandom.currentBlockHash=hash;}};function enableBlockchainRandom(){Math.random=function(){let t=seed+=0x6D2B79F5;t=Math.imul(t^t>>>15,t|1);t^=t+Math.imul(t^t>>>7,t|61);return((t^t>>>14)>>>0)/4294967296;};} const charColor=new Map();let borderC='#fff';let textC='#fff';let topC='#fff';function setCustomColorScheme(scheme,palette){charColor.clear();borderC=scheme.borderColor||scheme.topBottomColor||'#fff';textC=scheme.textColor||scheme.sideColor||'#fff';topC=scheme.topBottomColor||'#fff';const colors=scheme.voidling?Object.values(scheme.voidling):[];const explicit=scheme.voidling||{};if(palette&&colors.length){for(let i=0;i<palette.length;i++){charColor.set(palette[i],colors[i%colors.length]);}} for(const[ch,c]of Object.entries(explicit))charColor.set(ch,c);if(!charColor.has('$'))charColor.set('$',borderC);} const getTopColor=()=>topC;function getColorForChar(ch){return charColor.get(ch)||textC;} const FONT_FAMILY="'Courier New', Courier, monospace";const getFont=()=>{const size=(typeof window!=='undefined'&&window.innerWidth<700)?'8px':'12px';return size+' '+FONT_FAMILY;};function getCharacterDimensions(){const c=document.createElement('canvas').getContext('2d');c.font=getFont();const m=c.measureText('$$$$');return{width:Math.round(m.width/4),height:Math.ceil(m.actualBoundingBoxAscent+m.actualBoundingBoxDescent)+1};} function calculateDimensions(el){const cv=getCharacterDimensions();return{width:Math.floor(el.offsetWidth/cv.width),height:Math.floor(el.offsetHeight/cv.height),charWidth:cv.width,charHeight:cv.height};} let g_width=190,g_height=61;const MOVEMENT_RANGES={explore_radius_pct:0.25,traverse_min_distance_pct:0.60,escaping_border_offset_pct:0.05};let exploreOriginX=0,exploreOriginY=0;let meditatingOriginX=0,meditatingOriginY=0;let exploreMoveCount=0;let bounceGoingUp=false;let escapingTargetBorder='left';let escapingMoveCount=0;let buffer=null,zBuffer=null;let deformPhases=null,targetDeformPhases=null;let deformFreqs=null,targetDeformFreqs=null;let currentTime=0;let config={};let rotX=0,rotY=0,rotZ=0;let targetRotX=0,targetRotY=0,targetRotZ=0;let rotationSpeed=0;let movementX,movementY,targetX,targetY;let currentBehavior;let behaviorTimer=0;let stuckCounter=0;let isInitialized=0;let frameCounter=0;const CACHE_SIZE=628;let cachedSin=new Array(CACHE_SIZE);let cachedCos=new Array(CACHE_SIZE);let borderProximityCounter=0;const BORDER_PROXIMITY_THRESHOLD=1.0;const MAX_BORDER_FRAMES=200;let squishCounter=0;let characterPalette=".,-~:;=!*#@$";function lerp(a,b,t){return a+t*(b-a);} function distance(x1,y1,x2,y2){return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));} function calculateBorderSquish(pos,border,radius){const d=Math.abs(pos-border);const squishRange=radius*7;if(d<squishRange){const factor=d/squishRange;const factorIndex=(Math.floor((factor*Math.PI/2)*CACHE_SIZE/(2.0*Math.PI))+CACHE_SIZE)%CACHE_SIZE;return 0.1+(0.95*cachedSin[factorIndex]);} return 1.0;} function calculateVolumePreservingSquish(currentSquish,targetSquish,volumeRatio){return currentSquish+(targetSquish-currentSquish)*Math.min(0.15,1.0/volumeRatio);} function calculateEffectiveRadius(baseRadius,volumeCompensation,deformStrength){const rawSquishX=calculateBorderSquish(movementX,0,baseRadius)*calculateBorderSquish(movementX,g_width,baseRadius)*config.aspectRatio;const rawSquishY=calculateBorderSquish(movementY,0,baseRadius)*calculateBorderSquish(movementY,g_height,baseRadius)*config.aspectRatio;const maxElongation=Math.max(1.0/rawSquishX,1.0/rawSquishY);return baseRadius*(1.0+deformStrength)*Math.max(volumeCompensation,maxElongation);} function getDistanceToBorder(x,y,radius,width,height){return Math.min(Math.min(x,width-x),Math.min(y,height-y))/radius;} function enforceObjectBounds(x,y,radius,width,height){const margin=radius*1.2;const prevX=x,prevY=y;let currentSquishX=calculateBorderSquish(x,0,radius)*calculateBorderSquish(x,width,radius);let currentSquishY=calculateBorderSquish(y,0,radius)*calculateBorderSquish(y,height,radius);const currentVolume=1.0/(currentSquishX*currentSquishY);x=Math.max(margin,Math.min(width-margin,x));y=Math.max(margin,Math.min(height-margin,y));if(x!==prevX||y!==prevY){const targetSquishX=calculateBorderSquish(x,0,radius)*calculateBorderSquish(x,width,radius);const targetSquishY=calculateBorderSquish(y,0,radius)*calculateBorderSquish(y,height,radius);currentSquishX=calculateVolumePreservingSquish(currentSquishX,targetSquishX,currentVolume);currentSquishY=calculateVolumePreservingSquish(currentSquishY,targetSquishY,currentVolume);if(getDistanceToBorder(x,y,radius,width,height)<1.5){const pushAngle=Math.atan2(height/2-y,width/2-x)+((Math.random()*100)-50)/100.0;targetX=x+Math.cos(pushAngle)*(radius*2);targetY=y+Math.sin(pushAngle)*(radius*2);}}} function allocateBuffers(){buffer=new Array(g_width*g_height);zBuffer=new Float32Array(g_width*g_height);if(config.deformComplexity>0){deformPhases=new Float32Array(config.deformComplexity);targetDeformPhases=new Float32Array(config.deformComplexity);deformFreqs=new Float32Array(config.deformComplexity);targetDeformFreqs=new Float32Array(config.deformComplexity);for(let i=0;i<config.deformComplexity;i++){deformPhases[i]=(Math.random()*628)/100.0;targetDeformPhases[i]=deformPhases[i];deformFreqs[i]=1.0+Math.random()*5;targetDeformFreqs[i]=deformFreqs[i];}}} function initVoidlingWithConfig(cfg){Object.assign(config,cfg);allocateBuffers();for(let i=0;i<CACHE_SIZE;i++){const angle=i*(2.0*Math.PI)/CACHE_SIZE;cachedSin[i]=Math.sin(angle);cachedCos[i]=Math.cos(angle);} movementX=targetX=g_width/2;movementY=targetY=g_height/2;currentBehavior="BEHAVIOR_EXPLORE";exploreOriginX=meditatingOriginX=g_width/2;exploreOriginY=meditatingOriginY=g_height/2;exploreMoveCount=0;bounceGoingUp=false;escapingTargetBorder='left';escapingMoveCount=0;behaviorTimer=0;stuckCounter=0;rotationSpeed=(config.minRotationSpeed+config.maxRotationSpeed)/2;rotX=(Math.random()*2.0*Math.PI)-Math.PI;rotY=(Math.random()*2.0*Math.PI)-Math.PI;rotZ=(Math.random()*2.0*Math.PI)-Math.PI;targetRotX=(Math.random()*2.0*Math.PI)-Math.PI;targetRotY=(Math.random()*2.0*Math.PI)-Math.PI;targetRotZ=(Math.random()*2.0*Math.PI)-Math.PI;isInitialized=1;} function bounceFromBorder(){const randomAngle=(Math.random()*628)/100.0;targetX=movementX+(Math.cos(randomAngle)*config.baseRadius*2.0);targetY=movementY+(Math.sin(randomAngle)*config.baseRadius*2.0);borderProximityCounter=0;stuckCounter=0;} function forceBorderRecovery(){const centerX=g_width/2,centerY=g_height/2;const inCorner=(movementX<=config.baseRadius*2||movementX>=g_width-config.baseRadius*2)&&(movementY<=config.baseRadius*2||movementY>=g_height-config.baseRadius*2);if(inCorner){targetX=centerX+(Math.random()*20-10);targetY=centerY+(Math.random()*20-10);}else{if(movementX<=config.baseRadius||movementX>=g_width-config.baseRadius){targetX=centerX;targetY=movementY+(Math.random()*20-10);} if(movementY<=config.baseRadius||movementY>=g_height-config.baseRadius){targetY=centerY;targetX=movementX+(Math.random()*20-10);}} movementX=lerp(movementX,targetX,0.1);movementY=lerp(movementY,targetY,0.1);stuckCounter=0;borderProximityCounter=0;} function setDimensions(width,height){g_width=width;g_height=height;if(isInitialized){allocateBuffers();movementX=targetX=g_width/2;movementY=targetY=g_height/2;}else{buffer=new Array(g_width*g_height);zBuffer=new Float32Array(g_width*g_height);}} function setCharacterPalette(palette){characterPalette=palette;} function setTargetX(v){targetX=v;} function setTargetY(v){targetY=v;} function pickExploreOrigin(){const m=0.30;exploreOriginX=Math.max(g_width*m,Math.min(g_width*(1-m),movementX));exploreOriginY=Math.max(g_height*m,Math.min(g_height*(1-m),movementY));exploreMoveCount=0;} function updateMovementTargets(){if(currentBehavior==="BEHAVIOR_MEDITATE"){targetX=meditatingOriginX;targetY=meditatingOriginY;return;} if(distance(movementX,movementY,targetX,targetY)>=2.0)return;if(currentBehavior==="BEHAVIOR_EXPLORE"){exploreMoveCount++;const returnInterval=Math.random()<0.5?2:3;if(exploreMoveCount>=returnInterval){targetX=exploreOriginX;targetY=exploreOriginY;exploreMoveCount=0;}else{const maxRadius=g_width*MOVEMENT_RANGES.explore_radius_pct;const angleIndex=Math.floor(Math.random()*CACHE_SIZE);const radius=Math.random()*maxRadius;targetX=Math.max(5,Math.min(g_width-5,exploreOriginX+cachedCos[angleIndex]*radius));targetY=Math.max(5,Math.min(g_height-5,exploreOriginY+cachedSin[angleIndex]*radius));}}else if(currentBehavior==="BEHAVIOR_TRAVERSE"){const minDistance=g_width*MOVEMENT_RANGES.traverse_min_distance_pct;let attempts=0,cx,cy,d;do{cx=Math.random()*g_width;cy=Math.random()*g_height;d=distance(movementX,movementY,cx,cy);attempts++;}while(d<minDistance&&attempts<20);targetX=cx;targetY=cy;}else if(currentBehavior==="BEHAVIOR_BOUNCE"){bounceGoingUp=!bounceGoingUp;if(bounceGoingUp){targetY=g_height*(0.25+Math.random()*0.25);const xVariation=(Math.random()-0.5)*g_width*0.2;targetX=Math.max(20,Math.min(g_width-20,(g_width/2)+xVariation));}else{targetY=g_height-5;targetX=Math.max(20,Math.min(g_width-20,targetX));}}else if(currentBehavior==="BEHAVIOR_ESCAPE"){escapingMoveCount++;const borderChangeInterval=Math.random()<0.5?2:3;if(escapingMoveCount>=borderChangeInterval){const r=Math.random();escapingTargetBorder=r<0.25?'left':r<0.5?'right':r<0.75?'top':'bottom';escapingMoveCount=0;} const offset=g_width*MOVEMENT_RANGES.escaping_border_offset_pct;if(escapingTargetBorder==='left'){targetX=-offset;targetY=Math.random()*g_height;} else if(escapingTargetBorder==='right'){targetX=g_width+offset;targetY=Math.random()*g_height;} else if(escapingTargetBorder==='top'){targetX=Math.random()*g_width;targetY=-offset;} else{targetX=Math.random()*g_width;targetY=g_height+offset;}}} function updateRotationTargets(){if(currentBehavior!=="BEHAVIOR_MEDITATE"&&distance(movementX,movementY,targetX,targetY)<2.0){targetRotX=(Math.random()*2.0*Math.PI)-Math.PI;targetRotY=(Math.random()*2.0*Math.PI)-Math.PI;targetRotZ=(Math.random()*2.0*Math.PI)-Math.PI;rotationSpeed=config.minRotationSpeed+Math.random()*(config.maxRotationSpeed-config.minRotationSpeed);} if(frameCounter%20===0&&Math.random()<0.30){targetRotX=(Math.random()*2.0*Math.PI)-Math.PI;targetRotY=(Math.random()*2.0*Math.PI)-Math.PI;targetRotZ=(Math.random()*2.0*Math.PI)-Math.PI;rotationSpeed=config.minRotationSpeed+Math.random()*(config.maxRotationSpeed-config.minRotationSpeed);}} function hashToDecision(blockHash){if(!blockHash||blockHash.length<10)return Math.floor(Math.random()*10);return parseInt(blockHash.slice(2,4),16)%10;} let lastPivotHash=null;let pivotUseCount=0;function deriveEpochParams(blockHash){if(!blockHash||blockHash.length<20)return null;if(blockHash!==lastPivotHash){lastPivotHash=blockHash;pivotUseCount=0;} else pivotUseCount++;const offset=pivotUseCount;const byteAt=(i)=>parseInt(blockHash.slice(2+(((i+offset)%32)*2),4+(((i+offset)%32)*2)),16);return{rotTargetX:(byteAt(1)/255)*2*Math.PI-Math.PI,rotTargetY:(byteAt(2)/255)*2*Math.PI-Math.PI,rotTargetZ:(byteAt(3)/255)*2*Math.PI-Math.PI,rotSpeedUnit:byteAt(4)/255};} function animationFrame(){if(!isInitialized||!buffer||!zBuffer)return;let radius=config.baseRadius;frameCounter++;currentTime+=config.timeSpeed;if(frameCounter%72===0){for(let i=0;i<config.deformComplexity;i++){if(Math.random()<0.2){targetDeformPhases[i]=Math.random()*6.28;targetDeformFreqs[i]=1.0+Math.random()*5;}}} buffer.fill(' ');zBuffer.fill(-1000.0);for(let x=0;x<g_width;x++){buffer[x]='$';buffer[x+(g_height-1)*g_width]='$';} for(let y=0;y<g_height;y++){buffer[y*g_width]='$';buffer[g_width-1+y*g_width]='$';} behaviorTimer++;if(behaviorTimer>=config.behaviorChangeTime){behaviorTimer=0;const blockHash=window.bcRandom&&window.bcRandom.currentBlockHash;const r=hashToDecision(blockHash);if(r===0){currentBehavior="BEHAVIOR_MEDITATE";meditatingOriginX=g_width/2;meditatingOriginY=g_height/2;}else if(r<=3){currentBehavior="BEHAVIOR_TRAVERSE";}else if(r<=5){currentBehavior="BEHAVIOR_BOUNCE";bounceGoingUp=false;}else if(r<=8){currentBehavior="BEHAVIOR_EXPLORE";pickExploreOrigin();}else{currentBehavior="BEHAVIOR_ESCAPE";escapingMoveCount=0;} if(window.fetchLatestBlockHash)window.fetchLatestBlockHash().catch(()=>{});stuckCounter=0;updateMovementTargets();updateRotationTargets();const epoch=deriveEpochParams(blockHash);if(epoch){targetRotX=epoch.rotTargetX;targetRotY=epoch.rotTargetY;targetRotZ=epoch.rotTargetZ;rotationSpeed=config.minRotationSpeed+epoch.rotSpeedUnit*(config.maxRotationSpeed-config.minRotationSpeed);}} if(distance(movementX,movementY,targetX,targetY)<2.0)stuckCounter++;else stuckCounter=0;updateMovementTargets();updateRotationTargets();movementX=lerp(movementX,targetX,config.moveSpeed*config.xBias*0.3);movementY=lerp(movementY,targetY,config.moveSpeed*config.yBias);enforceObjectBounds(movementX,movementY,radius,g_width,g_height);if(movementX<=radius*1.5||movementX>=g_width-radius*1.5||movementY<=radius*1.5||movementY>=g_height-radius*1.5){const sx=calculateBorderSquish(movementX,0,radius)*calculateBorderSquish(movementX,g_width,radius);const sy=calculateBorderSquish(movementY,0,radius)*calculateBorderSquish(movementY,g_height,radius);if(sx<0.5||sy<0.5){squishCounter++;if(squishCounter>60&&stuckCounter>100){forceBorderRecovery();squishCounter=0;}}else squishCounter=0;}else squishCounter=0;rotX=lerp(rotX,targetRotX,config.rotationSmoothness*rotationSpeed);rotY=lerp(rotY,targetRotY,config.rotationSmoothness*rotationSpeed);rotZ=lerp(rotZ,targetRotZ,config.rotationSmoothness*rotationSpeed);for(let i=0;i<config.deformComplexity;i++){deformPhases[i]=lerp(deformPhases[i],targetDeformPhases[i],config.deformSmoothness);deformFreqs[i]=lerp(deformFreqs[i],targetDeformFreqs[i],config.deformSmoothness);} const squishX=calculateBorderSquish(movementX,0,radius)*calculateBorderSquish(movementX,g_width,radius)*config.aspectRatio;const squishY=calculateBorderSquish(movementY,0,radius)*calculateBorderSquish(movementY,g_height,radius)*config.aspectRatio;let volumeCompensation=Math.max(1.0,Math.min(1.5,1.0/(squishX*squishY)));const effectiveRadius=calculateEffectiveRadius(radius,volumeCompensation,config.baseDeformStrength);const borderDist=getDistanceToBorder(movementX,movementY,effectiveRadius,g_width,g_height);if(borderDist<BORDER_PROXIMITY_THRESHOLD&&stuckCounter>200){borderProximityCounter++;if(borderProximityCounter>MAX_BORDER_FRAMES)bounceFromBorder();}else borderProximityCounter=0;if(stuckCounter>100){const s=Math.floor(Math.random()*10);if(s<=3)currentBehavior="BEHAVIOR_TRAVERSE";else if(s<=5){currentBehavior="BEHAVIOR_EXPLORE";pickExploreOrigin();} else if(s<=7){currentBehavior="BEHAVIOR_ESCAPE";escapingMoveCount=0;} else{currentBehavior="BEHAVIOR_BOUNCE";bounceGoingUp=false;} stuckCounter=0;} const bpx=Math.min(movementX,g_width-movementX);const bpy=Math.min(movementY,g_height-movementY);let borderDeform=Math.max(0,(1.0-(Math.min(bpx,bpy)/(radius*7)))*config.baseDeformStrength);if(bpx<radius)borderDeform+=(1.0-(bpx/radius))*config.baseDeformStrength;if(bpy<radius)borderDeform+=(1.0-(bpy/radius))*config.baseDeformStrength;const borderDeformContribution=borderDeform*config.baseDeformStrength*3.5;const TWO_PI=2.0*Math.PI;const CACHE_OVER_2PI=CACHE_SIZE/TWO_PI;const rotXIndex=(Math.floor(rotX*CACHE_OVER_2PI)+CACHE_SIZE)%CACHE_SIZE;const rotYIndex=(Math.floor(rotY*CACHE_OVER_2PI)+CACHE_SIZE)%CACHE_SIZE;const rotZIndex=(Math.floor(rotZ*CACHE_OVER_2PI)+CACHE_SIZE)%CACHE_SIZE;const cosRotX=cachedCos[rotXIndex],sinRotX=cachedSin[rotXIndex];const cosRotY=cachedCos[rotYIndex],sinRotY=cachedSin[rotYIndex];const cosRotZ=cachedCos[rotZIndex],sinRotZ=cachedSin[rotZIndex];const tf=currentTime*0.05;const bf=behaviorTimer/config.behaviorChangeTime;let lx=Math.cos(tf)*(1.0+0.3*Math.sin(rotX));let ly=Math.sin(tf)*(1.0+0.3*Math.sin(rotZ));let lz=-0.5+0.3*Math.sin(bf*Math.PI)+0.2*Math.sin(rotY);const ll=Math.sqrt(lx*lx+ly*ly+lz*lz);lx/=ll;ly/=ll;lz/=ll;const chars=characterPalette;const charsMaxIdx=chars.length-1;const aspectY=config.aspectRatio*1.8;const volumeZFactor=1.0+(volumeCompensation-1.0)*0.7;const perspectiveD=config.perspectiveDistance;const perspectiveStrength=config.perspectiveStrength;const twoRadius=2*radius;const deformFreq_time=currentTime*config.deformFrequency;for(let phi=0;phi<TWO_PI;phi+=config.stepSize){const phiIndex=Math.floor(phi*CACHE_OVER_2PI)%CACHE_SIZE;const cosPhi=cachedCos[phiIndex];const sinPhi=cachedSin[phiIndex];let deformPhi=0;for(let i=0;i<config.deformComplexity;i++){const deformIndex=(Math.floor((phi*deformFreqs[i]+deformFreq_time+deformPhases[i])*CACHE_OVER_2PI)+CACHE_SIZE)%CACHE_SIZE;deformPhi+=cachedSin[deformIndex]*(config.baseDeformStrength/(i+1));} for(let theta=0;theta<Math.PI;theta+=config.stepSize){const thetaIndex=Math.floor(theta*CACHE_OVER_2PI)%CACHE_SIZE;const cosTheta=cachedCos[thetaIndex];const sinTheta=cachedSin[thetaIndex];const dripEffect=config.drippiness*(1-cosTheta)*sinPhi*config.extraDeformStrength;const deformation=deformPhi+dripEffect+borderDeformContribution;const rr=radius+deformation;const nx=sinTheta*cosPhi;const ny=sinTheta*sinPhi*config.aspectRatio;const nz=cosTheta;const nDotL=nx*lx+ny*ly+nz*lz;const illumBase=nDotL*0.3+0.8;const dx_unit=sinTheta*cosPhi*squishX;const dy_unit=sinTheta*sinPhi*aspectY*squishY;const dz_unit=cosTheta*volumeZFactor;for(let dr=0;dr<=rr;dr+=0.3){const relativeDepth=dr/rr;let x=dr*dx_unit;let y=dr*dy_unit;let z=dr*dz_unit;let ty=y,tz=z;y=ty*cosRotX-tz*sinRotX;z=ty*sinRotX+tz*cosRotX;let tx=x;tz=z;x=tx*cosRotY+tz*sinRotY;z=-tx*sinRotY+tz*cosRotY;tx=x;ty=y;x=tx*cosRotZ-ty*sinRotZ;y=tx*sinRotZ+ty*cosRotZ;const perspective=perspectiveD/(perspectiveD+z);const px=Math.floor(movementX+x*perspective*perspectiveStrength);const py=Math.floor(movementY+y*perspective);if(px>=1&&px<g_width-1&&py>=1&&py<g_height-1){const idx=px+py*g_width;if(z>zBuffer[idx]){const illumination=illumBase*(1.0-relativeDepth*0.2)*(z+radius)/twoRadius;let charIndex=Math.floor(illumination*charsMaxIdx);if(charIndex<0)charIndex=0;else if(charIndex>charsMaxIdx)charIndex=charsMaxIdx;zBuffer[idx]=z;buffer[idx]=chars[charIndex];}}}}} currentTime+=config.timeSpeed;} function getBuffer(){return buffer;} const ETH_RPC_URLS=['https://ethereum-rpc.publicnode.com','https://eth.llamarpc.com','https://rpc.ankr.com/eth','https://cloudflare-eth.com','https://eth.drpc.org'];let preferred=0;let fetching=false;let lastHash=null;async function rpcAttempt(url){const c=new AbortController();const t=setTimeout(()=>c.abort(),5000);try{const r=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:'{"jsonrpc":"2.0","id":1,"method":"eth_getBlockByNumber","params":["latest",false]}',signal:c.signal});if(!r.ok)throw 0;const d=await r.json();if(!d.result)throw 0;return d.result;}finally{clearTimeout(t);}} async function fetchLatestBlockHash(){if(document.hidden||fetching)return;fetching=true;try{for(let i=0;i<ETH_RPC_URLS.length;i++){const idx=(preferred+i)%ETH_RPC_URLS.length;try{const res=await rpcAttempt(ETH_RPC_URLS[idx]);preferred=idx;if(res.hash!==lastHash){lastHash=res.hash;bcRandom.setSeed(res.hash);} return;}catch(e){}}}finally{fetching=false;}} const FRAME_INTERVAL=48;let canvasEl,ctx;let label='';let cv;let gridW=0,gridH=0;let xOffset=0,yOffset=0;let labelStart=0,labelEnd=0;let dpr=1;let lastFrameTime=0;let paused=false;let drag=false;function startRenderLoop(canvas,id){canvasEl=canvas;ctx=canvas.getContext('2d');label=' DEL #'+id+' ';cv=getCharacterDimensions();resize();window.addEventListener('resize',resize);window.addEventListener('keydown',onKey);canvasEl.addEventListener('mousedown',onMouseDown);requestAnimationFrame(loop);} function onKey(e){if(e.code==='Space'){e.preventDefault();paused=!paused;}} function pixelToGrid(e){const r=canvasEl.getBoundingClientRect();const gx=(e.clientX-r.left-xOffset)/cv.width;const gy=(e.clientY-r.top-yOffset)/cv.height;if(gx<1||gx>gridW-1||gy<1||gy>gridH-1)return null;return[Math.max(5,Math.min(gridW-5,gx)),Math.max(5,Math.min(gridH-5,gy))];} function onMouseDown(e){if(e.button!==0||paused)return;const t=pixelToGrid(e);if(!t)return;setTargetX(t[0]);setTargetY(t[1]);drag=true;document.addEventListener('mousemove',onMouseMove);document.addEventListener('mouseup',onMouseUp);} function onMouseMove(e){if(!drag)return;const t=pixelToGrid(e);if(t){setTargetX(t[0]);setTargetY(t[1]);}} function onMouseUp(){drag=false;document.removeEventListener('mousemove',onMouseMove);document.removeEventListener('mouseup',onMouseUp);} function resize(){dpr=window.devicePixelRatio||1;canvasEl.width=canvasEl.offsetWidth*dpr;canvasEl.height=canvasEl.offsetHeight*dpr;cv=getCharacterDimensions();gridW=Math.floor(canvasEl.offsetWidth/cv.width);gridH=Math.floor(canvasEl.offsetHeight/cv.height);setDimensions(gridW,gridH);const contentW=gridW*cv.width;const contentH=gridH*cv.height;ctx.setTransform(1,0,0,1,0,0);ctx.scale(dpr,dpr);ctx.font=getFont();ctx.textAlign='left';const ascent=ctx.measureText('$').actualBoundingBoxAscent;xOffset=(canvasEl.offsetWidth-contentW)/2;yOffset=(canvasEl.offsetHeight-contentH)/2-cv.height+ascent;ctx.translate(xOffset,yOffset);labelStart=Math.max(1,Math.floor((gridW-label.length)/2));labelEnd=labelStart+label.length;} function loop(t){requestAnimationFrame(loop);if(paused)return;if(t-lastFrameTime<FRAME_INTERVAL)return;lastFrameTime=t;animationFrame();const buf=getBuffer();if(!buf)return;ctx.clearRect(-xOffset,-yOffset,canvasEl.offsetWidth,canvasEl.offsetHeight);const topC=getTopColor();const w=gridW,h=gridH;const cw=cv.width,ch_=cv.height;let curStyle=null;for(let row=0;row<h;row++){const y=(row+1)*ch_;const rowBase=row*w;for(let col=0;col<w;col++){let ch=buf[rowBase+col]||' ';if(row===0&&col>=labelStart&&col<labelEnd){ch=label[col-labelStart];if(ch!==' '){if(curStyle!==topC){ctx.fillStyle=topC;curStyle=topC;} ctx.fillText(ch,col*cw,y);} continue;} if(row===0||row===h-1||col===0||col===w-1)ch='$';if(ch===' ')continue;const color=getColorForChar(ch);if(color!==curStyle){ctx.fillStyle=color;curStyle=color;} ctx.fillText(ch,col*cw,y);}}} function boot(){const cfg=window.DEL_CONFIG;if(!cfg)return;setCustomColorScheme(cfg.colorScheme,cfg.characterPalette);document.body.style.backgroundColor=cfg.colorScheme.background;window.bcRandom=bcRandom;window.fetchLatestBlockHash=fetchLatestBlockHash;enableBlockchainRandom();const canvas=document.getElementById('c');const dims=calculateDimensions(canvas);setDimensions(dims.width,dims.height);initVoidlingWithConfig(cfg);setCharacterPalette(cfg.characterPalette);startRenderLoop(canvas,window.DEL_ID);fetchLatestBlockHash();} if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',boot);else boot(); })(); </script></body></html>