@@ -1303,51 +1303,53 @@ <h2 class="section-title">Connect</h2>
13031303 ============================================================ */
13041304 const trailCanvas = document . getElementById ( 'trail-canvas' ) ;
13051305 const trailCtx = trailCanvas . getContext ( '2d' ) ;
1306- const TRAIL_MAX = 64 ;
1307- let trailBuf = new Array ( TRAIL_MAX ) . fill ( null ) ;
1308- let trailHead = 0 ;
1309- let trailLen = 0 ;
1306+ // 16 points max, 150ms window — tight, clean, never scribbles
1307+ const TRAIL_MAX = 16 ;
1308+ const TRAIL_AGE = 150 ; // ms — points older than this are dropped
1309+ let trailPts = [ ] ; // simple array, push/shift — small enough
13101310
1311+ /** @param {number } x @param {number } y @param {number } t */
13111312 function pushTrailPoint ( x , y , t ) {
1312- trailBuf [ trailHead ] = { x, y, t } ;
1313- trailHead = ( trailHead + 1 ) % TRAIL_MAX ;
1314- if ( trailLen < TRAIL_MAX ) trailLen ++ ;
1313+ trailPts . push ( { x, y, t } ) ;
1314+ // Evict points beyond age window first, then cap count
1315+ const cutoff = t - TRAIL_AGE ;
1316+ while ( trailPts . length > 0 && trailPts [ 0 ] . t < cutoff ) trailPts . shift ( ) ;
1317+ if ( trailPts . length > TRAIL_MAX ) trailPts . shift ( ) ;
13151318 }
13161319
13171320 function renderTrail ( now ) {
13181321 trailCtx . clearRect ( 0 , 0 , W , H ) ;
1319- if ( trailLen < 3 ) return ;
13201322
1321- const pts = [ ] ;
1322- for ( let i = 0 ; i < trailLen ; i ++ ) {
1323- const idx = ( trailHead - trailLen + i + TRAIL_MAX ) % TRAIL_MAX ;
1324- pts . push ( trailBuf [ idx ] ) ;
1323+ // Drop stale points on every frame too (mouse may have stopped)
1324+ const cutoff = now - TRAIL_AGE ;
1325+ while ( trailPts . length > 0 && trailPts [ 0 ] . t < cutoff ) trailPts . shift ( ) ;
1326+
1327+ if ( trailPts . length < 2 ) return ;
1328+
1329+ // Draw each consecutive segment with its own fading alpha
1330+ // Newest point = index (length-1) = alpha 1.0
1331+ // Oldest point = index 0 = alpha 0.0
1332+ for ( let i = 0 ; i < trailPts . length - 1 ; i ++ ) {
1333+ const p0 = trailPts [ i ] ;
1334+ const p1 = trailPts [ i + 1 ] ;
1335+
1336+ // t=0 (oldest) → alpha 0, t=1 (newest) → alpha 0.55
1337+ const tNorm = i / ( trailPts . length - 1 ) ;
1338+ const alpha = tNorm * 0.55 ;
1339+ const width = 1 + tNorm * 1.5 ; // tapers from tip to tail
1340+
1341+ trailCtx . beginPath ( ) ;
1342+ trailCtx . moveTo ( p0 . x , p0 . y ) ;
1343+ trailCtx . lineTo ( p1 . x , p1 . y ) ;
1344+ trailCtx . strokeStyle = `rgba(0,212,255,${ alpha . toFixed ( 3 ) } )` ;
1345+ trailCtx . lineWidth = width ;
1346+ trailCtx . lineCap = 'round' ;
1347+ trailCtx . shadowColor = '#00d4ff' ;
1348+ trailCtx . shadowBlur = tNorm * 8 ;
1349+ trailCtx . stroke ( ) ;
13251350 }
13261351
1327- trailCtx . beginPath ( ) ;
1328- trailCtx . moveTo ( pts [ 0 ] . x , pts [ 0 ] . y ) ;
1329-
1330- for ( let i = 1 ; i < pts . length - 1 ; i ++ ) {
1331- const age = ( now - pts [ i ] . t ) / 400 ; // 0 fresh → 1 old
1332- const alpha = Math . max ( 0 , 1 - age ) ;
1333- if ( alpha < 0.01 ) continue ;
1334-
1335- const cx = ( pts [ i ] . x + pts [ i + 1 ] . x ) / 2 ;
1336- const cy = ( pts [ i ] . y + pts [ i + 1 ] . y ) / 2 ;
1337- trailCtx . quadraticCurveTo ( pts [ i ] . x , pts [ i ] . y , cx , cy ) ;
1338- }
1339-
1340- const vel = mouse . x !== null
1341- ? Math . hypot ( mouse . vx , mouse . vy )
1342- : 0 ;
1343- const alpha = Math . max ( 0.1 , Math . min ( 0.6 , vel / 20 ) ) ;
1344-
1345- trailCtx . strokeStyle = `rgba(0,212,255,${ alpha } )` ;
1346- trailCtx . lineWidth = 1.5 ;
1347- trailCtx . shadowColor = '#00d4ff' ;
1348- trailCtx . shadowBlur = 6 ;
1349- trailCtx . stroke ( ) ;
1350- trailCtx . shadowBlur = 0 ;
1352+ trailCtx . shadowBlur = 0 ;
13511353 }
13521354
13531355 /* ============================================================
0 commit comments