@@ -159,8 +159,10 @@ 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 ;
162+ let targetMouseX = - 9999 ;
163+ let targetMouseY = - 9999 ;
164+ let currentMouseX = - 9999 ;
165+ let currentMouseY = - 9999 ;
164166
165167 const targetFps = isMobile ? 24 : 30 ;
166168 const fpsInterval = 1000 / targetFps ;
@@ -192,10 +194,16 @@ export default function EscherBackground() {
192194 vec2 cellCenter = floor(pos / u_gap + 0.5) * u_gap;
193195 float dist = distance(pos, cellCenter);
194196
195- // Cursor hover: scale dots near the mouse
197+ // Buttery smooth magnifying glass effect
198+ float magRadius = 260.0;
196199 float mouseDist = distance(pos, u_mouse);
197- float hoverRadius = 120.0;
198- float hoverScale = 1.0 + 1.2 * smoothstep(hoverRadius, 0.0, mouseDist);
200+
201+ // Lens falloff for dot scaling
202+ float lensFalloff = smoothstep(magRadius, 0.0, mouseDist);
203+ // Slight easing for an organic, softer swell
204+ float swellFalloff = lensFalloff * lensFalloff * (3.0 - 2.0 * lensFalloff);
205+
206+ float hoverScale = 1.0 + 1.4 * swellFalloff;
199207 float effectiveRadius = u_baseRadius * hoverScale;
200208
201209 if (dist > effectiveRadius + 0.5) {
@@ -221,7 +229,17 @@ export default function EscherBackground() {
221229 float offsetY = (u_resolution.y - drawHeight) / 2.0;
222230
223231 vec2 screenCenter = u_resolution / 2.0;
224- vec2 diff = cellCenter - screenCenter;
232+
233+ // Texture magnification / distortion map
234+ vec2 cellMouseDiff = cellCenter - u_mouse;
235+ float cellMouseDist = length(cellMouseDiff);
236+ float cellLensFalloff = smoothstep(magRadius, 0.0, cellMouseDist);
237+ float zoomFalloff = cellLensFalloff * cellLensFalloff * (3.0 - 2.0 * cellLensFalloff); // Cubic ease
238+
239+ // Shrink distance to mouse to "zoom in"
240+ vec2 sampleCenter = mix(cellCenter, u_mouse + cellMouseDiff * 0.35, zoomFalloff);
241+
242+ vec2 diff = sampleCenter - screenCenter;
225243
226244 float angle = -u_time * 0.002;
227245 float s = sin(angle);
@@ -372,12 +390,21 @@ export default function EscherBackground() {
372390 lastTime = now - ( elapsed % fpsInterval ) ;
373391 time += scrollSpeed * ( elapsed / 16.666 ) ;
374392
393+ // Buttery smooth mouse interpolation
394+ if ( currentMouseX === - 9999 && targetMouseX !== - 9999 ) {
395+ currentMouseX = targetMouseX ;
396+ currentMouseY = targetMouseY ;
397+ } else if ( targetMouseX !== - 9999 ) {
398+ currentMouseX += ( targetMouseX - currentMouseX ) * 0.12 ;
399+ currentMouseY += ( targetMouseY - currentMouseY ) * 0.12 ;
400+ }
401+
375402 if ( uTime ) {
376403 gl . uniform1f ( uTime , time ) ;
377404 }
378405 if ( uMouse ) {
379406 const dpr = Math . min ( window . devicePixelRatio || 1 , isMobile ? ( isIPhone ? 2.25 : 1.6 ) : 2.0 ) ;
380- gl . uniform2f ( uMouse , mouseX * dpr , mouseY * dpr ) ;
407+ gl . uniform2f ( uMouse , currentMouseX * dpr , currentMouseY * dpr ) ;
381408 }
382409 gl . drawArrays ( gl . TRIANGLES , 0 , 6 ) ;
383410 } ;
@@ -411,8 +438,8 @@ export default function EscherBackground() {
411438
412439 const handleMouseMove = ( e : MouseEvent ) => {
413440 const rect = canvas . getBoundingClientRect ( ) ;
414- mouseX = e . clientX - rect . left ;
415- mouseY = e . clientY - rect . top ;
441+ targetMouseX = e . clientX - rect . left ;
442+ targetMouseY = e . clientY - rect . top ;
416443 } ;
417444
418445 observer . observe ( container ) ;
0 commit comments