@hdcodedev/snowfall 1.0.11 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,13 +12,13 @@ A realistic snowfall effect for React with physics-based accumulation on surface
12
12
  ## Features
13
13
 
14
14
  - **Realistic Physics** - Wind, wobble, and varied snowflake speeds
15
- - **Surface Accumulation** - Snow naturally accumulates on elements
16
- - **Melting Effect** - Snow gradually melts over time
15
+ - **Surface Accumulation** - Snow naturally piles up on elements
16
+ - **Melting Effect** - Gradual melting over time
17
17
  - **Border-Radius Aware** - Respects rounded corners
18
- - **Smart Detection** - Automatically detects and accumulates on specified elements
19
- - **Optimized Performance** - Efficient canvas rendering with ResizeObserver
20
- - **Toggleable** - Built-in provider for enabling/disabling
21
- - **Responsive** - Adapts to viewport and element size changes
18
+ - **Auto-Detection** - Automatically finds and accumulates on semantic elements
19
+ - **High Performance** - Smooth 60 FPS with adaptive optimizations
20
+ - **Toggleable** - Enable/disable on demand
21
+ - **Responsive** - Adapts to viewport and element changes
22
22
 
23
23
  ## Installation
24
24
 
@@ -107,8 +107,6 @@ const customPhysics = {
107
107
  MAX: 1.6, // Maximum flake radius
108
108
  }
109
109
  };
