0xa89f3db4…071asent to0xecb92cc7…1463·#25,067,888·view on Etherscan
rX-this._cursorXSm)*a,this._cursorYSm+=(this._cursorY-this._cursorYSm)*a,this.params.enableLakeMode){const e=this._cursorVel<this.params.lakeThresholdSpeed?1:0;this._lakeT+=(e-this._lakeT)*i*1.2,this._lakeT=Math.max(0,Math.min(1,this._lakeT))}else this._lakeT=0;let s=1;if(this.params.cursorReverseHalf){s=2*(this._cursorX-.5);const e=this.params.minForwardMotion;s>-e&&s<e&&(s=e)}const o=1+this._audio.amplitude*this.params.audioSpeedMult;let r=this.params.baseSpeed*o*s*(1-.95*this._lakeT);r*=void 0!==window._playbackDirection?window._playbackDirection:1;const n=this.params.maxDepth,l=this.params.ringSpacing;for(const e of this._rings)e.z-=r*i*30,e.z<.5?(e.z+=this.params.ringCount*l,e.distortStartMs=-1):e.z>n&&(e.z-=this.params.ringCount*l,e.distortStartMs=-1);const h=1e3*this._time;for(const e of this._torpedoes)if(!e.exploded&&(e.z+=this.params.torpedoSpeed*(t/16),e.z>=12+8*Math.random())){e.exploded=!0;let t=null,i=1/0;for(const a of this._rings){const s=Math.abs(a.z-e.z);s<i&&(i=s,t=a)}t&&(t.distortStartMs=h,t.distortAmount=this.params.distortionAmount),e.detonationMs=h}this._torpedoes=this._torpedoes.filter(e=>!e.exploded||h-e.detonationMs<500)}_fireTorpedo(e,t){this._torpedoes.push({x:e,y:t,z:1.5,exploded:!1,detonationMs:0,spawnMs:1e3*this._time})}draw(e){const t=e.drawingContext;if(!t)return this._drawFallback(e);const i=e.width,a=e.height,s=i/2,o=a/2,r=Math.min(i,a),n=this.params.focal*(r/720),l=this.params.tunnelRadius,h=1-this.params.backgroundFade;t.fillStyle=`rgba(0,0,0,${Math.min(1,h)})`,t.fillRect(0,0,i,a);const d=(this._cursorXSm-.5)*this.params.cursorFollowStrength,c=(this._cursorYSm-.5)*this.params.cursorFollowStrength,u=[...this._rings].sort((e,t)=>t.z-e.z),p=this.params.lakeBasinSpread,m=this.params.maxDepth,g=this.params.segmentCount,f=this.params.ringWobbleAmount,y=this.params.audioRingDeform,b=this._audio.spectrum,_=1e3*this._time,w=this.params.distortionDuration,S=this._lakeT,T=this.params.lakeShellZ;let C=0;this.params.enableChromaticAberration&&(C=this._audio.bass*this.params.artifactSensitivity*6),t.lineCap="round",t.lineJoin="round";let x=0,P=s,M=o;for(const e of u){const r=T+e.z/m*p,h=e.z*(1-S)+r*S;if(h<.3)continue;const u=n/h,E=Math.min(1,h/20),v=this.params.horizonRange,k=s+d*i*v*E,F=o+c*a*v*E,A=l*(1+Math.sin(this._time*e.wobbleFreq+e.wobblePhase)*f)*u;if(A<1)continue;A>x&&(x=A,P=k,M=F);let D=0;if(e.distortStartMs>=0){const t=_-e.distortStartMs;t<w?D=(1-t/w)*e.distortAmount:e.distortStartMs=-1}const L=Math.max(.15,1-h/this.params.maxDepth)*(.5+.5*this._audio.amplitude+.4*this._beatPulse)*(1+.7*S)*this.params.overallBrightness,[B,R,N]=this._channelColor(h,Math.min(1,L)),z=[];for(let e=0;e<g;e++){const t=e/g*Math.PI*2;let i=A;if(b&&y>.001){i+=b[Math.floor(e/g*b.length)]/255*y*A*.3}if(D>0){const t=(Math.sin(7.3*e+.02*_)+Math.sin(13.1*e+.013*_))*D*A*.5;i+=6*Math.round(t/6)}z.push([k+Math.cos(t)*i,F+Math.sin(t)*i])}t.lineWidth=this.params.ringStrokeWeight;const I=(e,i,a)=>{t.strokeStyle=a,t.beginPath();for(let a=0;a<z.length;a++){const[s,o]=z[a];0===a?t.moveTo(s+e,o+i):t.lineTo(s+e,o+i)}t.closePath(),t.stroke()};if(C>.5){const e=Math.floor(255*L),t=.6*L,[i,a,s]=this._channelTuple(0,e),[o,r,n]=this._channelTuple(1,e),[l,h,d]=this._channelTuple(2,e);I(-C,0,`rgba(${i},${a},${s},${t})`),I(0,0,`rgba(${o},${r},${n},${t})`),I(C,0,`rgba(${l},${h},${d},${t})`)}else I(0,0,`rgb(${B},${R},${N})`)}for(const e of this._torpedoes)if(e.exploded){const s=_-e.detonationMs;if(s<200){const o=e.x*i,r=e.y*a,n=s/200*80,l=1-s/200;t.strokeStyle=`rgba(0,255,0,${l})`,t.lineWidth=2,t.beginPath(),t.arc(o,r,n,0,2*Math.PI),t.stroke(),t.strokeStyle=`rgba(255,0,0,${.7*l})`,t.beginPath(),t.arc(o,r,.6*n,0,2*Math.PI),t.stroke()}}else{const s=e.x*i,o=e.y*a,r=n/Math.max(1,e.z),l=Math.max(3,this.params.torpedoSize*r),h=(_-e.spawnMs)/1e3,d=.5,c=3;t.lineWidth=1.5;for(let e=0;e<c;e++){const i=(h+e*(d/c))%d/d,a=l*(.4+1.4*i),r=.9*(1-i),n=(e+Math.floor(4*h))%3,[u,p,m]=this._channelTuple(n,255);t.strokeStyle=`rgba(${u},${p},${m},${r})`,t.beginPath(),t.arc(s,o,a,0,2*Math.PI),t.stroke()}}if(this.params.enableScanlineNoise&&this._audio.high>.15/Math.max(.1,this.params.artifactSensitivity)){const e=this._audio.high*this.params.artifactSensitivity;t.save(),x>0&&(t.beginPath(),t.rect(0,0,i,a),t.arc(P,M,x,0,2*Math.PI),t.clip("evenodd"));const s=Math.floor(40*e);for(let e=0;e<s;e++){const e=Math.random()*a,s=Math.floor(3*Math.random()),[o,r,n]=this._channelTuple(s,255);t.fillStyle=`rgba(${o},${r},${n},${.15+.2*Math.random()})`,t.fillRect(0,e,i,1)}t.restore()}if(this.params.showLakeMarker&&S>.05){const e=l*(n/T),i=.4*S;t.strokeStyle=`rgba(0,255,0,${i})`,t.lineWidth=1;for(let i=-1;i<=1;i++)t.beginPath(),t.arc(s,o,e*(1+.04*i),0,2*Math.PI),t.stroke();t.fillStyle=`rgba(255,0,0,${.8*i})`,t.beginPath(),t.arc(s,o,3,0,2*Math.PI),t.fill()}}_drawFallback(e){e.background(0),e.fill(0,255,0),e.noStroke(),e.textAlign(e.CENTER,e.CENTER),e.text("TemporalTunnel: drawingContext unavailable",e.width/2,e.height/2)}resize(e,t){super.resize(e,t)}}window.DirectionalPlayback=DirectionalPlayback,window.TemporalTunnel=TemporalTunnel;class AudioEngine{constructor(){this._audioContext=null,this._analyser=null,this._gainNode=null,this._sourceNode=null,this._audioBuffer=null,this.isLoaded=!1,this.isPlaying=!1,this._spectrum=null,this._waveform=null,this._energy={bass:0,lowMid:0,mid:0,highMid:0,treble:0},this._amplitude=0,this.beatThreshold=.15,this.beatCooldown=100,this.lastBeatTime=0,this.beatDetected=!1,this._startOffset=0,this._lastStartTime=0,this._lastStartOffset=0,this.energyBands={bass:[20,140],lowMid:[140,400],mid:[400,2600],highMid:[2600,5200],treble:[5200,14e3]},this._forwardBuffer=null,this._reverseBuffer=null,this._directionalEnabled=!1,this._isForward=!0,this._savedDirectionalPosition=0,this._simulating=!1,this._simTime=0,this._simSpectrum=null,this._simWaveform=null}_getContext(){return this._audioContext||(this._audioContext=new(window.AudioContext||window.webkitAudioContext)),this._audioContext}init(){const e=this._getContext();this._analyser=e.createAnalyser(),this._analyser.fftSize=2048,this._analyser.smoothingTimeConstant=.8,this._gainNode=e.createGain(),this._gainNode.connect(e.destination),this._analyser.connect(e.destination),this._spectrum=new Uint8Array(this._analyser.frequencyBinCount),this._waveform=new Uint8Array(this._analyser.frequencyBinCount)}async resume(){const e=this._getContext();"suspended"===e.state&&await e.resume()}async loadFromUrl(e){try{const t=this._getContext(),i=await fetch(e);if(!i.ok)throw new Error("Failed to fetch audio: "+i.status);const a=await i.arrayBuffer();this._audioBuffer=await t.decodeAudioData(a),this._forwardBuffer=this._audioBuffer,this.isLoaded=!0}catch(e){console.error("Audio load error:",e)}}play(){if(!this._audioBuffer||!this.isLoaded)return;const e=this._getContext();if(this._sourceNode)try{this._sourceNode.stop(),this._sourceNode.disconnect()}catch(e){}this._sourceNode=e.createBufferSource(),this._sourceNode.buffer=this._audioBuffer,this._sourceNode.connect(this._gainNode),this._sourceNode.connect(this._analyser),this._sourceNode.loop=!0;const t=Math.max(0,Math.min(this._startOffset,this._audioBuffer.duration-.01));this._sourceNode.start(0,t),this._lastStartTime=e.currentTime,this._lastStartOffset=t,this.isPlaying=!0}pause(){if(this._sourceNode&&this.isPlaying){const e=this._getContext().currentTime-this._lastStartTime;this._startOffset=(this._lastStartOffset+e)%this._audioBuffer.duration;try{this._sourceNode.stop(),this._sourceNode.disconnect()}catch(e){}this._sourceNode=null,this.isPlaying=!1}}toggle(){this._directionalEnabled&&this.isPlaying?this.stopDirectional():this._directionalEnabled?this.startDirectional(this._isForward):this.isLoaded?this.isPlaying?this.pause():this.play():(this._simulating=!this._simulating,this.isPlaying=this._simulating)}async initDirectionalPlayback(){if(this._forwardBuffer)try{const e=this._getContext();this._reverseBuffer=e.createBuffer(this._forwardBuffer.numberOfChannels,this._forwardBuffer.length,this._forwardBuffer.sampleRate);for(let e=0;e<this._forwardBuffer.numberOfChannels;e++){const t=this._forwardBuffer.getChannelData(e),i=this._reverseBuffer.getChannelData(e);for(let e=0;e<t.length;e++)i[e]=t[t.length-1-e]}this._directionalEnabled=!0,console.log("[AudioEngine] Directional playback initialized")}catch(e){console.error("[AudioEngine] Failed to init directional:",e),this._directionalEnabled=!1}}getDirectionalPosition(){if(!this._audioContext||!this._lastStartTime)return 0;const e=this._audioContext.currentTime-this._lastStartTime;return this._isForward?this._lastStartOffset+e:this._lastStartOffset-e}playDirectional(e,t){if(!this._directionalEnabled||!this._audioContext||!this._forwardBuffer)return;if(this._sourceNode){this._sourceNode.onended=null;try{this._sourceNode.stop(),this._sourceNode.disconnect()}catch(e){}}this._sourceNode=this._audioContext.createBufferSource(),this._sourceNode.buffer=t?this._forwardBuffer:this._reverseBuffer,this._sourceNode.connect(this._gainNode),this._sourceNode.connect(this._analyser);const i=t?e:this._forwardBuffer.duration-e,a=Math.max(0,Math.min(i,this._sourceNode.buffer.duration-.01));this._sourceNode.start(0,a),this._lastStartTime=this._audioContext.currentTime,this._lastStartOffset=e,this._isForward=t,this.isPlaying=!0,this._sourceNode.onended=()=>{this._isForward?this.playDirectional(0,!0):this.playDirectional(this._forwardBuffer.duration,!1)}}setDirection(e){if(!this._directionalEnabled)return;const t=e>=0;if(t===this._isForward)return;const i=this.getDirectionalPosition();this.playDirectional(i,t)}isDirectionalEnabled(){return this._directionalEnabled}getDirection(){return this._isForward?1:-1}stopDirectional(){if(this.isPlaying&&(this._savedDirectionalPosition=this.getDirectionalPosition(),this._forwardBuffer&&(this._savedDirectionalPosition=Math.max(0,Math.min(this._savedDirectionalPosition,this._forwardBuffer.duration)))),this._sourceNode){this._sourceNode.onended=null;try{this._sourceNode.stop(),this._sourceNode.di