@@ -185,6 +185,10 @@ let terminalHistory = [];
185185let terminalHistoryIndex = 0 ;
186186let terminalDraft = "" ;
187187let blackflagShotLockUntil = 0 ;
188+ let pulseWaveLayer = null ;
189+ let pulseWaveRing = null ;
190+ let pulseCoreFlash = null ;
191+ let sparkBatchId = 0 ;
188192const typewriterTokens = new WeakMap ( ) ;
189193const heroTaglineVariants = [
190194 "Aura Farmer // Chaotic Fun 🚀" ,
@@ -504,39 +508,65 @@ function triggerPulseBackdrop(clientX = null, clientY = null) {
504508 const x = typeof clientX === "number" && clientX > 0 ? clientX : width * 0.5 ;
505509 const y = typeof clientY === "number" && clientY > 0 ? clientY : height * 0.35 ;
506510
507- const layer = document . createElement ( "div" ) ;
508- layer . className = "pulse-wave" ;
509- layer . style . setProperty ( "--pulse-x" , `${ Math . round ( ( x / width ) * 100 ) } %` ) ;
510- layer . style . setProperty ( "--pulse-y" , `${ Math . round ( ( y / height ) * 100 ) } %` ) ;
511+ const isConstrained =
512+ window . matchMedia ?. ( "(max-width: 820px)" ) ?. matches
513+ || window . matchMedia ?. ( "(pointer: coarse)" ) ?. matches
514+ || false ;
515+ const isFirefoxLike = navigator . userAgent . includes ( "Firefox" ) || navigator . userAgent . includes ( "LibreWolf" ) ;
516+
517+ if ( ! pulseWaveLayer ) {
518+ pulseWaveLayer = document . createElement ( "div" ) ;
519+ pulseWaveLayer . className = "pulse-wave" ;
520+ pulseWaveRing = document . createElement ( "span" ) ;
521+ pulseWaveRing . className = "pulse-wave-ring" ;
522+ pulseWaveLayer . appendChild ( pulseWaveRing ) ;
523+ document . body . appendChild ( pulseWaveLayer ) ;
524+ }
511525
512- document . body . appendChild ( layer ) ;
526+ if ( ! pulseCoreFlash ) {
527+ pulseCoreFlash = document . createElement ( "span" ) ;
528+ pulseCoreFlash . className = "pulse-core-flash" ;
529+ document . body . appendChild ( pulseCoreFlash ) ;
530+ }
513531
514- // Force animation start reliably across restricted browsers.
515- void layer . offsetWidth ;
516- layer . classList . add ( "is-active" ) ;
517- requestAnimationFrame ( ( ) => layer . classList . add ( "is-active" ) ) ;
532+ const px = `${ Math . round ( ( x / width ) * 100 ) } %` ;
533+ const py = `${ Math . round ( ( y / height ) * 100 ) } %` ;
534+ pulseWaveLayer . style . setProperty ( "--pulse-x" , px ) ;
535+ pulseWaveLayer . style . setProperty ( "--pulse-y" , py ) ;
536+ pulseCoreFlash . style . left = `${ x } px` ;
537+ pulseCoreFlash . style . top = `${ y } px` ;
518538
519- // Extra visible fallback flash for browsers that suppress blend/animation effects.
520- const flash = document . createElement ( "span" ) ;
521- flash . className = "pulse-core-flash" ;
522- flash . style . left = `${ x } px` ;
523- flash . style . top = `${ y } px` ;
524- document . body . appendChild ( flash ) ;
525- if ( typeof flash . animate === "function" ) {
526- flash . animate (
527- [
528- { transform : "translate(-50%, -50%) scale(0.45)" , opacity : 0.9 } ,
529- { transform : "translate(-50%, -50%) scale(3.6)" , opacity : 0 } ,
530- ] ,
531- { duration : 420 , easing : "cubic-bezier(0.2, 0.7, 0.3, 1)" , fill : "forwards" }
532- ) . onfinish = ( ) => flash . remove ( ) ;
533- } else {
534- window . setTimeout ( ( ) => flash . remove ( ) , 450 ) ;
535- }
539+ pulseWaveLayer . getAnimations ( ) . forEach ( ( animation ) => animation . cancel ( ) ) ;
540+ pulseWaveRing ?. getAnimations ( ) . forEach ( ( animation ) => animation . cancel ( ) ) ;
541+ pulseCoreFlash . getAnimations ( ) . forEach ( ( animation ) => animation . cancel ( ) ) ;
536542
537- const cleanup = ( ) => layer . remove ( ) ;
538- layer . addEventListener ( "animationend" , cleanup , { once : true } ) ;
539- window . setTimeout ( cleanup , 1150 ) ;
543+ const waveDuration = isConstrained ? 640 : 820 ;
544+ const waveScaleTo = isConstrained ? 1.12 : 1.2 ;
545+ pulseWaveLayer . animate (
546+ [
547+ { transform : "scale(0.92)" , opacity : 0 } ,
548+ { opacity : isFirefoxLike ? 0.62 : 0.82 , offset : 0.18 } ,
549+ { opacity : 0.3 , offset : 0.6 } ,
550+ { transform : `scale(${ waveScaleTo } )` , opacity : 0 } ,
551+ ] ,
552+ { duration : waveDuration , easing : "cubic-bezier(0.16, 0.82, 0.27, 1)" , fill : "forwards" }
553+ ) ;
554+
555+ pulseWaveRing ?. animate (
556+ [
557+ { transform : "translate(-50%, -50%) scale(0.34)" , opacity : 0.95 } ,
558+ { transform : `translate(-50%, -50%) scale(${ isConstrained ? 9.5 : 13 } )` , opacity : 0 } ,
559+ ] ,
560+ { duration : waveDuration , easing : "ease-out" , fill : "forwards" }
561+ ) ;
562+
563+ pulseCoreFlash . animate (
564+ [
565+ { transform : "translate(-50%, -50%) scale(0.45)" , opacity : 0.9 } ,
566+ { transform : "translate(-50%, -50%) scale(3.4)" , opacity : 0 } ,
567+ ] ,
568+ { duration : isConstrained ? 320 : 420 , easing : "cubic-bezier(0.2, 0.7, 0.3, 1)" , fill : "forwards" }
569+ ) ;
540570}
541571
542572function playPulseSound ( pulseCount = 1 ) {
@@ -1779,16 +1809,30 @@ function showToast(message) {
17791809}
17801810
17811811function spawnSparks ( total ) {
1782- for ( let i = 0 ; i < total ; i += 1 ) {
1783- window . setTimeout ( ( ) => {
1784- const spark = document . createElement ( "span" ) ;
1785- spark . className = "spark" ;
1786- spark . style . left = `${ Math . random ( ) * 98 } vw` ;
1787- spark . style . setProperty ( "--dx" , `${ Math . random ( ) * 110 - 55 } px` ) ;
1788- document . body . appendChild ( spark ) ;
1789- window . setTimeout ( ( ) => spark . remove ( ) , 1200 ) ;
1790- } , i * 38 ) ;
1812+ const isConstrained =
1813+ window . matchMedia ?. ( "(max-width: 820px)" ) ?. matches
1814+ || window . matchMedia ?. ( "(pointer: coarse)" ) ?. matches
1815+ || false ;
1816+ const isFirefoxLike = navigator . userAgent . includes ( "Firefox" ) || navigator . userAgent . includes ( "LibreWolf" ) ;
1817+ const densityScale = isConstrained ? 0.35 : ( isFirefoxLike ? 0.55 : 1 ) ;
1818+ const batchTotal = Math . max ( 3 , Math . round ( total * densityScale ) ) ;
1819+ const batchId = `s${ Date . now ( ) } -${ sparkBatchId += 1 } ` ;
1820+ const fragment = document . createDocumentFragment ( ) ;
1821+
1822+ for ( let i = 0 ; i < batchTotal ; i += 1 ) {
1823+ const spark = document . createElement ( "span" ) ;
1824+ spark . className = "spark" ;
1825+ spark . dataset . sparkBatch = batchId ;
1826+ spark . style . left = `${ Math . random ( ) * 98 } vw` ;
1827+ spark . style . setProperty ( "--dx" , `${ Math . random ( ) * 110 - 55 } px` ) ;
1828+ spark . style . setProperty ( "--spark-delay" , `${ i * 28 } ms` ) ;
1829+ fragment . appendChild ( spark ) ;
17911830 }
1831+
1832+ document . body . appendChild ( fragment ) ;
1833+ window . setTimeout ( ( ) => {
1834+ document . querySelectorAll ( `[data-spark-batch="${ batchId } "]` ) . forEach ( ( node ) => node . remove ( ) ) ;
1835+ } , 1400 + batchTotal * 28 ) ;
17921836}
17931837
17941838let matrixCanvas = null ;
0 commit comments