Optimize glass morphism effects for smooth, performant user experiences
AtomixGlass automatically uses hardware acceleration through CSS transforms and will-change properties. However, overusing these can cause memory issues.
Higher values for displacement, blur, and aberration require more processing power. Balance visual appeal with performance.
| Property | Standard | Performance | Impact |
|---|---|---|---|
displacementScale | 20 (default) | 10-15 | High |
blurAmount | 1 (default) | 0.5-0.8 | Medium |
saturation | 140 (default) | 120-130 | Low |
aberrationIntensity | 2.5 (default) | 1.5-2 | Medium |
elasticity | 0.05 (default) | 0-0.02 | Variable |
The 'shader' mode uses WebGL for advanced effects and is the most performance-intensive option. Use strategically.
Implement adaptive loading to provide appropriate experiences based on device capabilities. This ensures optimal performance across all devices.
1import { useEffect, useState } from 'react';
2import { AtomixGlass } from '@shohojdhara/atomix';
3
4function AdaptiveGlassComponent() {
5 const [isLowEndDevice, setIsLowEndDevice] = useState(false);
6
7 useEffect(() => {
8 // Simple heuristic for detecting low-end devices
9 const isLowEnd = (
10 navigator.hardwareConcurrency <= 4 ||
11 /Android|iPhone|iPod|iPad/.test(navigator.userAgent)
12 );
13
14 setIsLowEndDevice(isLowEnd);
15 }, []);
16
17 return (
18 <AtomixGlass
19 mode={isLowEndDevice ? 'standard' : 'shader'}
20 displacementScale={isLowEndDevice ? 10 : 20}
21 blurAmount={isLowEndDevice ? 0.5 : 1}
22 saturation={isLowEndDevice ? 120 : 140}
23 aberrationIntensity={isLowEndDevice ? 1.5 : 2.5}
24 elasticity={isLowEndDevice ? 0 : 0.05}
25 withLiquidBlur={!isLowEndDevice}
26 withoutEffects={isLowEndDevice}
27 >
28 Content
29 </AtomixGlass>
30 );
31}1import { useEffect, useState } from 'react';
2
3interface DeviceCapabilities {
4 isLowEnd: boolean;
5 isMobile: boolean;
6 hasGoodGPU: boolean;
7 prefersReducedMotion: boolean;
8}
9
10export function useDeviceCapabilities(): DeviceCapabilities {
11 const [capabilities, setCapabilities] = useState<DeviceCapabilities>({
12 isLowEnd: false,
13 isMobile: false,
14 hasGoodGPU: true,
15 prefersReducedMotion: false,
16 });
17
18 useEffect(() => {
19 // Check CPU cores
20 const cores = navigator.hardwareConcurrency || 2;
21
22 // Check device type
23 const isMobile = /Android|iPhone|iPod|iPad/.test(navigator.userAgent);
24
25 // Check GPU (basic heuristic)
26 const canvas = document.createElement('canvas');
27 const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
28 const debugInfo = gl?.getExtension('WEBGL_debug_renderer_info');
29 const renderer = debugInfo ? gl?.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : '';
30 const hasGoodGPU = !renderer.toLowerCase().includes('intel');
31
32 // Check reduced motion preference
33 const prefersReducedMotion = typeof window !== 'undefined'
34 ? window.matchMedia('(prefers-reduced-motion: reduce)').matches
35 : false;
36
37 // Determine if low-end
38 const isLowEnd = cores <= 4 || isMobile || !hasGoodGPU;
39
40 setCapabilities({
41 isLowEnd,
42 isMobile,
43 hasGoodGPU,
44 prefersReducedMotion,
45 });
46 }, []);
47
48 return capabilities;
49}
50
51// Usage
52function OptimizedGlassComponent() {
53 const { isLowEnd, prefersReducedMotion } = useDeviceCapabilities();
54
55 return (
56 <AtomixGlass
57 mode={isLowEnd ? 'standard' : 'shader'}
58 displacementScale={isLowEnd ? 10 : 20}
59 blurAmount={isLowEnd ? 0.5 : 1}
60 saturation={isLowEnd ? 120 : 140}
61 aberrationIntensity={isLowEnd ? 1.5 : 2.5}
62 elasticity={prefersReducedMotion ? 0 : 0.05}
63 reducedMotion={prefersReducedMotion}
64 withLiquidBlur={!isLowEnd}
65 >
66 Content
67 </AtomixGlass>
68 );
69}Essential tools for performance monitoring
Key metrics for user experience
Time until largest content element is rendered. AtomixGlass can delay LCP if used in hero sections.
Time from first interaction to browser response. Heavy shader effects can increase FID.
Visual stability score. Always set explicit dimensions for AtomixGlass containers.
Responsiveness metric. Debounce interactive glass effects to maintain good INP.
1import { useEffect, useRef } from 'react';
2
3interface PerformanceMetrics {
4 fps: number;
5 renderTime: number;
6 memoryUsage: number;
7}
8
9export function usePerformanceMonitor(
10 onMetrics: (metrics: PerformanceMetrics) => void,
11 interval: number = 1000
12) {
13 const frameCountRef = useRef(0);
14 const lastTimeRef = useRef(performance.now());
15 const rafIdRef = useRef<number>();
16
17 useEffect(() => {
18 const measureFrame = () => {
19 frameCountRef.current++;
20 rafIdRef.current = requestAnimationFrame(measureFrame);
21 };
22
23 const intervalId = setInterval(() => {
24 const now = performance.now();
25 const elapsed = now - lastTimeRef.current;
26 const fps = Math.round((frameCountRef.current * 1000) / elapsed);
27
28 // Get memory usage (if available)
29 const memory = (performance as any).memory;
30 const memoryUsage = memory
31 ? Math.round(memory.usedJSHeapSize / 1048576) // Convert to MB
32 : 0;
33
34 // Get paint timing
35 const paintEntries = performance.getEntriesByType('paint');
36 const renderTime = paintEntries.length > 0
37 ? Math.round(paintEntries[paintEntries.length - 1].startTime)
38 : 0;
39
40 onMetrics({ fps, renderTime, memoryUsage });
41
42 // Reset counters
43 frameCountRef.current = 0;
44 lastTimeRef.current = now;
45 }, interval);
46
47 rafIdRef.current = requestAnimationFrame(measureFrame);
48
49 return () => {
50 clearInterval(intervalId);
51 if (rafIdRef.current) {
52 cancelAnimationFrame(rafIdRef.current);
53 }
54 };
55 }, [interval, onMetrics]);
56}
57
58// Usage
59function MonitoredGlassComponent() {
60 const [metrics, setMetrics] = useState<PerformanceMetrics | null>(null);
61
62 usePerformanceMonitor((m) => {
63 setMetrics(m);
64
65 // Optional: Log warning if FPS drops below 30
66 // if (m.fps < 30) {
67 // console.warn('Low FPS detected:', m.fps);
68 // }
69 });
70
71 return (
72 <div>
73 {metrics && (
74 <div className="performance-overlay">
75 <div>FPS: {metrics.fps}</div>
76 <div>Memory: {metrics.memoryUsage}MB</div>
77 </div>
78 )}
79 <AtomixGlass>Content</AtomixGlass>
80 </div>
81 );
82}Only render AtomixGlass components when they're visible in the viewport. This dramatically reduces GPU load.
1import { useInView } from 'react-intersection-observer';
2
3function LazyGlassComponent() {
4 const { ref, inView } = useInView({
5 threshold: 0.1,
6 triggerOnce: false,
7 });
8
9 return (
10 <div ref={ref}>
11 {inView ? (
12 <AtomixGlass>
13 Expensive content
14 </AtomixGlass>
15 ) : (
16 <div className="placeholder">
17 Loading...
18 </div>
19 )}
20 </div>
21 );
22}Respect user preferences for reduced motion. This also improves performance on low-end devices.
1const prefersReducedMotion = window.matchMedia(
2 '(prefers-reduced-motion: reduce)'
3).matches;
4
5function AccessibleGlassComponent() {
6 return (
7<AtomixGlass
8 animationDuration={prefersReducedMotion ? 0 : 300}
9 elasticity={prefersReducedMotion ? 0 : 0.5}
10 displacementScale={prefersReducedMotion ? 0 : 20}
11>
12 Content
13 </AtomixGlass>
14 );
15}Debounce mouse/touch events to reduce the frequency of expensive shader updates.
1import { useMemo } from 'react';
2import { debounce } from 'lodash';
3
4function DebouncedGlassComponent() {
5 const handleMouseMove = useMemo(
6 () => debounce((e: MouseEvent) => {
7 // Handle mouse move
8 }, 16), // ~60fps
9 []
10 );
11
12 return (
13 <AtomixGlass
14 onMouseMove={handleMouseMove}
15 elasticity={0.5}
16 >
17 Interactive content
18 </AtomixGlass>
19 );
20}Memoize AtomixGlass components to prevent unnecessary re-renders and shader recompilation.
1import { memo } from 'react';
2
3const MemoizedGlassCard = memo(({ title, content }) => {
4 return (
5 <AtomixGlass
6 mode="shader"
7 displacementScale={20}
8 blurAmount={1}
9 saturation={140}
10 aberrationIntensity={2.5}
11 elasticity={0.05}
12 >
13 <h3>{title}</h3>
14 <p>{content}</p>
15 </AtomixGlass>
16 );
17}, (prevProps, nextProps) => {
18 // Custom comparison - only re-render if props change
19 return (
20 prevProps.title === nextProps.title &&
21 prevProps.content === nextProps.content
22 );
23});