@@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from "react";
55
66export default function EscherBackground ( ) {
77 const INITIAL_RADIUS_DESKTOP_PX = 0 ;
8- const INITIAL_RADIUS_MOBILE_PX = 150 ;
8+ const INITIAL_RADIUS_MOBILE_PX = 0 ;
99
1010 const canvasRef = useRef < HTMLCanvasElement > ( null ) ;
1111 const containerRef = useRef < HTMLDivElement > ( null ) ;
@@ -159,6 +159,8 @@ export default function EscherBackground() {
159159 let time = 0 ;
160160 let lastTime = performance . now ( ) ;
161161 let isVisible = true ;
162+ let mouseX = - 9999 ;
163+ let mouseY = - 9999 ;
162164
163165 const targetFps = isMobile ? 24 : 30 ;
164166 const fpsInterval = 1000 / targetFps ;
@@ -183,13 +185,20 @@ export default function EscherBackground() {
183185 uniform float u_gap;
184186 uniform float u_baseRadius;
185187 uniform float u_zoom;
188+ uniform vec2 u_mouse;
186189
187190 void main() {
188191 vec2 pos = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y);
189192 vec2 cellCenter = floor(pos / u_gap + 0.5) * u_gap;
190193 float dist = distance(pos, cellCenter);
191194
192- if (dist > u_baseRadius + 0.5) {
195+ // Cursor hover: scale dots near the mouse
196+ float mouseDist = distance(pos, u_mouse);
197+ float hoverRadius = 120.0;
198+ float hoverScale = 1.0 + 1.2 * smoothstep(hoverRadius, 0.0, mouseDist);
199+ float effectiveRadius = u_baseRadius * hoverScale;
200+
201+ if (dist > effectiveRadius + 0.5) {
193202 gl_FragColor = vec4(0.0);
194203 return;
195204 }
@@ -214,7 +223,7 @@ export default function EscherBackground() {
214223 vec2 screenCenter = u_resolution / 2.0;
215224 vec2 diff = cellCenter - screenCenter;
216225
217- float angle = u_time * 0.002;
226+ float angle = - u_time * 0.002;
218227 float s = sin(angle);
219228 float c = cos(angle);
220229
@@ -224,6 +233,9 @@ export default function EscherBackground() {
224233 );
225234
226235 vec2 rotatedCenter = screenCenter + rotatedDiff;
236+
237+ // Add a gentle hovering effect (bobbing up and down)
238+ rotatedCenter.y += sin(u_time * 0.003) * 20.0;
227239
228240 float u = (rotatedCenter.x - offsetX) / drawWidth;
229241 float v = (rotatedCenter.y - offsetY) / drawHeight;
@@ -238,7 +250,7 @@ export default function EscherBackground() {
238250 }
239251
240252 float sizeFactor = (255.0 - brightness) / 255.0;
241- float dotSize = sizeFactor * u_baseRadius ;
253+ float dotSize = sizeFactor * effectiveRadius ;
242254
243255 if (brightness < 240.0 && dotSize > 0.5) {
244256 float alpha = 1.0 - smoothstep(dotSize - 0.5, dotSize + 0.5, dist);
@@ -298,10 +310,12 @@ export default function EscherBackground() {
298310 const uGap = gl . getUniformLocation ( program , "u_gap" ) ;
299311 const uBaseRadius = gl . getUniformLocation ( program , "u_baseRadius" ) ;
300312 const uZoom = gl . getUniformLocation ( program , "u_zoom" ) ;
313+ const uMouse = gl . getUniformLocation ( program , "u_mouse" ) ;
301314
302315 gl . uniform1f ( uGap , gap ) ;
303316 gl . uniform1f ( uBaseRadius , baseRadius ) ;
304317 gl . uniform1f ( uZoom , iphoneCenterZoom ) ;
318+ gl . uniform2f ( uMouse , - 9999 , - 9999 ) ;
305319
306320 const texture = gl . createTexture ( ) ;
307321 let isTextureLoaded = false ;
@@ -361,6 +375,10 @@ export default function EscherBackground() {
361375 if ( uTime ) {
362376 gl . uniform1f ( uTime , time ) ;
363377 }
378+ if ( uMouse ) {
379+ const dpr = Math . min ( window . devicePixelRatio || 1 , isMobile ? ( isIPhone ? 2.25 : 1.6 ) : 2.0 ) ;
380+ gl . uniform2f ( uMouse , mouseX * dpr , mouseY * dpr ) ;
381+ }
364382 gl . drawArrays ( gl . TRIANGLES , 0 , 6 ) ;
365383 } ;
366384
@@ -391,15 +409,23 @@ export default function EscherBackground() {
391409 }
392410 } ;
393411
412+ const handleMouseMove = ( e : MouseEvent ) => {
413+ const rect = canvas . getBoundingClientRect ( ) ;
414+ mouseX = e . clientX - rect . left ;
415+ mouseY = e . clientY - rect . top ;
416+ } ;
417+
394418 observer . observe ( container ) ;
395419 window . addEventListener ( "resize" , handleResize ) ;
396420 window . visualViewport ?. addEventListener ( "resize" , handleResize ) ;
421+ document . addEventListener ( "mousemove" , handleMouseMove ) ;
397422 document . addEventListener ( "visibilitychange" , handleVisibilityChange ) ;
398423
399424 return ( ) => {
400425 observer . disconnect ( ) ;
401426 window . removeEventListener ( "resize" , handleResize ) ;
402427 window . visualViewport ?. removeEventListener ( "resize" , handleResize ) ;
428+ document . removeEventListener ( "mousemove" , handleMouseMove ) ;
403429 document . removeEventListener ( "visibilitychange" , handleVisibilityChange ) ;
404430 cancelAnimationFrame ( animationFrameId ) ;
405431 gl . deleteProgram ( program ) ;
0 commit comments