110
-
111
- // Then use it in your provider - see API section below
112
110
  ```
113
111
 
114
112
  ## API
@@ -117,14 +115,10 @@ const customPhysics = {
117
115
 
118
116
  Wraps your app to provide snowfall context.
119
117
 
120
- **Props:** None
121
-
122
118
  ### `<Snowfall />`
123
119
 
124
120
  The main snowfall canvas component. Must be used within `SnowfallProvider`.
125
121
 
126
- **Props:** None
127
-
128
122
  ### `useSnowfall()`
129
123
 
130
124
  Hook to access snowfall controls. Must be used within `SnowfallProvider`.
@@ -145,7 +139,25 @@ Hook to access snowfall controls. Must be used within `SnowfallProvider`.
145
139
  - The snowfall canvas has `pointer-events: none`, so it won't interfere with user interactions
146
140
  - Snow accumulation works best on static or slowly-changing layouts
147
141
  - The component uses `'use client'` directive for Next.js 13+ App Router compatibility
148
- - For best performance, limit the number of accumulation surfaces
142
+
143
+ ## Performance Optimizations
144
+
145
+ Key optimizations for smooth 60 FPS performance:
146
+
147
+ - **Probabilistic Collision Detection**: Only 30% of snowflakes check collisions per frame (configurable via `COLLISION_CHECK_RATE`), significantly reducing CPU load while maintaining visual quality
148
+ - **Adaptive Spawn Rate**: Automatically reduces snowflake spawning when FPS drops below 40 to prevent performance degradation
149
+ - **Viewport Culling**: Only renders accumulation for visible elements
150
+ - **Zero-allocation FPS Tracking**: Uses a second-bucket approach to eliminate per-frame memory allocations
151
+
152
+ **Tuning for lower-end devices:**
153
+ ```tsx
154
+ const customPhysics = {
155
+ ...DEFAULT_PHYSICS,
156
+ MAX_FLAKES: 200,
157
+ COLLISION_CHECK_RATE: 0.1,
158
+ MAX_SURFACES: 15,
159
+ };
160
+ ```
149
161
 
150
162
  ## Development
151
163
 
@@ -171,12 +183,6 @@ Licensed under the [Apache 2.0 License](LICENSE).
171
183
 
172
184
  ## Contributing
173
185
 
174
- Contributions are welcome! Please feel free to submit a Pull Request.
175
-
176
- ## Show Your Support
177
-
178
- If you like this project, please consider giving it a star on GitHub!
179
-
180
- ## Issues
186
+ Contributions are welcome! If you like this project, please give it a star
181
187
 
182
- Found a bug? Please [open an issue](https://github.com/hdcodedev/snowfall/issues) on GitHub.
188
+ Found a bug or have a feature request? [Open an issue](https://github.com/hdcodedev/snowfall/issues) on GitHub.
package/dist/index.d.mts CHANGED
@@ -49,9 +49,10 @@ interface SnowfallContextType {
49
49
  metrics: PerformanceMetrics | null;
50
50
  setMetrics: (metrics: PerformanceMetrics) => void;
51
51
  }
52
- declare function SnowfallProvider({ children, initialDebug }: {
52
+ declare function SnowfallProvider({ children, initialDebug, initialEnabled }: {
53
53
  children: ReactNode;
54
54
  initialDebug?: boolean;
55
+ initialEnabled?: boolean;
55
56
  }): react_jsx_runtime.JSX.Element;
56
57
  declare function useSnowfall(): SnowfallContextType;
57
58
 
package/dist/index.d.ts CHANGED
@@ -49,9 +49,10 @@ interface SnowfallContextType {
49
49
  metrics: PerformanceMetrics | null;
50
50
  setMetrics: (metrics: PerformanceMetrics) => void;
51
51
  }
52
- declare function SnowfallProvider({ children, initialDebug }: {
52
+ declare function SnowfallProvider({ children, initialDebug, initialEnabled }: {
53
53
  children: ReactNode;
54
54
  initialDebug?: boolean;
55
+ initialEnabled?: boolean;
55
56
  }): react_jsx_runtime.JSX.Element;
56
57
  declare function useSnowfall(): SnowfallContextType;
57
58
 
package/dist/index.js CHANGED
@@ -58,8 +58,8 @@ var DEFAULT_PHYSICS = {
58
58
  // 30% of snowflakes check collisions per frame
59
59
  };
60
60
  var SnowfallContext = (0, import_react.createContext)(void 0);
61
- function SnowfallProvider({ children, initialDebug = false }) {
62
- const [isEnabled, setIsEnabled] = (0, import_react.useState)(true);
61
+ function SnowfallProvider({ children, initialDebug = false, initialEnabled = true }) {
62
+ const [isEnabled, setIsEnabled] = (0, import_react.useState)(initialEnabled);
63
63
  const [physicsConfig, setPhysicsConfig] = (0, import_react.useState)(DEFAULT_PHYSICS);
64
64
  const [debugMode, setDebugMode] = (0, import_react.useState)(initialDebug);
65
65
  const [metrics, setMetrics] = (0, import_react.useState)(null);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/components/Snowfall.tsx","../src/components/SnowfallProvider.tsx","../src/core/constants.ts","../src/core/dom.ts","../src/core/physics.ts","../src/hooks/usePerformanceMetrics.ts","../src/hooks/useSnowfallCanvas.ts","../src/hooks/useAnimationLoop.ts","../src/core/draw.ts","../src/components/DebugPanel.tsx"],"sourcesContent":["'use client';\n\n// Main components\nexport { default as Snowfall } from './components/Snowfall';\nexport { SnowfallProvider, useSnowfall, DEFAULT_PHYSICS } from './components/SnowfallProvider';\nexport { default as DebugPanel } from './components/DebugPanel';\n\n// Types\nexport type { PhysicsConfig, PerformanceMetrics } from './components/SnowfallProvider';\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { initializeAccumulation } from '../core/physics';\nimport { usePerformanceMetrics } from '../hooks/usePerformanceMetrics';\nimport { useSnowfallCanvas } from '../hooks/useSnowfallCanvas';\nimport { useAnimationLoop } from '../hooks/useAnimationLoop';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n\n // Canvas setup\n const { canvasRef, dprRef, resizeCanvas } = useSnowfallCanvas();\n\n // Performance metrics tracking\n const { metricsRef, updateFps, getCurrentFps, buildMetrics } = usePerformanceMetrics();\n\n // Animation loop\n const { start: startAnimation, stop: stopAnimation, markRectsDirty } = useAnimationLoop({\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n resizeCanvas();\n\n snowflakesRef.current = [];\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n initAccumulationWrapper();\n }\n });\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n // Mark rects dirty so they get recalculated on next frame\n markRectsDirty();\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n // Start the animation loop\n startAnimation();\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n markRectsDirty();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Observe window/body resize\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Observe DOM mutations to detect new/removed elements\n const mutationObserver = new MutationObserver((mutations) => {\n // Check if any mutations actually added or removed nodes\n let hasStructuralChange = false;\n for (const mutation of mutations) {\n if (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) {\n hasStructuralChange = true;\n break;\n }\n }\n if (hasStructuralChange) {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n metricsRef.current.scanTime = performance.now() - scanStart;\n markRectsDirty();\n }\n });\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n stopAnimation();\n window.removeEventListener('resize', handleResize);\n windowResizeObserver.disconnect();\n mutationObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n COLLISION_CHECK_RATE: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15,\n COLLISION_CHECK_RATE: 0.3 // 30% of snowflakes check collisions per frame\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false }: { children: ReactNode; initialDebug?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\n\nexport type { ElementSurface };\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../components/SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n const initialWobble = noise.wobblePhase * TAU;\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: initialWobble,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,\n rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\n/**\n * Accumulate snow on a side array and return the new max height.\n * The caller should update acc.leftMax or acc.rightMax with the returned value.\n */\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig,\n currentMax: number\n): number => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n let newMax = currentMax;\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n sideArray[y] = newHeight;\n if (newHeight > newMax) newMax = newHeight;\n }\n }\n return newMax;\n};\n\n/**\n * Update snowflake position and animation state.\n * This must happen every frame for smooth animation.\n */\nconst updateSnowflakePosition = (flake: Snowflake, dt: number): void => {\n flake.wobble += flake.wobbleSpeed * dt;\n\n // Calculate position with wobble effect\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n};\n\n/**\n * Check and handle side collisions (left/right edges of elements).\n * Returns true if snowflake landed on a side.\n */\nconst checkSideCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n config: PhysicsConfig\n): boolean => {\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n if (!isInVerticalBounds || acc.maxSideHeight <= 0) {\n return false;\n }\n\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (isCorner) {\n return false;\n }\n\n // Check left side\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);\n return true;\n }\n\n // Check right side\n if (flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);\n return true;\n }\n\n return false;\n};\n\n/**\n * Check and handle top/bottom surface collisions.\n * Returns true if snowflake landed on the surface.\n */\nconst checkSurfaceCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n isBottom: boolean,\n config: PhysicsConfig\n): boolean => {\n if (flakeViewportX < rect.left || flakeViewportX > rect.right) {\n return false;\n }\n\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY < surfaceY || flakeViewportY >= surfaceY + 10 || currentHeight >= maxHeight) {\n return false;\n }\n\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n // For bottom surfaces, only land if accumulation happened\n if (isBottom) {\n return true; // We only get here if shouldAccumulate was true\n }\n }\n\n // For top surfaces, always land when hitting the surface\n // For bottom surfaces that didn't accumulate, don't land (return false)\n return !isBottom;\n};\n\n/**\n * Check if snowflake should be removed (out of bounds or landed).\n */\nconst shouldRemoveSnowflake = (\n flake: Snowflake,\n landed: boolean,\n worldWidth: number,\n worldHeight: number\n): boolean => {\n return landed ||\n flake.y > worldHeight + 10 ||\n flake.x < -20 ||\n flake.x > worldWidth + 20;\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n // Always update position and animation (cheap, must happen every frame)\n updateSnowflakePosition(flake, dt);\n\n let landed = false;\n\n // OPTIMIZATION: Probabilistic collision detection\n // Only check collisions for a percentage of snowflakes per frame\n // Configurable via config.COLLISION_CHECK_RATE\n const shouldCheckCollision = Math.random() < config.COLLISION_CHECK_RATE;\n\n if (shouldCheckCollision) {\n // Map flake from World Space to Viewport Space for collision detection\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Check side collisions (left/right edges)\n if (!landed && !isBottom) {\n landed = checkSideCollision(flake, flakeViewportX, flakeViewportY, rect, acc, config);\n if (landed) break;\n }\n\n // Check top/bottom surface collisions\n if (!landed) {\n landed = checkSurfaceCollision(flake, flakeViewportX, flakeViewportY, rect, acc, isBottom, config);\n if (landed) break;\n }\n }\n }\n\n // Remove snowflake if it landed or went out of bounds\n if (shouldRemoveSnowflake(flake, landed, worldWidth, worldHeight)) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n // Melt sides and update max values\n let leftMax = 0;\n let rightMax = 0;\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) {\n acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];\n }\n if (acc.rightSide[i] > 0) {\n acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];\n }\n }\n acc.leftMax = leftMax;\n acc.rightMax = rightMax;\n }\n};\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { PerformanceMetrics } from '../components/SnowfallProvider';\n\n/**\n * Custom hook for tracking performance metrics with zero per-frame allocations.\n * Uses a second-bucket approach for FPS calculation instead of array filtering.\n */\nexport function usePerformanceMetrics() {\n // FPS tracking - second-bucket approach (zero allocations per frame)\n const lastFpsSecondRef = useRef(0);\n const framesInSecondRef = useRef(0);\n const currentFpsRef = useRef(0);\n\n // Detailed timing metrics\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n /**\n * Update FPS counter - call once per frame.\n * Uses second-bucket approach to avoid per-frame array allocations.\n */\n const updateFps = useCallback((now: number) => {\n const currentSecond = Math.floor(now / 1000);\n if (currentSecond !== lastFpsSecondRef.current) {\n // New second started - store the count from previous second\n currentFpsRef.current = framesInSecondRef.current;\n framesInSecondRef.current = 1;\n lastFpsSecondRef.current = currentSecond;\n } else {\n framesInSecondRef.current++;\n }\n }, []);\n\n /**\n * Get current FPS value.\n */\n const getCurrentFps = useCallback(() => {\n return currentFpsRef.current || framesInSecondRef.current;\n }, []);\n\n /**\n * Build metrics object for reporting to context.\n */\n const buildMetrics = useCallback((\n surfaceCount: number,\n flakeCount: number,\n maxFlakes: number\n ): PerformanceMetrics => {\n return {\n fps: currentFpsRef.current || framesInSecondRef.current,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount,\n flakeCount,\n maxFlakes,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n };\n }, []);\n\n return {\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\n\n/**\n * Custom hook for managing the snowfall canvas setup and resizing.\n * Handles DPR scaling for high-density displays.\n */\nexport function useSnowfallCanvas() {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n const resizeCanvas = useCallback(() => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n }, []);\n\n return {\n canvasRef,\n dprRef,\n resizeCanvas,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { PhysicsConfig } from '../components/SnowfallProvider';\nimport { getElementRects, ElementSurface } from '../core/dom';\nimport { createSnowflake, meltAndSmoothAccumulation, updateSnowflakes } from '../core/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from '../core/draw';\n\ninterface UseAnimationLoopParams {\n canvasRef: { current: HTMLCanvasElement | null };\n dprRef: { current: number };\n snowflakesRef: { current: Snowflake[] };\n accumulationRef: { current: Map<Element, SnowAccumulation> };\n isEnabledRef: { current: boolean };\n physicsConfigRef: { current: PhysicsConfig };\n metricsRef: {\n current: {\n scanTime: number;\n rectUpdateTime: number;\n frameTime: number;\n rafGap: number;\n clearTime: number;\n physicsTime: number;\n drawTime: number;\n }\n };\n updateFps: (now: number) => void;\n getCurrentFps: () => number;\n buildMetrics: (surfaceCount: number, flakeCount: number, maxFlakes: number) => any;\n setMetricsRef: { current: (metrics: any) => void };\n}\n\n/**\n * Custom hook for the RAF animation loop.\n * Handles all rendering, physics, and drawing logic.\n */\nexport function useAnimationLoop(params: UseAnimationLoopParams) {\n const animationIdRef = useRef<number>(0);\n const lastTimeRef = useRef(0);\n const lastMetricsUpdateRef = useRef(0);\n const elementRectsRef = useRef<ElementSurface[]>([]);\n // Dirty flag for rect updates - only recalculate when needed (resize, element changes)\n const dirtyRectsRef = useRef(true);\n\n const animate = useCallback((currentTime: number) => {\n const {\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n } = params;\n\n const canvas = canvasRef.current;\n if (!canvas) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n if (lastTimeRef.current === 0) {\n lastTimeRef.current = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTimeRef.current, 50);\n\n // Track FPS using second-bucket approach (zero allocations per frame)\n const now = performance.now();\n updateFps(now);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTimeRef.current;\n\n lastTimeRef.current = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // Only update element rects when dirty (resize, element added/removed)\n // For fixed/sticky elements, getBoundingClientRect values don't change on scroll\n if (dirtyRectsRef.current) {\n const rectStart = performance.now();\n elementRectsRef.current = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n dirtyRectsRef.current = false;\n }\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRectsRef.current, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRectsRef.current,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = getCurrentFps();\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRectsRef.current.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdateRef.current > 500) {\n setMetricsRef.current(buildMetrics(\n accumulationRef.current.size,\n snowflakes.length,\n physicsConfigRef.current.MAX_FLAKES\n ));\n lastMetricsUpdateRef.current = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n }, [params]);\n\n const start = useCallback(() => {\n lastTimeRef.current = 0;\n lastMetricsUpdateRef.current = 0;\n animationIdRef.current = requestAnimationFrame(animate);\n }, [animate]);\n\n const stop = useCallback(() => {\n cancelAnimationFrame(animationIdRef.current);\n }, []);\n\n // Mark rects dirty - call on resize or when accumulation elements change\n const markRectsDirty = useCallback(() => {\n dirtyRectsRef.current = true;\n }, []);\n\n return {\n start,\n stop,\n markRectsDirty,\n };\n}\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftMax > 0.3;\n const hasRightSnow = acc.rightMax > 0.3;\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","shouldAccumulate","import_react","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/components/Snowfall.tsx","../src/components/SnowfallProvider.tsx","../src/core/constants.ts","../src/core/dom.ts","../src/core/physics.ts","../src/hooks/usePerformanceMetrics.ts","../src/hooks/useSnowfallCanvas.ts","../src/hooks/useAnimationLoop.ts","../src/core/draw.ts","../src/components/DebugPanel.tsx"],"sourcesContent":["'use client';\n\n// Main components\nexport { default as Snowfall } from './components/Snowfall';\nexport { SnowfallProvider, useSnowfall, DEFAULT_PHYSICS } from './components/SnowfallProvider';\nexport { default as DebugPanel } from './components/DebugPanel';\n\n// Types\nexport type { PhysicsConfig, PerformanceMetrics } from './components/SnowfallProvider';\n","'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { initializeAccumulation } from '../core/physics';\nimport { usePerformanceMetrics } from '../hooks/usePerformanceMetrics';\nimport { useSnowfallCanvas } from '../hooks/useSnowfallCanvas';\nimport { useAnimationLoop } from '../hooks/useAnimationLoop';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n\n // Canvas setup\n const { canvasRef, dprRef, resizeCanvas } = useSnowfallCanvas();\n\n // Performance metrics tracking\n const { metricsRef, updateFps, getCurrentFps, buildMetrics } = usePerformanceMetrics();\n\n // Animation loop\n const { start: startAnimation, stop: stopAnimation, markRectsDirty } = useAnimationLoop({\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n resizeCanvas();\n\n snowflakesRef.current = [];\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n initAccumulationWrapper();\n }\n });\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n // Mark rects dirty so they get recalculated on next frame\n markRectsDirty();\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n // Start the animation loop\n startAnimation();\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n markRectsDirty();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Observe window/body resize\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Observe DOM mutations to detect new/removed elements\n const mutationObserver = new MutationObserver((mutations) => {\n // Check if any mutations actually added or removed nodes\n let hasStructuralChange = false;\n for (const mutation of mutations) {\n if (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) {\n hasStructuralChange = true;\n break;\n }\n }\n if (hasStructuralChange) {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n metricsRef.current.scanTime = performance.now() - scanStart;\n markRectsDirty();\n }\n });\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n stopAnimation();\n window.removeEventListener('resize', handleResize);\n windowResizeObserver.disconnect();\n mutationObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n COLLISION_CHECK_RATE: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15,\n COLLISION_CHECK_RATE: 0.3 // 30% of snowflakes check collisions per frame\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false, initialEnabled = true }: { children: ReactNode; initialDebug?: boolean; initialEnabled?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(initialEnabled);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\n\nexport type { ElementSurface };\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../components/SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n const initialWobble = noise.wobblePhase * TAU;\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: initialWobble,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,\n rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\n/**\n * Accumulate snow on a side array and return the new max height.\n * The caller should update acc.leftMax or acc.rightMax with the returned value.\n */\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig,\n currentMax: number\n): number => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n let newMax = currentMax;\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n sideArray[y] = newHeight;\n if (newHeight > newMax) newMax = newHeight;\n }\n }\n return newMax;\n};\n\n/**\n * Update snowflake position and animation state.\n * This must happen every frame for smooth animation.\n */\nconst updateSnowflakePosition = (flake: Snowflake, dt: number): void => {\n flake.wobble += flake.wobbleSpeed * dt;\n\n // Calculate position with wobble effect\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n};\n\n/**\n * Check and handle side collisions (left/right edges of elements).\n * Returns true if snowflake landed on a side.\n */\nconst checkSideCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n config: PhysicsConfig\n): boolean => {\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n if (!isInVerticalBounds || acc.maxSideHeight <= 0) {\n return false;\n }\n\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (isCorner) {\n return false;\n }\n\n // Check left side\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);\n return true;\n }\n\n // Check right side\n if (flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);\n return true;\n }\n\n return false;\n};\n\n/**\n * Check and handle top/bottom surface collisions.\n * Returns true if snowflake landed on the surface.\n */\nconst checkSurfaceCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n isBottom: boolean,\n config: PhysicsConfig\n): boolean => {\n if (flakeViewportX < rect.left || flakeViewportX > rect.right) {\n return false;\n }\n\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY < surfaceY || flakeViewportY >= surfaceY + 10 || currentHeight >= maxHeight) {\n return false;\n }\n\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n // For bottom surfaces, only land if accumulation happened\n if (isBottom) {\n return true; // We only get here if shouldAccumulate was true\n }\n }\n\n // For top surfaces, always land when hitting the surface\n // For bottom surfaces that didn't accumulate, don't land (return false)\n return !isBottom;\n};\n\n/**\n * Check if snowflake should be removed (out of bounds or landed).\n */\nconst shouldRemoveSnowflake = (\n flake: Snowflake,\n landed: boolean,\n worldWidth: number,\n worldHeight: number\n): boolean => {\n return landed ||\n flake.y > worldHeight + 10 ||\n flake.x < -20 ||\n flake.x > worldWidth + 20;\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n // Always update position and animation (cheap, must happen every frame)\n updateSnowflakePosition(flake, dt);\n\n let landed = false;\n\n // OPTIMIZATION: Probabilistic collision detection\n // Only check collisions for a percentage of snowflakes per frame\n // Configurable via config.COLLISION_CHECK_RATE\n const shouldCheckCollision = Math.random() < config.COLLISION_CHECK_RATE;\n\n if (shouldCheckCollision) {\n // Map flake from World Space to Viewport Space for collision detection\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Check side collisions (left/right edges)\n if (!landed && !isBottom) {\n landed = checkSideCollision(flake, flakeViewportX, flakeViewportY, rect, acc, config);\n if (landed) break;\n }\n\n // Check top/bottom surface collisions\n if (!landed) {\n landed = checkSurfaceCollision(flake, flakeViewportX, flakeViewportY, rect, acc, isBottom, config);\n if (landed) break;\n }\n }\n }\n\n // Remove snowflake if it landed or went out of bounds\n if (shouldRemoveSnowflake(flake, landed, worldWidth, worldHeight)) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n // Melt sides and update max values\n let leftMax = 0;\n let rightMax = 0;\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) {\n acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];\n }\n if (acc.rightSide[i] > 0) {\n acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];\n }\n }\n acc.leftMax = leftMax;\n acc.rightMax = rightMax;\n }\n};\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { PerformanceMetrics } from '../components/SnowfallProvider';\n\n/**\n * Custom hook for tracking performance metrics with zero per-frame allocations.\n * Uses a second-bucket approach for FPS calculation instead of array filtering.\n */\nexport function usePerformanceMetrics() {\n // FPS tracking - second-bucket approach (zero allocations per frame)\n const lastFpsSecondRef = useRef(0);\n const framesInSecondRef = useRef(0);\n const currentFpsRef = useRef(0);\n\n // Detailed timing metrics\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n /**\n * Update FPS counter - call once per frame.\n * Uses second-bucket approach to avoid per-frame array allocations.\n */\n const updateFps = useCallback((now: number) => {\n const currentSecond = Math.floor(now / 1000);\n if (currentSecond !== lastFpsSecondRef.current) {\n // New second started - store the count from previous second\n currentFpsRef.current = framesInSecondRef.current;\n framesInSecondRef.current = 1;\n lastFpsSecondRef.current = currentSecond;\n } else {\n framesInSecondRef.current++;\n }\n }, []);\n\n /**\n * Get current FPS value.\n */\n const getCurrentFps = useCallback(() => {\n return currentFpsRef.current || framesInSecondRef.current;\n }, []);\n\n /**\n * Build metrics object for reporting to context.\n */\n const buildMetrics = useCallback((\n surfaceCount: number,\n flakeCount: number,\n maxFlakes: number\n ): PerformanceMetrics => {\n return {\n fps: currentFpsRef.current || framesInSecondRef.current,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount,\n flakeCount,\n maxFlakes,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n };\n }, []);\n\n return {\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\n\n/**\n * Custom hook for managing the snowfall canvas setup and resizing.\n * Handles DPR scaling for high-density displays.\n */\nexport function useSnowfallCanvas() {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n const resizeCanvas = useCallback(() => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n }, []);\n\n return {\n canvasRef,\n dprRef,\n resizeCanvas,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { PhysicsConfig } from '../components/SnowfallProvider';\nimport { getElementRects, ElementSurface } from '../core/dom';\nimport { createSnowflake, meltAndSmoothAccumulation, updateSnowflakes } from '../core/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from '../core/draw';\n\ninterface UseAnimationLoopParams {\n canvasRef: { current: HTMLCanvasElement | null };\n dprRef: { current: number };\n snowflakesRef: { current: Snowflake[] };\n accumulationRef: { current: Map<Element, SnowAccumulation> };\n isEnabledRef: { current: boolean };\n physicsConfigRef: { current: PhysicsConfig };\n metricsRef: {\n current: {\n scanTime: number;\n rectUpdateTime: number;\n frameTime: number;\n rafGap: number;\n clearTime: number;\n physicsTime: number;\n drawTime: number;\n }\n };\n updateFps: (now: number) => void;\n getCurrentFps: () => number;\n buildMetrics: (surfaceCount: number, flakeCount: number, maxFlakes: number) => any;\n setMetricsRef: { current: (metrics: any) => void };\n}\n\n/**\n * Custom hook for the RAF animation loop.\n * Handles all rendering, physics, and drawing logic.\n */\nexport function useAnimationLoop(params: UseAnimationLoopParams) {\n const animationIdRef = useRef<number>(0);\n const lastTimeRef = useRef(0);\n const lastMetricsUpdateRef = useRef(0);\n const elementRectsRef = useRef<ElementSurface[]>([]);\n // Dirty flag for rect updates - only recalculate when needed (resize, element changes)\n const dirtyRectsRef = useRef(true);\n\n const animate = useCallback((currentTime: number) => {\n const {\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n } = params;\n\n const canvas = canvasRef.current;\n if (!canvas) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n if (lastTimeRef.current === 0) {\n lastTimeRef.current = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTimeRef.current, 50);\n\n // Track FPS using second-bucket approach (zero allocations per frame)\n const now = performance.now();\n updateFps(now);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTimeRef.current;\n\n lastTimeRef.current = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // Only update element rects when dirty (resize, element added/removed)\n // For fixed/sticky elements, getBoundingClientRect values don't change on scroll\n if (dirtyRectsRef.current) {\n const rectStart = performance.now();\n elementRectsRef.current = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n dirtyRectsRef.current = false;\n }\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRectsRef.current, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRectsRef.current,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = getCurrentFps();\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRectsRef.current.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdateRef.current > 500) {\n setMetricsRef.current(buildMetrics(\n accumulationRef.current.size,\n snowflakes.length,\n physicsConfigRef.current.MAX_FLAKES\n ));\n lastMetricsUpdateRef.current = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n }, [params]);\n\n const start = useCallback(() => {\n lastTimeRef.current = 0;\n lastMetricsUpdateRef.current = 0;\n animationIdRef.current = requestAnimationFrame(animate);\n }, [animate]);\n\n const stop = useCallback(() => {\n cancelAnimationFrame(animationIdRef.current);\n }, []);\n\n // Mark rects dirty - call on resize or when accumulation elements change\n const markRectsDirty = useCallback(() => {\n dirtyRectsRef.current = true;\n }, []);\n\n return {\n start,\n stop,\n markRectsDirty,\n };\n}\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftMax > 0.3;\n const hasRightSnow = acc.rightMax > 0.3;\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","shouldAccumulate","import_react","import_react","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -29,8 +29,8 @@ var DEFAULT_PHYSICS = {
29
29
  // 30% of snowflakes check collisions per frame
30
30
  };
31
31
  var SnowfallContext = createContext(void 0);
32
- function SnowfallProvider({ children, initialDebug = false }) {
33
- const [isEnabled, setIsEnabled] = useState(true);
32
+ function SnowfallProvider({ children, initialDebug = false, initialEnabled = true }) {
33
+ const [isEnabled, setIsEnabled] = useState(initialEnabled);
34
34
  const [physicsConfig, setPhysicsConfig] = useState(DEFAULT_PHYSICS);
35
35
  const [debugMode, setDebugMode] = useState(initialDebug);
36
36
  const [metrics, setMetrics] = useState(null);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Snowfall.tsx","../src/components/SnowfallProvider.tsx","../src/core/constants.ts","../src/core/dom.ts","../src/core/physics.ts","../src/hooks/usePerformanceMetrics.ts","../src/hooks/useSnowfallCanvas.ts","../src/hooks/useAnimationLoop.ts","../src/core/draw.ts","../src/components/DebugPanel.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { initializeAccumulation } from '../core/physics';\nimport { usePerformanceMetrics } from '../hooks/usePerformanceMetrics';\nimport { useSnowfallCanvas } from '../hooks/useSnowfallCanvas';\nimport { useAnimationLoop } from '../hooks/useAnimationLoop';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n\n // Canvas setup\n const { canvasRef, dprRef, resizeCanvas } = useSnowfallCanvas();\n\n // Performance metrics tracking\n const { metricsRef, updateFps, getCurrentFps, buildMetrics } = usePerformanceMetrics();\n\n // Animation loop\n const { start: startAnimation, stop: stopAnimation, markRectsDirty } = useAnimationLoop({\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n resizeCanvas();\n\n snowflakesRef.current = [];\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n initAccumulationWrapper();\n }\n });\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n // Mark rects dirty so they get recalculated on next frame\n markRectsDirty();\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n // Start the animation loop\n startAnimation();\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n markRectsDirty();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Observe window/body resize\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Observe DOM mutations to detect new/removed elements\n const mutationObserver = new MutationObserver((mutations) => {\n // Check if any mutations actually added or removed nodes\n let hasStructuralChange = false;\n for (const mutation of mutations) {\n if (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) {\n hasStructuralChange = true;\n break;\n }\n }\n if (hasStructuralChange) {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n metricsRef.current.scanTime = performance.now() - scanStart;\n markRectsDirty();\n }\n });\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n stopAnimation();\n window.removeEventListener('resize', handleResize);\n windowResizeObserver.disconnect();\n mutationObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n COLLISION_CHECK_RATE: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15,\n COLLISION_CHECK_RATE: 0.3 // 30% of snowflakes check collisions per frame\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false }: { children: ReactNode; initialDebug?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(true);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\n\nexport type { ElementSurface };\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../components/SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n const initialWobble = noise.wobblePhase * TAU;\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: initialWobble,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,\n rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\n/**\n * Accumulate snow on a side array and return the new max height.\n * The caller should update acc.leftMax or acc.rightMax with the returned value.\n */\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig,\n currentMax: number\n): number => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n let newMax = currentMax;\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n sideArray[y] = newHeight;\n if (newHeight > newMax) newMax = newHeight;\n }\n }\n return newMax;\n};\n\n/**\n * Update snowflake position and animation state.\n * This must happen every frame for smooth animation.\n */\nconst updateSnowflakePosition = (flake: Snowflake, dt: number): void => {\n flake.wobble += flake.wobbleSpeed * dt;\n\n // Calculate position with wobble effect\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n};\n\n/**\n * Check and handle side collisions (left/right edges of elements).\n * Returns true if snowflake landed on a side.\n */\nconst checkSideCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n config: PhysicsConfig\n): boolean => {\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n if (!isInVerticalBounds || acc.maxSideHeight <= 0) {\n return false;\n }\n\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (isCorner) {\n return false;\n }\n\n // Check left side\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);\n return true;\n }\n\n // Check right side\n if (flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);\n return true;\n }\n\n return false;\n};\n\n/**\n * Check and handle top/bottom surface collisions.\n * Returns true if snowflake landed on the surface.\n */\nconst checkSurfaceCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n isBottom: boolean,\n config: PhysicsConfig\n): boolean => {\n if (flakeViewportX < rect.left || flakeViewportX > rect.right) {\n return false;\n }\n\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY < surfaceY || flakeViewportY >= surfaceY + 10 || currentHeight >= maxHeight) {\n return false;\n }\n\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n // For bottom surfaces, only land if accumulation happened\n if (isBottom) {\n return true; // We only get here if shouldAccumulate was true\n }\n }\n\n // For top surfaces, always land when hitting the surface\n // For bottom surfaces that didn't accumulate, don't land (return false)\n return !isBottom;\n};\n\n/**\n * Check if snowflake should be removed (out of bounds or landed).\n */\nconst shouldRemoveSnowflake = (\n flake: Snowflake,\n landed: boolean,\n worldWidth: number,\n worldHeight: number\n): boolean => {\n return landed ||\n flake.y > worldHeight + 10 ||\n flake.x < -20 ||\n flake.x > worldWidth + 20;\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n // Always update position and animation (cheap, must happen every frame)\n updateSnowflakePosition(flake, dt);\n\n let landed = false;\n\n // OPTIMIZATION: Probabilistic collision detection\n // Only check collisions for a percentage of snowflakes per frame\n // Configurable via config.COLLISION_CHECK_RATE\n const shouldCheckCollision = Math.random() < config.COLLISION_CHECK_RATE;\n\n if (shouldCheckCollision) {\n // Map flake from World Space to Viewport Space for collision detection\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Check side collisions (left/right edges)\n if (!landed && !isBottom) {\n landed = checkSideCollision(flake, flakeViewportX, flakeViewportY, rect, acc, config);\n if (landed) break;\n }\n\n // Check top/bottom surface collisions\n if (!landed) {\n landed = checkSurfaceCollision(flake, flakeViewportX, flakeViewportY, rect, acc, isBottom, config);\n if (landed) break;\n }\n }\n }\n\n // Remove snowflake if it landed or went out of bounds\n if (shouldRemoveSnowflake(flake, landed, worldWidth, worldHeight)) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n // Melt sides and update max values\n let leftMax = 0;\n let rightMax = 0;\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) {\n acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];\n }\n if (acc.rightSide[i] > 0) {\n acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];\n }\n }\n acc.leftMax = leftMax;\n acc.rightMax = rightMax;\n }\n};\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { PerformanceMetrics } from '../components/SnowfallProvider';\n\n/**\n * Custom hook for tracking performance metrics with zero per-frame allocations.\n * Uses a second-bucket approach for FPS calculation instead of array filtering.\n */\nexport function usePerformanceMetrics() {\n // FPS tracking - second-bucket approach (zero allocations per frame)\n const lastFpsSecondRef = useRef(0);\n const framesInSecondRef = useRef(0);\n const currentFpsRef = useRef(0);\n\n // Detailed timing metrics\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n /**\n * Update FPS counter - call once per frame.\n * Uses second-bucket approach to avoid per-frame array allocations.\n */\n const updateFps = useCallback((now: number) => {\n const currentSecond = Math.floor(now / 1000);\n if (currentSecond !== lastFpsSecondRef.current) {\n // New second started - store the count from previous second\n currentFpsRef.current = framesInSecondRef.current;\n framesInSecondRef.current = 1;\n lastFpsSecondRef.current = currentSecond;\n } else {\n framesInSecondRef.current++;\n }\n }, []);\n\n /**\n * Get current FPS value.\n */\n const getCurrentFps = useCallback(() => {\n return currentFpsRef.current || framesInSecondRef.current;\n }, []);\n\n /**\n * Build metrics object for reporting to context.\n */\n const buildMetrics = useCallback((\n surfaceCount: number,\n flakeCount: number,\n maxFlakes: number\n ): PerformanceMetrics => {\n return {\n fps: currentFpsRef.current || framesInSecondRef.current,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount,\n flakeCount,\n maxFlakes,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n };\n }, []);\n\n return {\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\n\n/**\n * Custom hook for managing the snowfall canvas setup and resizing.\n * Handles DPR scaling for high-density displays.\n */\nexport function useSnowfallCanvas() {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n const resizeCanvas = useCallback(() => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n }, []);\n\n return {\n canvasRef,\n dprRef,\n resizeCanvas,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { PhysicsConfig } from '../components/SnowfallProvider';\nimport { getElementRects, ElementSurface } from '../core/dom';\nimport { createSnowflake, meltAndSmoothAccumulation, updateSnowflakes } from '../core/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from '../core/draw';\n\ninterface UseAnimationLoopParams {\n canvasRef: { current: HTMLCanvasElement | null };\n dprRef: { current: number };\n snowflakesRef: { current: Snowflake[] };\n accumulationRef: { current: Map<Element, SnowAccumulation> };\n isEnabledRef: { current: boolean };\n physicsConfigRef: { current: PhysicsConfig };\n metricsRef: {\n current: {\n scanTime: number;\n rectUpdateTime: number;\n frameTime: number;\n rafGap: number;\n clearTime: number;\n physicsTime: number;\n drawTime: number;\n }\n };\n updateFps: (now: number) => void;\n getCurrentFps: () => number;\n buildMetrics: (surfaceCount: number, flakeCount: number, maxFlakes: number) => any;\n setMetricsRef: { current: (metrics: any) => void };\n}\n\n/**\n * Custom hook for the RAF animation loop.\n * Handles all rendering, physics, and drawing logic.\n */\nexport function useAnimationLoop(params: UseAnimationLoopParams) {\n const animationIdRef = useRef<number>(0);\n const lastTimeRef = useRef(0);\n const lastMetricsUpdateRef = useRef(0);\n const elementRectsRef = useRef<ElementSurface[]>([]);\n // Dirty flag for rect updates - only recalculate when needed (resize, element changes)\n const dirtyRectsRef = useRef(true);\n\n const animate = useCallback((currentTime: number) => {\n const {\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n } = params;\n\n const canvas = canvasRef.current;\n if (!canvas) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n if (lastTimeRef.current === 0) {\n lastTimeRef.current = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTimeRef.current, 50);\n\n // Track FPS using second-bucket approach (zero allocations per frame)\n const now = performance.now();\n updateFps(now);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTimeRef.current;\n\n lastTimeRef.current = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // Only update element rects when dirty (resize, element added/removed)\n // For fixed/sticky elements, getBoundingClientRect values don't change on scroll\n if (dirtyRectsRef.current) {\n const rectStart = performance.now();\n elementRectsRef.current = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n dirtyRectsRef.current = false;\n }\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRectsRef.current, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRectsRef.current,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = getCurrentFps();\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRectsRef.current.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdateRef.current > 500) {\n setMetricsRef.current(buildMetrics(\n accumulationRef.current.size,\n snowflakes.length,\n physicsConfigRef.current.MAX_FLAKES\n ));\n lastMetricsUpdateRef.current = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n }, [params]);\n\n const start = useCallback(() => {\n lastTimeRef.current = 0;\n lastMetricsUpdateRef.current = 0;\n animationIdRef.current = requestAnimationFrame(animate);\n }, [animate]);\n\n const stop = useCallback(() => {\n cancelAnimationFrame(animationIdRef.current);\n }, []);\n\n // Mark rects dirty - call on resize or when accumulation elements change\n const markRectsDirty = useCallback(() => {\n dirtyRectsRef.current = true;\n }, []);\n\n return {\n start,\n stop,\n markRectsDirty,\n };\n}\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftMax > 0.3;\n const hasRightSnow = acc.rightMax > 0.3;\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useRef","useState","shouldAccumulate","useRef","useCallback","useRef","useCallback","useRef","useCallback","jsx","useRef","useState","useEffect","useState","Fragment","jsx"]}
1
+ {"version":3,"sources":["../src/components/Snowfall.tsx","../src/components/SnowfallProvider.tsx","../src/core/constants.ts","../src/core/dom.ts","../src/core/physics.ts","../src/hooks/usePerformanceMetrics.ts","../src/hooks/useSnowfallCanvas.ts","../src/hooks/useAnimationLoop.ts","../src/core/draw.ts","../src/components/DebugPanel.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef, useState } from 'react';\nimport { useSnowfall } from './SnowfallProvider';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { initializeAccumulation } from '../core/physics';\nimport { usePerformanceMetrics } from '../hooks/usePerformanceMetrics';\nimport { useSnowfallCanvas } from '../hooks/useSnowfallCanvas';\nimport { useAnimationLoop } from '../hooks/useAnimationLoop';\n\nexport default function Snowfall() {\n const { isEnabled, physicsConfig, setMetrics } = useSnowfall();\n const isEnabledRef = useRef(isEnabled);\n const physicsConfigRef = useRef(physicsConfig);\n const setMetricsRef = useRef(setMetrics);\n const [isMounted, setIsMounted] = useState(false);\n const [isVisible, setIsVisible] = useState(false);\n\n const snowflakesRef = useRef<Snowflake[]>([]);\n const accumulationRef = useRef<Map<Element, SnowAccumulation>>(new Map());\n\n // Canvas setup\n const { canvasRef, dprRef, resizeCanvas } = useSnowfallCanvas();\n\n // Performance metrics tracking\n const { metricsRef, updateFps, getCurrentFps, buildMetrics } = usePerformanceMetrics();\n\n // Animation loop\n const { start: startAnimation, stop: stopAnimation, markRectsDirty } = useAnimationLoop({\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n });\n\n useEffect(() => {\n requestAnimationFrame(() => setIsMounted(true));\n }, []);\n\n useEffect(() => {\n isEnabledRef.current = isEnabled;\n }, [isEnabled]);\n\n useEffect(() => {\n physicsConfigRef.current = physicsConfig;\n }, [physicsConfig]);\n\n useEffect(() => {\n setMetricsRef.current = setMetrics;\n }, [setMetrics]);\n\n useEffect(() => {\n if (!isMounted) return;\n\n const canvas = canvasRef.current;\n if (!canvas) return;\n\n const ctx = canvas.getContext('2d');\n if (!ctx) return;\n\n resizeCanvas();\n\n snowflakesRef.current = [];\n\n // Separate observer for snow accumulation surfaces\n const surfaceObserver = new ResizeObserver((entries) => {\n // Check if any accumulation element actually changed size\n let needsUpdate = false;\n for (const entry of entries) {\n if (entry.target.isConnected) {\n needsUpdate = true;\n break;\n }\n }\n if (needsUpdate) {\n // Re-run initialization to adapt to new sizes\n initAccumulationWrapper();\n }\n });\n\n const initAccumulationWrapper = () => {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n\n // Sync observer with current surfaces\n surfaceObserver.disconnect();\n for (const [el] of accumulationRef.current) {\n surfaceObserver.observe(el);\n }\n\n metricsRef.current.scanTime = performance.now() - scanStart;\n // Mark rects dirty so they get recalculated on next frame\n markRectsDirty();\n };\n initAccumulationWrapper();\n\n // Delay visibility slightly to ensure smooth fade-in after canvas is ready\n requestAnimationFrame(() => {\n if (isMounted) setIsVisible(true);\n });\n\n // Start the animation loop\n startAnimation();\n\n const handleResize = () => {\n resizeCanvas();\n accumulationRef.current.clear();\n initAccumulationWrapper();\n markRectsDirty();\n };\n\n window.addEventListener('resize', handleResize);\n\n // Observe window/body resize\n const windowResizeObserver = new ResizeObserver(() => {\n resizeCanvas();\n });\n windowResizeObserver.observe(document.body);\n\n // Observe DOM mutations to detect new/removed elements\n const mutationObserver = new MutationObserver((mutations) => {\n // Check if any mutations actually added or removed nodes\n let hasStructuralChange = false;\n for (const mutation of mutations) {\n if (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0) {\n hasStructuralChange = true;\n break;\n }\n }\n if (hasStructuralChange) {\n const scanStart = performance.now();\n initializeAccumulation(accumulationRef.current, physicsConfigRef.current);\n metricsRef.current.scanTime = performance.now() - scanStart;\n markRectsDirty();\n }\n });\n mutationObserver.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n return () => {\n stopAnimation();\n window.removeEventListener('resize', handleResize);\n windowResizeObserver.disconnect();\n mutationObserver.disconnect();\n surfaceObserver.disconnect();\n };\n }, [isMounted]);\n\n if (!isMounted) return null;\n\n return (\n <>\n <canvas\n ref={canvasRef}\n style={{\n position: 'fixed', // FIXED position to eliminate scroll jitter\n top: 0,\n left: 0,\n pointerEvents: 'none',\n zIndex: 9999,\n opacity: isVisible ? 1 : 0,\n transition: 'opacity 0.3s ease-in',\n willChange: 'transform',\n }}\n aria-hidden=\"true\"\n />\n\n </>\n );\n}\n","'use client';\n\nimport React, { createContext, useContext, useState, ReactNode } from 'react';\n\nexport interface PhysicsConfig {\n MAX_FLAKES: number;\n MELT_SPEED: number;\n WIND_STRENGTH: number;\n ACCUMULATION: {\n SIDE_RATE: number;\n TOP_RATE: number;\n BOTTOM_RATE: number;\n };\n MAX_DEPTH: {\n TOP: number;\n BOTTOM: number;\n SIDE: number;\n };\n FLAKE_SIZE: {\n MIN: number;\n MAX: number;\n };\n MAX_SURFACES: number;\n COLLISION_CHECK_RATE: number;\n}\n\nexport const DEFAULT_PHYSICS: PhysicsConfig = {\n MAX_FLAKES: 1000,\n MELT_SPEED: 0.00001,\n WIND_STRENGTH: 1.5,\n ACCUMULATION: {\n SIDE_RATE: 1,\n TOP_RATE: 5,\n BOTTOM_RATE: 5.0,\n },\n MAX_DEPTH: {\n TOP: 100,\n BOTTOM: 50,\n SIDE: 20,\n },\n FLAKE_SIZE: {\n MIN: 0.5,\n MAX: 1.6,\n },\n MAX_SURFACES: 15,\n COLLISION_CHECK_RATE: 0.3 // 30% of snowflakes check collisions per frame\n};\n\nexport interface PerformanceMetrics {\n fps: number;\n frameTime: number;\n scanTime: number;\n rectUpdateTime: number;\n surfaceCount: number;\n flakeCount: number;\n maxFlakes: number;\n // Detailed metrics\n rafGap: number; // Time between requestAnimationFrame calls\n clearTime: number; // Time to clear canvas\n physicsTime: number; // Time for physics updates\n drawTime: number; // Time to draw snowflakes and accumulation\n}\n\ninterface SnowfallContextType {\n isEnabled: boolean;\n toggleSnow: () => void;\n physicsConfig: PhysicsConfig;\n updatePhysicsConfig: (config: Partial<PhysicsConfig>) => void;\n resetPhysics: () => void;\n debugMode: boolean;\n toggleDebug: () => void;\n metrics: PerformanceMetrics | null;\n setMetrics: (metrics: PerformanceMetrics) => void;\n}\n\nconst SnowfallContext = createContext<SnowfallContextType | undefined>(undefined);\n\nexport function SnowfallProvider({ children, initialDebug = false, initialEnabled = true }: { children: ReactNode; initialDebug?: boolean; initialEnabled?: boolean }) {\n const [isEnabled, setIsEnabled] = useState(initialEnabled);\n const [physicsConfig, setPhysicsConfig] = useState<PhysicsConfig>(DEFAULT_PHYSICS);\n const [debugMode, setDebugMode] = useState(initialDebug);\n const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);\n\n const toggleSnow = () => {\n setIsEnabled((prev) => !prev);\n };\n\n const toggleDebug = () => {\n setDebugMode((prev) => !prev);\n };\n\n const updatePhysicsConfig = (config: Partial<PhysicsConfig>) => {\n setPhysicsConfig((prev) => ({\n ...prev,\n ...config,\n ACCUMULATION: {\n ...prev.ACCUMULATION,\n ...(config.ACCUMULATION || {}),\n },\n MAX_DEPTH: {\n ...prev.MAX_DEPTH,\n ...(config.MAX_DEPTH || {}),\n },\n FLAKE_SIZE: {\n ...prev.FLAKE_SIZE,\n ...(config.FLAKE_SIZE || {}),\n },\n }));\n };\n\n const resetPhysics = () => {\n setPhysicsConfig(DEFAULT_PHYSICS);\n };\n\n return (\n <SnowfallContext.Provider value={{\n isEnabled,\n toggleSnow,\n physicsConfig,\n updatePhysicsConfig,\n resetPhysics,\n debugMode,\n toggleDebug,\n metrics,\n setMetrics,\n }}>\n {children}\n </SnowfallContext.Provider>\n );\n}\n\nexport function useSnowfall() {\n const context = useContext(SnowfallContext);\n if (context === undefined) {\n throw new Error('useSnowfall must be used within a SnowfallProvider');\n }\n return context;\n}\n","export const ATTR_SNOWFALL = 'data-snowfall';\n\nexport const VAL_IGNORE = 'ignore';\nexport const VAL_TOP = 'top';\nexport const VAL_BOTTOM = 'bottom';\n\nexport const TAG_HEADER = 'header';\nexport const TAG_FOOTER = 'footer';\n\nexport const ROLE_BANNER = 'banner';\nexport const ROLE_CONTENTINFO = 'contentinfo';\n\n// Mathematical constants\nexport const TAU = Math.PI * 2; // Full circle in radians\n","import { SnowAccumulation, ElementSurface, SnowfallSurface } from './types';\n\nexport type { ElementSurface };\nimport {\n ATTR_SNOWFALL, VAL_IGNORE, VAL_TOP, VAL_BOTTOM,\n TAG_HEADER, TAG_FOOTER, ROLE_BANNER, ROLE_CONTENTINFO\n} from './constants';\n\n// Headers: snow accumulates on BOTTOM edge\n// Footers: default to TOP surface (snow piles on top)\n// Use data-snowfall attributes to override this behavior\nconst BOTTOM_TAGS = [TAG_HEADER];\nconst BOTTOM_ROLES = [ROLE_BANNER];\n\nconst AUTO_DETECT_TAGS = [TAG_HEADER, TAG_FOOTER, 'article', 'section', 'aside', 'nav'];\nconst AUTO_DETECT_ROLES = [`[role=\"${ROLE_BANNER}\"]`, `[role=\"${ROLE_CONTENTINFO}\"]`, '[role=\"main\"]'];\nconst AUTO_DETECT_CLASSES = [\n '.card', '[class*=\"card\"]', '[class*=\"Card\"]',\n '[class*=\"bg-\"]', '[class*=\"shadow-\"]', '[class*=\"rounded-\"]'\n];\n\n// Helper to get element type (Top vs Bottom surface) based on tags/roles\n// This is used to determine if snow should sit ON TOP or hang from the BOTTOM.\n\nexport const getElementType = (el: Element): SnowfallSurface => {\n const tagName = el.tagName.toLowerCase();\n if (BOTTOM_TAGS.includes(tagName)) return VAL_BOTTOM;\n\n const role = el.getAttribute('role');\n if (role && BOTTOM_ROLES.includes(role)) return VAL_BOTTOM;\n\n // Default: snow accumulates on top of elements (natural physics)\n return VAL_TOP;\n};\n\nconst shouldAccumulate = (el: Element, precomputedStyle?: CSSStyleDeclaration): boolean => {\n if (el.getAttribute(ATTR_SNOWFALL) === VAL_IGNORE) return false;\n // Explicit opt-in\n if (el.hasAttribute(ATTR_SNOWFALL)) return true;\n\n const styles = precomputedStyle || window.getComputedStyle(el);\n const isVisible = styles.display !== 'none' &&\n styles.visibility !== 'hidden' &&\n parseFloat(styles.opacity) > 0.1;\n if (!isVisible) return false;\n\n // Check for visual prominence\n const bgColor = styles.backgroundColor;\n const hasBackground = bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent';\n const hasBorder = (parseFloat(styles.borderWidth) > 0 && styles.borderColor !== 'transparent' && styles.borderColor !== 'rgba(0, 0, 0, 0)') && styles.borderStyle !== 'none';\n const hasBoxShadow = styles.boxShadow !== 'none';\n const hasFilter = styles.filter !== 'none' && styles.filter.includes('drop-shadow');\n const hasBackdropFilter = styles.backdropFilter !== 'none';\n\n return hasBackground || hasBorder || hasBoxShadow || hasFilter || hasBackdropFilter;\n};\n\nexport const getAccumulationSurfaces = (maxSurfaces: number = 5): { el: Element; type: SnowfallSurface }[] => {\n // No explicit clearing needed as we don't cache styles persistently anymore.\n const surfaces: { el: Element; type: SnowfallSurface }[] = [];\n const seen = new Set<Element>();\n\n const candidates = document.querySelectorAll(\n [\n `[${ATTR_SNOWFALL}]`,\n ...AUTO_DETECT_TAGS,\n ...AUTO_DETECT_ROLES,\n ...AUTO_DETECT_CLASSES\n ].join(', ')\n );\n\n for (const el of candidates) {\n if (surfaces.length >= maxSurfaces) break;\n if (seen.has(el)) continue;\n\n // Manual override check first\n const manualOverride = el.getAttribute(ATTR_SNOWFALL);\n if (manualOverride === VAL_IGNORE) continue;\n\n // If manually opted in, skip some heuristic checks but keep basic visibility/size sanity\n const isManuallyIncluded = manualOverride !== null;\n const styles = window.getComputedStyle(el);\n\n // Visibility Check: Must be visible and opaque enough\n const isVisible = styles.display !== 'none' && styles.visibility !== 'hidden' && parseFloat(styles.opacity) > 0.1;\n\n if (!isVisible && !isManuallyIncluded) continue;\n\n const rect = el.getBoundingClientRect();\n const hasSize = rect.width >= 100 && rect.height >= 50;\n if (!hasSize && !isManuallyIncluded) continue;\n\n const isFullPageWrapper = rect.top <= 10 && rect.height >= window.innerHeight * 0.9;\n const isBottomTag = BOTTOM_TAGS.includes(el.tagName.toLowerCase());\n const isBottomRole = BOTTOM_ROLES.includes(el.getAttribute('role') || '');\n const isBottomSurface = isBottomTag || isBottomRole || manualOverride === VAL_BOTTOM;\n\n if (isFullPageWrapper && !isBottomSurface && !isManuallyIncluded) continue;\n\n if (shouldAccumulate(el, styles)) {\n let type: SnowfallSurface = getElementType(el);\n if (manualOverride === VAL_BOTTOM) type = VAL_BOTTOM;\n else if (manualOverride === VAL_TOP) type = VAL_TOP;\n\n surfaces.push({ el, type });\n seen.add(el);\n }\n }\n\n return surfaces;\n};\n\nexport const getElementRects = (accumulationMap: Map<Element, SnowAccumulation>): ElementSurface[] => {\n const elementRects: ElementSurface[] = [];\n for (const [el, acc] of accumulationMap.entries()) {\n if (!el.isConnected) continue;\n // PURE VIEWPORT RECT. No scroll math.\n const rect = el.getBoundingClientRect();\n elementRects.push({ el, rect, acc });\n }\n return elementRects;\n};\n","import { PhysicsConfig } from '../components/SnowfallProvider';\nimport { Snowflake, SnowAccumulation, ElementSurface } from './types';\nimport { getAccumulationSurfaces } from './dom';\nimport { VAL_BOTTOM, TAU } from './constants';\n\n// Opacity buckets for batched rendering (reduce globalAlpha state changes)\nconst OPACITY_BUCKETS = [0.3, 0.5, 0.7, 0.9];\n\n/**\n * Quantize opacity to nearest bucket for efficient batching during draw.\n * This eliminates runtime bucketing overhead by snapping at creation time.\n */\nconst quantizeOpacity = (opacity: number): number => {\n return OPACITY_BUCKETS.reduce((prev, curr) =>\n Math.abs(curr - opacity) < Math.abs(prev - opacity) ? curr : prev\n );\n};\n\nexport const createSnowflake = (\n worldWidth: number,\n config: PhysicsConfig,\n isBackground = false\n): Snowflake => {\n // Two random calls per flake:\n // 1) horizontal position\n // 2) DNA seed for all other traits\n const x = Math.random() * worldWidth;\n const dna = Math.random();\n\n // Pseudo-random trait extraction from DNA\n const noise = {\n speed: (dna * 13) % 1,\n wind: (dna * 7) % 1,\n wobblePhase: (dna * 23) % 1,\n wobbleSpeed: (dna * 5) % 1\n };\n\n const { MIN, MAX } = config.FLAKE_SIZE;\n const sizeRatio = dna;\n\n // Background vs foreground tuning\n const profile = isBackground\n ? {\n sizeMin: MIN * 0.6,\n sizeRange: (MAX - MIN) * 0.4,\n speedBase: 0.2,\n speedScale: 0.3,\n noiseSpeedScale: 0.2,\n windScale: config.WIND_STRENGTH * 0.625,\n opacityBase: 0.2,\n opacityScale: 0.2,\n wobbleBase: 0.005,\n wobbleScale: 0.015\n }\n : {\n sizeMin: MIN,\n sizeRange: MAX - MIN,\n speedBase: 0.5,\n speedScale: 0.5,\n noiseSpeedScale: 0.3,\n windScale: config.WIND_STRENGTH,\n opacityBase: 0.5,\n opacityScale: 0.3,\n wobbleBase: 0.01,\n wobbleScale: 0.02\n };\n\n const radius = profile.sizeMin + sizeRatio * profile.sizeRange;\n const glowRadius = radius * 1.5;\n\n // Calculate opacity and quantize to bucket for efficient batched rendering\n const rawOpacity = profile.opacityBase + sizeRatio * profile.opacityScale;\n const opacity = quantizeOpacity(rawOpacity);\n\n // Pre-calculate glow opacity (also quantized for batching)\n const rawGlowOpacity = opacity * 0.2;\n const glowOpacity = quantizeOpacity(rawGlowOpacity);\n\n const initialWobble = noise.wobblePhase * TAU;\n\n return {\n x: x,\n y: window.scrollY - 5,\n radius: radius,\n glowRadius: glowRadius,\n speed:\n radius * profile.speedScale +\n noise.speed * profile.noiseSpeedScale +\n profile.speedBase,\n wind: (noise.wind - 0.5) * profile.windScale,\n opacity: opacity,\n glowOpacity: glowOpacity,\n wobble: initialWobble,\n wobbleSpeed: noise.wobbleSpeed * profile.wobbleScale + profile.wobbleBase,\n sizeRatio: sizeRatio,\n isBackground: isBackground\n };\n};\n\n/**\n * Initialize max heights array for snow accumulation with edge tapering and smoothing.\n * Exported for benchmarking.\n */\nexport const initializeMaxHeights = (\n width: number,\n baseMax: number,\n borderRadius: number,\n isBottom: boolean = false\n): number[] => {\n let maxHeights = new Array(width);\n for (let i = 0; i < width; i++) {\n let edgeFactor = 1.0;\n if (!isBottom && borderRadius > 0) {\n if (i < borderRadius) {\n edgeFactor = Math.pow(i / borderRadius, 1.2);\n } else if (i > width - borderRadius) {\n edgeFactor = Math.pow((width - i) / borderRadius, 1.2);\n }\n }\n maxHeights[i] = baseMax * edgeFactor * (0.85 + Math.random() * 0.15);\n }\n\n const smoothPasses = 4;\n for (let p = 0; p < smoothPasses; p++) {\n const smoothed = [...maxHeights];\n for (let i = 1; i < width - 1; i++) {\n smoothed[i] = (maxHeights[i - 1] + maxHeights[i] + maxHeights[i + 1]) / 3;\n }\n maxHeights = smoothed;\n }\n\n return maxHeights;\n};\n\nconst calculateCurveOffsets = (width: number, borderRadius: number, isBottom: boolean): number[] => {\n const offsets = new Array(width).fill(0);\n if (borderRadius <= 0 || isBottom) return offsets;\n\n for (let x = 0; x < width; x++) {\n let offset = 0;\n if (x < borderRadius) {\n const dist = borderRadius - x;\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n } else if (x > width - borderRadius) {\n const dist = x - (width - borderRadius);\n offset = borderRadius - Math.sqrt(Math.max(0, borderRadius * borderRadius - dist * dist));\n }\n offsets[x] = offset;\n }\n return offsets;\n};\n\nconst calculateGravityMultipliers = (height: number): number[] => {\n const multipliers = new Array(height);\n for (let i = 0; i < height; i++) {\n const ratio = i / height;\n multipliers[i] = Math.sqrt(ratio);\n }\n return multipliers;\n};\n\nexport const initializeAccumulation = (\n accumulationMap: Map<Element, SnowAccumulation>,\n config: PhysicsConfig\n) => {\n // Scan DOM for new valid surfaces\n const elements = getAccumulationSurfaces(config.MAX_SURFACES);\n // Prune disconnected elements\n for (const [el] of accumulationMap.entries()) {\n if (!el.isConnected) {\n accumulationMap.delete(el);\n }\n }\n\n elements.forEach(({ el, type }) => {\n const existing = accumulationMap.get(el);\n const rect = el.getBoundingClientRect();\n const width = Math.ceil(rect.width);\n const isBottom = type === VAL_BOTTOM;\n\n if (existing && existing.heights.length === width) {\n existing.type = type;\n if (existing.borderRadius !== undefined) {\n const styleBuffer = window.getComputedStyle(el);\n existing.borderRadius = parseFloat(styleBuffer.borderTopLeftRadius) || 0;\n existing.curveOffsets = calculateCurveOffsets(width, existing.borderRadius, isBottom);\n // Initialize gravity multipliers if height matches but they're missing\n if (existing.leftSide.length === Math.ceil(rect.height) && !existing.sideGravityMultipliers) {\n existing.sideGravityMultipliers = calculateGravityMultipliers(existing.leftSide.length);\n }\n }\n return;\n }\n\n const height = Math.ceil(rect.height);\n const baseMax = isBottom ? config.MAX_DEPTH.BOTTOM : config.MAX_DEPTH.TOP;\n\n const styles = window.getComputedStyle(el);\n const borderRadius = parseFloat(styles.borderTopLeftRadius) || 0;\n\n const maxHeights = initializeMaxHeights(width, baseMax, borderRadius, isBottom);\n\n accumulationMap.set(el, {\n heights: existing?.heights.length === width ? existing.heights : new Array(width).fill(0),\n maxHeights,\n leftSide: existing?.leftSide.length === height ? existing.leftSide : new Array(height).fill(0),\n rightSide: existing?.rightSide.length === height ? existing.rightSide : new Array(height).fill(0),\n maxSideHeight: isBottom ? 0 : config.MAX_DEPTH.SIDE,\n leftMax: existing?.leftSide.length === height ? existing.leftMax : 0,\n rightMax: existing?.rightSide.length === height ? existing.rightMax : 0,\n borderRadius,\n curveOffsets: calculateCurveOffsets(width, borderRadius, isBottom),\n sideGravityMultipliers: calculateGravityMultipliers(height),\n type\n });\n });\n};\n\n/**\n * Accumulate snow on a side array and return the new max height.\n * The caller should update acc.leftMax or acc.rightMax with the returned value.\n */\nexport const accumulateSide = (\n sideArray: number[],\n rectHeight: number,\n localY: number,\n maxSideHeight: number,\n borderRadius: number,\n config: PhysicsConfig,\n currentMax: number\n): number => {\n const spread = 4;\n const addHeight = config.ACCUMULATION.SIDE_RATE * (0.8 + Math.random() * 0.4);\n let newMax = currentMax;\n\n for (let dy = -spread; dy <= spread; dy++) {\n const y = localY + dy;\n if (y >= 0 && y < sideArray.length) {\n const inTop = y < borderRadius;\n const inBottom = y > rectHeight - borderRadius;\n if (borderRadius > 0 && (inTop || inBottom)) continue;\n\n const normalizedDist = Math.abs(dy) / spread;\n const falloff = (Math.cos(normalizedDist * Math.PI) + 1) / 2;\n const newHeight = Math.min(maxSideHeight, sideArray[y] + addHeight * falloff);\n sideArray[y] = newHeight;\n if (newHeight > newMax) newMax = newHeight;\n }\n }\n return newMax;\n};\n\n/**\n * Update snowflake position and animation state.\n * This must happen every frame for smooth animation.\n */\nconst updateSnowflakePosition = (flake: Snowflake, dt: number): void => {\n flake.wobble += flake.wobbleSpeed * dt;\n\n // Calculate position with wobble effect\n flake.x += (flake.wind + Math.sin(flake.wobble) * 0.5) * dt;\n flake.y += (flake.speed + Math.cos(flake.wobble * 0.5) * 0.1) * dt;\n};\n\n/**\n * Check and handle side collisions (left/right edges of elements).\n * Returns true if snowflake landed on a side.\n */\nconst checkSideCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n config: PhysicsConfig\n): boolean => {\n const isInVerticalBounds = flakeViewportY >= rect.top && flakeViewportY <= rect.bottom;\n\n if (!isInVerticalBounds || acc.maxSideHeight <= 0) {\n return false;\n }\n\n const localY = Math.floor(flakeViewportY - rect.top);\n const borderRadius = acc.borderRadius;\n\n const isInTopCorner = localY < borderRadius;\n const isInBottomCorner = localY > rect.height - borderRadius;\n const isCorner = borderRadius > 0 && (isInTopCorner || isInBottomCorner);\n\n if (isCorner) {\n return false;\n }\n\n // Check left side\n if (flakeViewportX >= rect.left - 5 && flakeViewportX < rect.left + 3) {\n acc.leftMax = accumulateSide(acc.leftSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.leftMax);\n return true;\n }\n\n // Check right side\n if (flakeViewportX > rect.right - 3 && flakeViewportX <= rect.right + 5) {\n acc.rightMax = accumulateSide(acc.rightSide, rect.height, localY, acc.maxSideHeight, borderRadius, config, acc.rightMax);\n return true;\n }\n\n return false;\n};\n\n/**\n * Check and handle top/bottom surface collisions.\n * Returns true if snowflake landed on the surface.\n */\nconst checkSurfaceCollision = (\n flake: Snowflake,\n flakeViewportX: number,\n flakeViewportY: number,\n rect: DOMRect,\n acc: SnowAccumulation,\n isBottom: boolean,\n config: PhysicsConfig\n): boolean => {\n if (flakeViewportX < rect.left || flakeViewportX > rect.right) {\n return false;\n }\n\n const localX = Math.floor(flakeViewportX - rect.left);\n const currentHeight = acc.heights[localX] || 0;\n const maxHeight = acc.maxHeights[localX] || 5;\n const surfaceY = isBottom ? rect.bottom - currentHeight : rect.top - currentHeight;\n\n if (flakeViewportY < surfaceY || flakeViewportY >= surfaceY + 10 || currentHeight >= maxHeight) {\n return false;\n }\n\n const shouldAccumulate = isBottom ? Math.random() < 0.15 : true;\n\n if (shouldAccumulate) {\n const baseSpread = Math.ceil(flake.radius);\n const spread = baseSpread + Math.floor(Math.random() * 2);\n const accumRate = isBottom ? config.ACCUMULATION.BOTTOM_RATE : config.ACCUMULATION.TOP_RATE;\n const centerOffset = Math.floor(Math.random() * 3) - 1;\n\n for (let dx = -spread; dx <= spread; dx++) {\n if (Math.random() < 0.15) continue;\n const idx = localX + dx + centerOffset;\n if (idx >= 0 && idx < acc.heights.length) {\n const dist = Math.abs(dx);\n const pixelMax = acc.maxHeights[idx] || 5;\n\n const normDist = dist / spread;\n const falloff = (Math.cos(normDist * Math.PI) + 1) / 2;\n const baseAdd = 0.3 * falloff;\n\n const randomFactor = 0.8 + Math.random() * 0.4;\n const addHeight = baseAdd * randomFactor * accumRate;\n\n if (acc.heights[idx] < pixelMax && addHeight > 0) {\n acc.heights[idx] = Math.min(pixelMax, acc.heights[idx] + addHeight);\n }\n }\n }\n\n // For bottom surfaces, only land if accumulation happened\n if (isBottom) {\n return true; // We only get here if shouldAccumulate was true\n }\n }\n\n // For top surfaces, always land when hitting the surface\n // For bottom surfaces that didn't accumulate, don't land (return false)\n return !isBottom;\n};\n\n/**\n * Check if snowflake should be removed (out of bounds or landed).\n */\nconst shouldRemoveSnowflake = (\n flake: Snowflake,\n landed: boolean,\n worldWidth: number,\n worldHeight: number\n): boolean => {\n return landed ||\n flake.y > worldHeight + 10 ||\n flake.x < -20 ||\n flake.x > worldWidth + 20;\n};\n\nexport const updateSnowflakes = (\n snowflakes: Snowflake[],\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number,\n worldWidth: number,\n worldHeight: number\n) => {\n // Scroll used for World -> Viewport mapping for collision\n // Flakes are in World Space.\n // DOM Rects are in Viewport Space (relative to the window).\n // To check collision, we subtract scrollX/Y from Flake coordinates to get their Viewport position.\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n for (let i = snowflakes.length - 1; i >= 0; i--) {\n const flake = snowflakes[i];\n\n // Always update position and animation (cheap, must happen every frame)\n updateSnowflakePosition(flake, dt);\n\n let landed = false;\n\n // OPTIMIZATION: Probabilistic collision detection\n // Only check collisions for a percentage of snowflakes per frame\n // Configurable via config.COLLISION_CHECK_RATE\n const shouldCheckCollision = Math.random() < config.COLLISION_CHECK_RATE;\n\n if (shouldCheckCollision) {\n // Map flake from World Space to Viewport Space for collision detection\n const flakeViewportX = flake.x - scrollX;\n const flakeViewportY = flake.y - scrollY;\n\n for (const item of elementRects) {\n const { rect, acc } = item;\n const isBottom = acc.type === VAL_BOTTOM;\n\n // Check side collisions (left/right edges)\n if (!landed && !isBottom) {\n landed = checkSideCollision(flake, flakeViewportX, flakeViewportY, rect, acc, config);\n if (landed) break;\n }\n\n // Check top/bottom surface collisions\n if (!landed) {\n landed = checkSurfaceCollision(flake, flakeViewportX, flakeViewportY, rect, acc, isBottom, config);\n if (landed) break;\n }\n }\n }\n\n // Remove snowflake if it landed or went out of bounds\n if (shouldRemoveSnowflake(flake, landed, worldWidth, worldHeight)) {\n snowflakes.splice(i, 1);\n }\n }\n};\n\nexport const meltAndSmoothAccumulation = (\n elementRects: ElementSurface[],\n config: PhysicsConfig,\n dt: number\n) => {\n for (const { acc } of elementRects) {\n const meltRate = config.MELT_SPEED * dt;\n const len = acc.heights.length;\n\n // Smooth\n if (len > 2) {\n for (let i = 1; i < len - 1; i++) {\n if (acc.heights[i] > 0.05) {\n const avg = (acc.heights[i - 1] + acc.heights[i + 1]) / 2;\n acc.heights[i] = acc.heights[i] * 0.99 + avg * 0.01;\n }\n }\n }\n\n // Melt\n for (let i = 0; i < acc.heights.length; i++) {\n if (acc.heights[i] > 0) acc.heights[i] = Math.max(0, acc.heights[i] - meltRate);\n }\n // Melt sides and update max values\n let leftMax = 0;\n let rightMax = 0;\n for (let i = 0; i < acc.leftSide.length; i++) {\n if (acc.leftSide[i] > 0) {\n acc.leftSide[i] = Math.max(0, acc.leftSide[i] - meltRate);\n if (acc.leftSide[i] > leftMax) leftMax = acc.leftSide[i];\n }\n if (acc.rightSide[i] > 0) {\n acc.rightSide[i] = Math.max(0, acc.rightSide[i] - meltRate);\n if (acc.rightSide[i] > rightMax) rightMax = acc.rightSide[i];\n }\n }\n acc.leftMax = leftMax;\n acc.rightMax = rightMax;\n }\n};\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { PerformanceMetrics } from '../components/SnowfallProvider';\n\n/**\n * Custom hook for tracking performance metrics with zero per-frame allocations.\n * Uses a second-bucket approach for FPS calculation instead of array filtering.\n */\nexport function usePerformanceMetrics() {\n // FPS tracking - second-bucket approach (zero allocations per frame)\n const lastFpsSecondRef = useRef(0);\n const framesInSecondRef = useRef(0);\n const currentFpsRef = useRef(0);\n\n // Detailed timing metrics\n const metricsRef = useRef({\n scanTime: 0,\n rectUpdateTime: 0,\n frameTime: 0,\n rafGap: 0,\n clearTime: 0,\n physicsTime: 0,\n drawTime: 0,\n });\n\n /**\n * Update FPS counter - call once per frame.\n * Uses second-bucket approach to avoid per-frame array allocations.\n */\n const updateFps = useCallback((now: number) => {\n const currentSecond = Math.floor(now / 1000);\n if (currentSecond !== lastFpsSecondRef.current) {\n // New second started - store the count from previous second\n currentFpsRef.current = framesInSecondRef.current;\n framesInSecondRef.current = 1;\n lastFpsSecondRef.current = currentSecond;\n } else {\n framesInSecondRef.current++;\n }\n }, []);\n\n /**\n * Get current FPS value.\n */\n const getCurrentFps = useCallback(() => {\n return currentFpsRef.current || framesInSecondRef.current;\n }, []);\n\n /**\n * Build metrics object for reporting to context.\n */\n const buildMetrics = useCallback((\n surfaceCount: number,\n flakeCount: number,\n maxFlakes: number\n ): PerformanceMetrics => {\n return {\n fps: currentFpsRef.current || framesInSecondRef.current,\n frameTime: metricsRef.current.frameTime,\n scanTime: metricsRef.current.scanTime,\n rectUpdateTime: metricsRef.current.rectUpdateTime,\n surfaceCount,\n flakeCount,\n maxFlakes,\n rafGap: metricsRef.current.rafGap,\n clearTime: metricsRef.current.clearTime,\n physicsTime: metricsRef.current.physicsTime,\n drawTime: metricsRef.current.drawTime,\n };\n }, []);\n\n return {\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\n\n/**\n * Custom hook for managing the snowfall canvas setup and resizing.\n * Handles DPR scaling for high-density displays.\n */\nexport function useSnowfallCanvas() {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n // Cache DPR to avoid reading it every frame (only changes on resize)\n // Initialize with safe default for SSR, actual value set in resizeCanvas\n const dprRef = useRef(1);\n\n const resizeCanvas = useCallback(() => {\n if (canvasRef.current) {\n // Use viewport dimensions for fixed canvas\n const newWidth = window.innerWidth;\n const newHeight = window.innerHeight;\n\n // Handle high DPI displays - cache DPR in ref for use in animation loop\n const dpr = window.devicePixelRatio || 1;\n dprRef.current = dpr;\n canvasRef.current.width = newWidth * dpr;\n canvasRef.current.height = newHeight * dpr;\n\n // Set CSS size\n canvasRef.current.style.width = `${newWidth}px`;\n canvasRef.current.style.height = `${newHeight}px`;\n }\n }, []);\n\n return {\n canvasRef,\n dprRef,\n resizeCanvas,\n };\n}\n","'use client';\n\nimport { useRef, useCallback } from 'react';\nimport { Snowflake, SnowAccumulation } from '../core/types';\nimport { PhysicsConfig } from '../components/SnowfallProvider';\nimport { getElementRects, ElementSurface } from '../core/dom';\nimport { createSnowflake, meltAndSmoothAccumulation, updateSnowflakes } from '../core/physics';\nimport { drawAccumulations, drawSideAccumulations, drawSnowflakes } from '../core/draw';\n\ninterface UseAnimationLoopParams {\n canvasRef: { current: HTMLCanvasElement | null };\n dprRef: { current: number };\n snowflakesRef: { current: Snowflake[] };\n accumulationRef: { current: Map<Element, SnowAccumulation> };\n isEnabledRef: { current: boolean };\n physicsConfigRef: { current: PhysicsConfig };\n metricsRef: {\n current: {\n scanTime: number;\n rectUpdateTime: number;\n frameTime: number;\n rafGap: number;\n clearTime: number;\n physicsTime: number;\n drawTime: number;\n }\n };\n updateFps: (now: number) => void;\n getCurrentFps: () => number;\n buildMetrics: (surfaceCount: number, flakeCount: number, maxFlakes: number) => any;\n setMetricsRef: { current: (metrics: any) => void };\n}\n\n/**\n * Custom hook for the RAF animation loop.\n * Handles all rendering, physics, and drawing logic.\n */\nexport function useAnimationLoop(params: UseAnimationLoopParams) {\n const animationIdRef = useRef<number>(0);\n const lastTimeRef = useRef(0);\n const lastMetricsUpdateRef = useRef(0);\n const elementRectsRef = useRef<ElementSurface[]>([]);\n // Dirty flag for rect updates - only recalculate when needed (resize, element changes)\n const dirtyRectsRef = useRef(true);\n\n const animate = useCallback((currentTime: number) => {\n const {\n canvasRef,\n dprRef,\n snowflakesRef,\n accumulationRef,\n isEnabledRef,\n physicsConfigRef,\n metricsRef,\n updateFps,\n getCurrentFps,\n buildMetrics,\n setMetricsRef,\n } = params;\n\n const canvas = canvasRef.current;\n if (!canvas) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n if (lastTimeRef.current === 0) {\n lastTimeRef.current = currentTime;\n animationIdRef.current = requestAnimationFrame(animate);\n return;\n }\n\n const deltaTime = Math.min(currentTime - lastTimeRef.current, 50);\n\n // Track FPS using second-bucket approach (zero allocations per frame)\n const now = performance.now();\n updateFps(now);\n\n // Track detailed performance metrics\n metricsRef.current.rafGap = currentTime - lastTimeRef.current;\n\n lastTimeRef.current = currentTime;\n const dt = deltaTime / 16.67;\n\n const frameStartTime = performance.now();\n\n // Time canvas clear\n const clearStart = performance.now();\n\n // Reset transform to clear the entire viewport-sized canvas\n const dpr = dprRef.current;\n ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n ctx.clearRect(0, 0, canvas.width / dpr, canvas.height / dpr);\n\n // Translate the context to Simulate Scrolling\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n ctx.translate(-scrollX, -scrollY);\n\n metricsRef.current.clearTime = performance.now() - clearStart;\n\n const snowflakes = snowflakesRef.current;\n\n // Only update element rects when dirty (resize, element added/removed)\n // For fixed/sticky elements, getBoundingClientRect values don't change on scroll\n if (dirtyRectsRef.current) {\n const rectStart = performance.now();\n elementRectsRef.current = getElementRects(accumulationRef.current);\n metricsRef.current.rectUpdateTime = performance.now() - rectStart;\n dirtyRectsRef.current = false;\n }\n\n // Time physics\n const physicsStart = performance.now();\n meltAndSmoothAccumulation(elementRectsRef.current, physicsConfigRef.current, dt);\n updateSnowflakes(\n snowflakes,\n elementRectsRef.current,\n physicsConfigRef.current,\n dt,\n document.documentElement.scrollWidth,\n document.documentElement.scrollHeight\n );\n metricsRef.current.physicsTime = performance.now() - physicsStart;\n\n // Draw Snowflakes (batched for performance)\n const drawStart = performance.now();\n drawSnowflakes(ctx, snowflakes);\n\n // Spawn new snowflakes with adaptive rate based on performance\n if (isEnabledRef.current && snowflakes.length < physicsConfigRef.current.MAX_FLAKES) {\n const currentFps = getCurrentFps();\n\n // Adaptive spawn rate: reduce load when FPS is low to prevent death spirals\n const shouldSpawn = currentFps >= 40 || Math.random() < 0.2;\n\n if (shouldSpawn) {\n const isBackground = Math.random() < 0.4;\n snowflakes.push(createSnowflake(document.documentElement.scrollWidth, physicsConfigRef.current, isBackground));\n }\n }\n\n // Draw Accumulation\n // Viewport culling: Filter to only visible elements before drawing\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const visibleRects = elementRectsRef.current.filter(({ rect }) =>\n rect.right >= 0 && rect.left <= viewportWidth &&\n rect.bottom >= 0 && rect.top <= viewportHeight\n );\n\n // Only call draw functions if there are visible elements\n if (visibleRects.length > 0) {\n drawAccumulations(ctx, visibleRects, scrollX, scrollY);\n drawSideAccumulations(ctx, visibleRects, scrollX, scrollY);\n }\n\n metricsRef.current.drawTime = performance.now() - drawStart;\n metricsRef.current.frameTime = performance.now() - frameStartTime;\n\n // Update metrics every 500ms\n if (currentTime - lastMetricsUpdateRef.current > 500) {\n setMetricsRef.current(buildMetrics(\n accumulationRef.current.size,\n snowflakes.length,\n physicsConfigRef.current.MAX_FLAKES\n ));\n lastMetricsUpdateRef.current = currentTime;\n }\n\n animationIdRef.current = requestAnimationFrame(animate);\n }, [params]);\n\n const start = useCallback(() => {\n lastTimeRef.current = 0;\n lastMetricsUpdateRef.current = 0;\n animationIdRef.current = requestAnimationFrame(animate);\n }, [animate]);\n\n const stop = useCallback(() => {\n cancelAnimationFrame(animationIdRef.current);\n }, []);\n\n // Mark rects dirty - call on resize or when accumulation elements change\n const markRectsDirty = useCallback(() => {\n dirtyRectsRef.current = true;\n }, []);\n\n return {\n start,\n stop,\n markRectsDirty,\n };\n}\n","import { Snowflake, ElementSurface } from './types';\nimport { VAL_BOTTOM, TAU } from './constants';\n\nconst ACC_FILL_STYLE = 'rgba(255, 255, 255, 0.95)';\nconst ACC_SHADOW_COLOR = 'rgba(200, 230, 255, 0.6)';\n\n\n/**\n * Single-pass snowflake rendering for optimal cache locality.\n * Flakes are tracked in World Space.\n * The Canvas Context is translated by (-scrollX, -scrollY), effectively viewing the World.\n * \n * PERFORMANCE: All glow/core values are pre-calculated at creation time (physics.ts),\n * enabling a simple single-pass render with excellent cache performance.\n */\nexport const drawSnowflakes = (ctx: CanvasRenderingContext2D, flakes: Snowflake[]) => {\n if (flakes.length === 0) return;\n\n ctx.fillStyle = '#FFFFFF';\n\n // Single pass: Draw glow (behind) then core (front) for each flake\n for (const flake of flakes) {\n // Draw glow effect first (behind the core) - skip for background flakes\n if (!flake.isBackground) {\n ctx.globalAlpha = flake.glowOpacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.glowRadius, 0, TAU);\n ctx.fill();\n }\n\n // Draw core on top\n ctx.globalAlpha = flake.opacity;\n ctx.beginPath();\n ctx.arc(flake.x, flake.y, flake.radius, 0, TAU);\n ctx.fill();\n }\n\n // Reset alpha once at the end\n ctx.globalAlpha = 1.0;\n};\n\nexport const drawAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 4;\n ctx.shadowOffsetY = -1;\n // Explicitly reset alpha as we modified it in drawSnowflake\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n // Iterate over all accumulation surfaces to build the single path\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (!acc.heights.some(h => h > 0.1)) continue;\n\n const isBottom = acc.type === VAL_BOTTOM;\n const baseY = isBottom ? rect.bottom - 1 : rect.top + 1;\n\n // Precompute world-space coordinates (hoist additions out of loops)\n const worldLeft = rect.left + scrollX;\n const worldBaseY = baseY + scrollY;\n\n let first = true;\n const step = 2;\n const len = acc.heights.length;\n\n // Draw the uneven top surface of the snow\n for (let x = 0; x < len; x += step) {\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n if (first) {\n ctx.moveTo(px, py);\n first = false;\n } else {\n ctx.lineTo(px, py);\n }\n }\n\n if ((len - 1) % step !== 0) {\n const x = len - 1;\n const height = acc.heights[x] || 0;\n const px = worldLeft + x;\n const py = worldBaseY - height + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n // Draw the bottom edge (aligned with element border) to close the shape\n for (let x = len - 1; x >= 0; x -= step) {\n const px = worldLeft + x;\n const py = worldBaseY + (acc.curveOffsets[x] || 0);\n ctx.lineTo(px, py);\n }\n\n const startX = 0;\n const startPx = worldLeft + startX;\n const startPy = worldBaseY + (acc.curveOffsets[startX] || 0);\n ctx.lineTo(startPx, startPy);\n }\n\n // Fill all accumulations in one pass to batch shadow rendering\n ctx.fill();\n\n ctx.shadowBlur = 0;\n ctx.shadowOffsetY = 0;\n};\n\nexport const drawSideAccumulations = (\n ctx: CanvasRenderingContext2D,\n elementRects: ElementSurface[],\n scrollX: number,\n scrollY: number\n) => {\n ctx.fillStyle = ACC_FILL_STYLE;\n ctx.shadowColor = ACC_SHADOW_COLOR;\n ctx.shadowBlur = 3;\n ctx.globalAlpha = 1.0;\n\n ctx.beginPath();\n\n const drawSide = (sideArray: number[], isLeft: boolean, multipliers: number[], rect: DOMRect, dx: number, dy: number) => {\n const baseX = isLeft ? rect.left : rect.right;\n\n // Precompute world-space coordinates\n const worldBaseX = baseX + dx;\n const worldTop = rect.top + dy;\n const worldBottom = rect.bottom + dy;\n\n ctx.moveTo(worldBaseX, worldTop);\n\n // Draw the uneven side profile\n for (let y = 0; y < sideArray.length; y += 2) {\n const width = sideArray[y] || 0;\n const nextY = Math.min(y + 2, sideArray.length - 1);\n const nextWidth = sideArray[nextY] || 0;\n\n const gravityMultiplier = multipliers[y] || 0;\n\n const py = worldTop + y;\n const px = (isLeft ? worldBaseX - (width * gravityMultiplier) : worldBaseX + (width * gravityMultiplier));\n const ny = worldTop + nextY;\n const nGravityMultiplier = multipliers[nextY] || 0;\n const nx = (isLeft ? worldBaseX - (nextWidth * nGravityMultiplier) : worldBaseX + (nextWidth * nGravityMultiplier));\n\n ctx.lineTo(px, py);\n ctx.lineTo(nx, ny);\n }\n\n // Close at the bottom\n ctx.lineTo(worldBaseX, worldBottom);\n };\n\n // Scan elements and append their side snow profiles to the current path for batched rendering\n for (const item of elementRects) {\n const { rect, acc } = item;\n\n if (acc.maxSideHeight === 0) continue;\n\n const hasLeftSnow = acc.leftMax > 0.3;\n const hasRightSnow = acc.rightMax > 0.3;\n\n if (!hasLeftSnow && !hasRightSnow) continue;\n\n if (hasLeftSnow) drawSide(acc.leftSide, true, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n if (hasRightSnow) drawSide(acc.rightSide, false, acc.sideGravityMultipliers, rect, scrollX, scrollY);\n }\n\n ctx.fill();\n\n ctx.shadowBlur = 0;\n};\n","'use client';\n\nimport { useSnowfall } from './SnowfallProvider';\nimport { useEffect, useState } from 'react';\n\nexport default function DebugPanel({ defaultOpen = true }: { defaultOpen?: boolean }) {\n const { debugMode, toggleDebug, metrics } = useSnowfall();\n const [isMinimized, setIsMinimized] = useState(!defaultOpen);\n const [copied, setCopied] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.shiftKey && e.key === 'D') {\n toggleDebug();\n }\n };\n window.addEventListener('keydown', handleKeyDown);\n return () => window.removeEventListener('keydown', handleKeyDown);\n }, [toggleDebug]);\n\n const copyToClipboard = () => {\n if (metrics) {\n const data = {\n ...metrics,\n timestamp: new Date().toISOString(),\n userAgent: navigator.userAgent,\n canvasSize: {\n width: window.innerWidth,\n height: window.innerHeight,\n },\n };\n navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n }\n };\n\n if (!debugMode) return null;\n\n return (\n <div\n data-snowfall=\"top\"\n style={{\n position: 'fixed',\n bottom: '80px',\n left: '24px',\n backgroundColor: 'rgba(15, 23, 42, 0.75)',\n backdropFilter: 'blur(16px)',\n WebkitBackdropFilter: 'blur(16px)',\n color: '#e2e8f0',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',\n fontSize: '12px',\n padding: isMinimized ? '12px' : '20px',\n borderRadius: '16px',\n zIndex: 10000,\n minWidth: isMinimized ? 'auto' : '300px',\n maxWidth: '100%',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 8px 10px -6px rgba(0, 0, 0, 0.2)',\n transition: 'all 0.2s ease',\n }}>\n <div style={{\n display: 'flex',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: isMinimized ? 0 : '16px',\n gap: '16px'\n }}>\n <div style={{ fontWeight: '600', color: '#fff', display: 'flex', alignItems: 'center', gap: '8px' }}>\n <span style={{ fontSize: '14px' }}>❄️</span>\n <span style={{\n background: 'linear-gradient(to right, #60a5fa, #22d3ee)',\n WebkitBackgroundClip: 'text',\n WebkitTextFillColor: 'transparent',\n fontWeight: '700'\n }}>DEBUG</span>\n </div>\n <div style={{ display: 'flex', gap: '8px' }}>\n <button\n onClick={() => setIsMinimized(!isMinimized)}\n style={{\n background: 'rgba(255, 255, 255, 0.1)',\n border: '1px solid rgba(255, 255, 255, 0.1)',\n color: '#fff',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '10px',\n }}\n >\n {isMinimized ? '▲' : '▼'}\n </button>\n <button\n onClick={toggleDebug}\n style={{\n background: 'rgba(239, 68, 68, 0.15)',\n border: '1px solid rgba(239, 68, 68, 0.2)',\n color: '#f87171',\n cursor: 'pointer',\n width: '24px',\n height: '24px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n borderRadius: '6px',\n fontSize: '12px',\n }}\n >\n ✕\n </button>\n </div>\n </div>\n\n {!isMinimized && metrics && (\n <>\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Performance\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>FPS</span>\n <span style={{ fontWeight: 'bold', color: metrics.fps < 30 ? '#f87171' : metrics.fps < 50 ? '#facc15' : '#4ade80' }}>\n {metrics.fps.toFixed(1)}\n </span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Frame Time</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.frameTime.toFixed(2)}ms</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span style={{ color: metrics.rafGap && metrics.rafGap > 20 ? '#fbbf24' : 'inherit' }}>rAF Gap</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.rafGap?.toFixed(1) || 0}ms</span>\n </div>\n </div>\n\n <div style={{ marginBottom: '12px', paddingBottom: '12px', borderBottom: '1px solid rgba(255,255,255,0.08)' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Detailed Timings\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px 12px' }}>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Clear</span> <span style={{ fontFamily: 'monospace' }}>{metrics.clearTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Physics</span> <span style={{ fontFamily: 'monospace' }}>{metrics.physicsTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Draw</span> <span style={{ fontFamily: 'monospace' }}>{metrics.drawTime?.toFixed(2) || 0}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}><span>Scan</span> <span style={{ fontFamily: 'monospace' }}>{metrics.scanTime.toFixed(2)}ms</span></div>\n <div style={{ display: 'flex', justifyContent: 'space-between', gridColumn: 'span 2' }}><span>Rect Update</span> <span style={{ fontFamily: 'monospace' }}>{metrics.rectUpdateTime.toFixed(2)}ms</span></div>\n </div>\n </div>\n\n <div style={{ marginBottom: '16px' }}>\n <div style={{ color: '#94a3b8', marginBottom: '8px', fontSize: '11px', fontWeight: '600', letterSpacing: '0.5px', textTransform: 'uppercase' }}>\n Counts\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '4px' }}>\n <span>Snowflakes</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.flakeCount} / {metrics.maxFlakes}</span>\n </div>\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>Surfaces</span>\n <span style={{ fontFamily: 'monospace' }}>{metrics.surfaceCount}</span>\n </div>\n </div>\n\n <button\n onClick={copyToClipboard}\n style={{\n width: '100%',\n padding: '10px',\n background: copied ? 'rgba(34, 197, 94, 0.2)' : 'rgba(255, 255, 255, 0.05)',\n border: copied ? '1px solid rgba(34, 197, 94, 0.5)' : '1px solid rgba(255, 255, 255, 0.1)',\n color: copied ? '#4ade80' : '#fff',\n cursor: 'pointer',\n borderRadius: '8px',\n fontSize: '11px',\n fontWeight: '600',\n transition: 'all 0.2s',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: '6px'\n }}\n >\n {copied ? '✓ COPIED' : '📋 COPY METRICS'}\n </button>\n\n <div style={{ marginTop: '12px', fontSize: '10px', color: '#64748b', textAlign: 'center' }}>\n Shift+D to toggle\n </div>\n </>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useRef","useState","shouldAccumulate","useRef","useCallback","useRef","useCallback","useRef","useCallback","jsx","useRef","useState","useEffect","useState","Fragment","jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hdcodedev/snowfall",
3
- "version": "1.0.11",
3
+ "version": "1.0.12",
4
4
  "description": "Realistic snowfall effect for React with physics-based accumulation on surfaces. Features melting, wind, and smart surface detection.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",