CSS Repaints & Reflows đĨ
Understand browser rendering performance and optimize your CSS for buttery-smooth experiences.
What are Repaints & Reflows?
đ Reflow (Layout)
When the browser recalculates the position and geometry of elements.
- Changing width/height/margin/padding
- Adding/removing DOM elements
- Changing font size or family
- Reading layout properties (offsetWidth, etc.)
đĨ Expensive: Can cause layout thrashing
đ¨ Repaint (Paint)
When the browser redraws pixels without changing layout.
- Changing color/background-color
- Changing visibility/opacity
- Adding box-shadows or borders
- Transforms that don't affect layout
⥠Moderate: Less expensive than reflow
đ Live Performance Dashboard
Interactive Features:
- Real-time FPS monitoring - Watch performance impact
- Reflow & repaint counters - See exactly what's happening
- Visual indicators - Know when expensive operations occur
- Compare strategies - See optimized vs unoptimized code
Performance Dashboard Implementation
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>đ Live Dashboard - Repaints & Reflows Demo</title> <style> .dashboard { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 2rem; color: white; font-family: 'Inter', system-ui, sans-serif; } .performance-monitor { background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); padding: 2rem; border-radius: 20px; margin-bottom: 2rem; border: 1px solid rgba(255,255,255,0.2); } .metrics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin: 2rem 0; } .metric-card { background: rgba(255,255,255,0.15); padding: 1.5rem; border-radius: 15px; text-align: center; transition: all 0.3s ease; border: 1px solid rgba(255,255,255,0.1); } .metric-value { font-size: 2.5rem; font-weight: bold; margin-bottom: 0.5rem; } .controls { display: flex; gap: 1rem; margin: 2rem 0; flex-wrap: wrap; } .btn { padding: 1rem 2rem; border: none; border-radius: 10px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; } .btn-danger { background: #e74c3c; color: white; } .btn-success { background: #2ecc71; color: white; } .btn-warning { background: #f39c12; color: white; } .data-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; margin-top: 2rem; } .data-card { background: rgba(255,255,255,0.1); padding: 1.5rem; border-radius: 15px; min-height: 200px; } .reflow-indicator { position: fixed; top: 20px; right: 20px; background: #e74c3c; color: white; padding: 0.5rem 1rem; border-radius: 20px; font-size: 0.8rem; font-weight: bold; opacity: 0; transition: opacity 0.3s ease; } .repaint-indicator { position: fixed; top: 60px; right: 20px; background: #f39c12; color: white; padding: 0.5rem 1rem; border-radius: 20px; font-size: 0.8rem; font-weight: bold; opacity: 0; transition: opacity 0.3s ease; } .expensive-operation { background: rgba(231, 76, 60, 0.3); border: 2px solid #e74c3c; animation: pulse 0.5s ease; } .cheap-operation { background: rgba(46, 204, 113, 0.3); border: 2px solid #2ecc71; } @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.02); } 100% { transform: scale(1); } } .particles { position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: -1; } .particle { position: absolute; background: rgba(255,255,255,0.1); border-radius: 50%; } </style> </head> <body> <div class="dashboard"> <!-- đ¯ Performance Indicators --> <div class="reflow-indicator" id="reflowIndicator">đ REFLOW</div> <div class="repaint-indicator" id="repaintIndicator">đ¨ REPAINT</div> <div class="particles" id="particles"></div> <header> <h1>đ Live Performance Dashboard</h1> <p>Monitor repaints and reflows in real-time</p> </header> <section class="performance-monitor"> <h2>đ Performance Metrics</h2> <div class="metrics-grid"> <div class="metric-card" id="fpsMetric"> <div class="metric-value">60</div> <div class="metric-label">FPS</div> </div> <div class="metric-card" id="reflowMetric"> <div class="metric-value">0</div> <div class="metric-label">Reflows</div> </div> <div class="metric-card" id="repaintMetric"> <div class="metric-value">0</div> <div class="metric-label">Repaints</div> </div> <div class="metric-card" id="memoryMetric"> <div class="metric-value">0 MB</div> <div class="metric-label">Memory</div> </div> </div> <div class="controls"> <button class="btn btn-danger" onclick="triggerReflowHell()"> đĨ Trigger Reflow Hell </button> <button class="btn btn-warning" onclick="triggerRepaintStorm()"> ⥠Trigger Repaint Storm </button> <button class="btn btn-success" onclick="optimizedAnimation()"> đ Optimized Animation </button> <button class="btn" onclick="resetMetrics()"> đ Reset Metrics </button> </div> </section> <section class="data-grid"> <div class="data-card" id="card1"> <h3>User Activity</h3> <div class="activity-chart" style="height: 120px; background: rgba(255,255,255,0.1); border-radius: 10px; margin-top: 1rem;"></div> </div> <div class="data-card" id="card2"> <h3>System Load</h3> <div class="load-chart" style="height: 120px; background: rgba(255,255,255,0.1); border-radius: 10px; margin-top: 1rem;"></div> </div> <div class="data-card" id="card3"> <h3>Network Traffic</h3> <div class="network-chart" style="height: 120px; background: rgba(255,255,255,0.1); border-radius: 10px; margin-top: 1rem;"></div> </div> </section> </div> <script> // đ¯ Performance Monitoring let reflowCount = 0; let repaintCount = 0; let lastFrameTime = performance.now(); let frameCount = 0; let currentFPS = 60; const reflowIndicator = document.getElementById('reflowIndicator'); const repaintIndicator = document.getElementById('repaintIndicator'); const reflowMetric = document.getElementById('reflowMetric'); const repaintMetric = document.getElementById('repaintMetric'); const fpsMetric = document.getElementById('fpsMetric'); const memoryMetric = document.getElementById('memoryMetric'); function showReflow() { reflowCount++; reflowMetric.querySelector('.metric-value').textContent = reflowCount; reflowIndicator.style.opacity = '1'; setTimeout(() => reflowIndicator.style.opacity = '0', 500); } function showRepaint() { repaintCount++; repaintMetric.querySelector('.metric-value').textContent = repaintCount; repaintIndicator.style.opacity = '1'; setTimeout(() => repaintIndicator.style.opacity = '0', 500); } function updateFPS() { frameCount++; const currentTime = performance.now(); if (currentTime >= lastFrameTime + 1000) { currentFPS = Math.round((frameCount * 1000) / (currentTime - lastFrameTime)); fpsMetric.querySelector('.metric-value').textContent = currentFPS; frameCount = 0; lastFrameTime = currentTime; } // Update memory usage if (performance.memory) { const memoryMB = Math.round(performance.memory.usedJSHeapSize / 1048576); memoryMetric.querySelector('.metric-value').textContent = memoryMB + ' MB'; } requestAnimationFrame(updateFPS); } // đ¨ EXPENSIVE OPERATIONS function triggerReflowHell() { const cards = document.querySelectorAll('.data-card'); const metrics = document.querySelectorAll('.metric-card'); // đĨ TRIGGER MULTIPLE REFLOWS (Very Expensive) cards.forEach((card, index) => { // Multiple synchronous layout-triggering operations card.style.width = (300 + Math.random() * 50) + 'px'; // đĨ Reflow showReflow(); card.style.height = (200 + Math.random() * 30) + 'px'; // đĨ Reflow showReflow(); card.style.margin = (10 + Math.random() * 5) + 'px'; // đĨ Reflow showReflow(); card.style.padding = (20 + Math.random() * 10) + 'px'; // đĨ Reflow showReflow(); card.classList.add('expensive-operation'); setTimeout(() => card.classList.remove('expensive-operation'), 1000); }); // Force layout thrashing setTimeout(() => { metrics.forEach(metric => { metric.style.width = metric.offsetWidth + 10 + 'px'; // đĨ Reflow showReflow(); }); }, 100); } function triggerRepaintStorm() { const cards = document.querySelectorAll('.data-card'); // ⥠TRIGGER MULTIPLE REPAINTS (Moderately Expensive) cards.forEach((card, index) => { // Multiple paint-triggering operations card.style.background = `rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.3)`; // đ¨ Repaint showRepaint(); card.style.color = `hsl(${Math.random() * 360}, 100%, 70%)`; // đ¨ Repaint showRepaint(); card.style.border = `2px solid hsl(${Math.random() * 360}, 100%, 60%)`; // đ¨ Repaint showRepaint(); card.style.boxShadow = `0 0 20px rgba(${Math.random() * 255}, ${Math.random() * 255}, ${Math.random() * 255}, 0.5)`; // đ¨ Repaint showRepaint(); }); } // đ OPTIMIZED OPERATIONS function optimizedAnimation() { const cards = document.querySelectorAll('.data-card'); // â USE TRANSFORM (No Reflow, Cheap Repaint) cards.forEach((card, index) => { card.style.transform = 'translateY(0) scale(1)'; card.style.transition = 'transform 0.5s ease'; setTimeout(() => { card.style.transform = 'translateY(-10px) scale(1.02)'; // â No reflow! card.classList.add('cheap-operation'); }, index * 100); }); // â BATCH DOM OPERATIONS requestAnimationFrame(() => { const fragment = document.createDocumentFragment(); // Create particles efficiently for (let i = 0; i < 20; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.cssText = ` left: ${Math.random() * 100}vw; top: ${Math.random() * 100}vh; width: ${Math.random() * 10 + 5}px; height: ${Math.random() * 10 + 5}px; background: hsl(${Math.random() * 360}, 100%, 70%); animation: float ${Math.random() * 3 + 2}s ease-in-out infinite; `; fragment.appendChild(particle); } document.getElementById('particles').appendChild(fragment); }); } function resetMetrics() { reflowCount = 0; repaintCount = 0; reflowMetric.querySelector('.metric-value').textContent = '0'; repaintMetric.querySelector('.metric-value').textContent = '0'; // Reset styles const cards = document.querySelectorAll('.data-card'); cards.forEach(card => { card.style.cssText = ''; card.className = 'data-card'; }); document.getElementById('particles').innerHTML = ''; } // Initialize particles and FPS counter function createParticles() { const particlesContainer = document.getElementById('particles'); for (let i = 0; i < 15; i++) { const particle = document.createElement('div'); particle.className = 'particle'; particle.style.cssText = ` left: ${Math.random() * 100}vw; top: ${Math.random() * 100}vh; width: ${Math.random() * 8 + 2}px; height: ${Math.random() * 8 + 2}px; background: rgba(255,255,255,${Math.random() * 0.3}); animation: float ${Math.random() * 6 + 3}s ease-in-out infinite; `; particlesContainer.appendChild(particle); } // Add float animation const style = document.createElement('style'); style.textContent = ` @keyframes float { 0%, 100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-20px) rotate(180deg); } } `; document.head.appendChild(style); } // Start monitoring createParticles(); updateFPS(); </script> </body> </html>
đŧī¸ Smart Image Gallery
â What to Avoid
đĨMultiple synchronous layout changes
đReading layout properties after writes
đForced synchronous layouts
â Best Practices
đUse transform and opacity
đĻBatch DOM operations
đ¯Use requestAnimationFrame
Gallery Implementation
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>đŧī¸ Smart Gallery - Repaint & Reflow Optimization</title> <style> .gallery { max-width: 1200px; margin: 0 auto; padding: 2rem; font-family: 'Inter', system-ui, sans-serif; } .gallery-header { text-align: center; margin-bottom: 3rem; } .performance-badge { display: inline-block; background: #2ecc71; color: white; padding: 0.5rem 1rem; border-radius: 20px; font-size: 0.9rem; font-weight: 600; margin: 0.5rem; } .performance-badge.slow { background: #e74c3c; } .controls { display: flex; gap: 1rem; justify-content: center; margin: 2rem 0; flex-wrap: wrap; } .btn { padding: 1rem 2rem; border: none; border-radius: 10px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; } .btn-slow { background: #e74c3c; color: white; } .btn-fast { background: #2ecc71; color: white; } .gallery-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1.5rem; margin: 2rem 0; } .gallery-item { background: #f8f9fa; border-radius: 15px; overflow: hidden; transition: all 0.3s ease; border: 3px solid transparent; } .gallery-item.reflowing { border-color: #e74c3c; animation: reflowPulse 0.5s ease; } .gallery-item.repainting { border-color: #f39c12; animation: repaintPulse 0.5s ease; } .gallery-item.optimized { border-color: #2ecc71; } .item-image { width: 100%; height: 200px; background: linear-gradient(45deg, #667eea, #764ba2); display: flex; align-items: center; justify-content: center; font-size: 3rem; color: white; } .item-content { padding: 1.5rem; } .item-title { font-size: 1.2rem; font-weight: 600; margin-bottom: 0.5rem; } .item-description { color: #666; line-height: 1.5; } .stats-panel { background: #34495e; color: white; padding: 1.5rem; border-radius: 15px; margin: 2rem 0; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; text-align: center; } .stat-value { font-size: 2rem; font-weight: bold; margin-bottom: 0.5rem; } .stat-label { font-size: 0.9rem; opacity: 0.8; } @keyframes reflowPulse { 0% { box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(231, 76, 60, 0); } 100% { box-shadow: 0 0 0 0 rgba(231, 76, 60, 0); } } @keyframes repaintPulse { 0% { box-shadow: 0 0 0 0 rgba(243, 156, 18, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(243, 156, 18, 0); } 100% { box-shadow: 0 0 0 0 rgba(243, 156, 18, 0); } } .comparison { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin: 3rem 0; } .comparison-card { background: white; padding: 2rem; border-radius: 15px; box-shadow: 0 5px 15px rgba(0,0,0,0.1); } .code-example { background: #2c3e50; color: #ecf0f1; padding: 1rem; border-radius: 8px; font-family: 'Fira Code', monospace; font-size: 0.9rem; margin: 1rem 0; } .bad { border-left: 4px solid #e74c3c; } .good { border-left: 4px solid #2ecc71; } </style> </head> <body> <div class="gallery"> <header class="gallery-header"> <h1>đŧī¸ Smart Image Gallery</h1> <p>See the difference between expensive and optimized operations</p> <div> <span class="performance-badge slow">đĨ Reflow Triggered</span> <span class="performance-badge">đ¨ Repaint Triggered</span> <span class="performance-badge">đ Optimized</span> </div> </header> <div class="controls"> <button class="btn btn-slow" onclick="triggerExpensiveOperations()"> đĨ Trigger Expensive Operations </button> <button class="btn btn-fast" onclick="triggerOptimizedOperations()"> đ Trigger Optimized Operations </button> <button class="btn" onclick="resetGallery()"> đ Reset Gallery </button> </div> <div class="stats-panel"> <div class="stats-grid"> <div class="stat"> <div class="stat-value" id="reflowCount">0</div> <div class="stat-label">Reflows</div> </div> <div class="stat"> <div class="stat-value" id="repaintCount">0</div> <div class="stat-label">Repaints</div> </div> <div class="stat"> <div class="stat-value" id="operationTime">0ms</div> <div class="stat-label">Operation Time</div> </div> <div class="stat"> <div class="stat-value" id="fps">60</div> <div class="stat-label">FPS</div> </div> </div> </div> <div class="gallery-grid" id="galleryGrid"> <!-- Gallery items will be generated by JavaScript --> </div> <div class="comparison"> <div class="comparison-card"> <h3>â Expensive Operations</h3> <p>These trigger reflows and hurt performance:</p> <div class="code-example bad"> // đĨ Multiple reflows<br> element.style.width = '200px';<br> element.style.height = '150px';<br> element.style.margin = '10px';<br> element.style.padding = '20px'; </div> <div class="code-example bad"> // đĨ Reading layout properties<br> const width = element.offsetWidth;<br> element.style.width = width + 10 + 'px'; </div> </div> <div class="comparison-card"> <h3>â Optimized Operations</h3> <p>These are cheap and performant:</p> <div class="code-example good"> // đ No reflow<br> element.style.transform = 'scale(1.1)';<br> element.style.opacity = '0.8'; </div> <div class="code-example good"> // đ Batch DOM operations<br> const fragment = document.createDocumentFragment();<br> // Add elements to fragment<br> container.appendChild(fragment); </div> </div> </div> </div> <script> let reflowCount = 0; let repaintCount = 0; let lastFrameTime = performance.now(); let frameCount = 0; // Generate gallery items function generateGalleryItems() { const grid = document.getElementById('galleryGrid'); const items = []; for (let i = 1; i <= 12; i++) { items.push(` <div class="gallery-item" id="item-${i}"> <div class="item-image"> ${getRandomEmoji()} </div> <div class="item-content"> <div class="item-title">Artwork #${i}</div> <div class="item-description"> Beautiful digital creation with amazing colors and composition. </div> </div> </div> `); } grid.innerHTML = items.join(''); } function getRandomEmoji() { const emojis = ['đ¨', 'đŧī¸', 'đ', 'â¨', 'đĨ', 'đĢ', 'đĒ', 'đĻ', 'đē', 'đ']; return emojis[Math.floor(Math.random() * emojis.length)]; } function updateStats() { document.getElementById('reflowCount').textContent = reflowCount; document.getElementById('repaintCount').textContent = repaintCount; frameCount++; const currentTime = performance.now(); if (currentTime >= lastFrameTime + 1000) { const fps = Math.round((frameCount * 1000) / (currentTime - lastFrameTime)); document.getElementById('fps').textContent = fps; frameCount = 0; lastFrameTime = currentTime; } requestAnimationFrame(updateStats); } // đ¨ EXPENSIVE OPERATIONS function triggerExpensiveOperations() { const startTime = performance.now(); const items = document.querySelectorAll('.gallery-item'); items.forEach((item, index) => { // đĨ TRIGGER MULTIPLE REFLOWS setTimeout(() => { item.classList.add('reflowing'); // Multiple layout-triggering changes item.style.width = (250 + Math.random() * 50) + 'px'; // Reflow reflowCount++; item.style.height = (300 + Math.random() * 40) + 'px'; // Reflow reflowCount++; item.style.margin = (10 + Math.random() * 5) + 'px'; // Reflow reflowCount++; item.offsetHeight; // Force reflow reflowCount++; // đ¨ TRIGGER REPAINTS item.style.background = `hsl(${Math.random() * 360}, 70%, 95%)`; // Repaint repaintCount++; item.style.borderRadius = (15 + Math.random() * 10) + 'px'; // Repaint repaintCount++; setTimeout(() => item.classList.remove('reflowing'), 1000); }, index * 50); }); const endTime = performance.now(); document.getElementById('operationTime').textContent = Math.round(endTime - startTime) + 'ms'; } // đ OPTIMIZED OPERATIONS function triggerOptimizedOperations() { const startTime = performance.now(); const items = document.querySelectorAll('.gallery-item'); // â USE TRANSFORM (No reflow) items.forEach((item, index) => { setTimeout(() => { item.classList.add('optimized'); item.style.transform = 'translateY(-10px) scale(1.05)'; // No reflow! item.style.transition = 'all 0.3s ease'; item.style.opacity = '0.9'; // Cheap repaint repaintCount++; }, index * 30); }); // â BATCH DOM READ/WRITE requestAnimationFrame(() => { // Batch all reads const measurements = Array.from(items).map(item => ({ element: item, width: item.offsetWidth })); // Batch all writes requestAnimationFrame(() => { measurements.forEach(({ element, width }) => { element.style.width = (width + 20) + 'px'; // Single reflow reflowCount++; }); }); }); const endTime = performance.now(); document.getElementById('operationTime').textContent = Math.round(endTime - startTime) + 'ms'; } function resetGallery() { const items = document.querySelectorAll('.gallery-item'); items.forEach(item => { item.style.cssText = ''; item.className = 'gallery-item'; }); reflowCount = 0; repaintCount = 0; document.getElementById('operationTime').textContent = '0ms'; } // Initialize generateGalleryItems(); updateStats(); </script> </body> </html>
đŦ Animation Performance Studio
Side-by-Side Comparison
See the dramatic difference between expensive animations (using top/left) and optimized animations (using transform) running simultaneously.
// Left: top/left = Reflows every frame
// Right: transform = GPU accelerated
Animation Studio Implementation
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>đŦ Animation Studio - Performance Comparison</title> <style> .studio { background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d); min-height: 100vh; padding: 2rem; color: white; font-family: 'Inter', system-ui, sans-serif; } .studio-header { text-align: center; margin-bottom: 3rem; } .animation-stage { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin: 2rem 0; } .stage { background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); border-radius: 20px; padding: 2rem; min-height: 400px; position: relative; overflow: hidden; } .stage-title { text-align: center; margin-bottom: 2rem; font-size: 1.5rem; font-weight: 600; } .animated-element { width: 80px; height: 80px; border-radius: 15px; position: absolute; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; font-weight: bold; } .expensive-element { background: linear-gradient(45deg, #e74c3c, #c0392b); left: 50%; transform: translateX(-50%); } .optimized-element { background: linear-gradient(45deg, #2ecc71, #27ae60); left: 50%; transform: translateX(-50%); } .controls { display: flex; gap: 1rem; justify-content: center; margin: 2rem 0; flex-wrap: wrap; } .btn { padding: 1rem 2rem; border: none; border-radius: 10px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; } .btn-primary { background: #3498db; color: white; } .performance-metrics { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; margin: 2rem 0; } .metric-panel { background: rgba(255,255,255,0.1); padding: 1.5rem; border-radius: 15px; text-align: center; } .metric-value { font-size: 2rem; font-weight: bold; margin-bottom: 0.5rem; } .metric-label { font-size: 0.9rem; opacity: 0.8; } .warning { color: #e74c3c; font-weight: bold; } .success { color: #2ecc71; font-weight: bold; } .code-comparison { background: rgba(0,0,0,0.3); padding: 2rem; border-radius: 15px; margin: 2rem 0; } .code-block { background: #2c3e50; color: #ecf0f1; padding: 1rem; border-radius: 8px; font-family: 'Fira Code', monospace; font-size: 0.9rem; margin: 1rem 0; } .bad-code { border-left: 4px solid #e74c3c; } .good-code { border-left: 4px solid #2ecc71; } @keyframes expensiveMove { 0% { top: 50px; left: 50px; } 25% { top: 50px; left: 250px; } 50% { top: 250px; left: 250px; } 75% { top: 250px; left: 50px; } 100% { top: 50px; left: 50px; } } @keyframes optimizedMove { 0% { transform: translate(50px, 50px); } 25% { transform: translate(250px, 50px); } 50% { transform: translate(250px, 250px); } 75% { transform: translate(50px, 250px); } 100% { transform: translate(50px, 50px); } } </style> </head> <body> <div class="studio"> <header class="studio-header"> <h1>đŦ Animation Performance Studio</h1> <p>Compare expensive vs optimized animations in real-time</p> </header> <div class="performance-metrics"> <div class="metric-panel"> <div class="metric-value warning" id="expensiveFPS">60</div> <div class="metric-label">Expensive Animation FPS</div> </div> <div class="metric-panel"> <div class="metric-value success" id="optimizedFPS">60</div> <div class="metric-label">Optimized Animation FPS</div> </div> </div> <div class="controls"> <button class="btn btn-primary" onclick="startAnimations()"> đŦ Start Animations </button> <button class="btn" onclick="stopAnimations()"> âšī¸ Stop Animations </button> <button class="btn" onclick="resetAnimations()"> đ Reset </button> </div> <div class="animation-stage"> <div class="stage"> <div class="stage-title">â Expensive Animation</div> <div class="animated-element expensive-element" id="expensiveBox"> đĨ </div> <div class="performance-info" style="position: absolute; bottom: 1rem; left: 1rem;"> <div>Triggers: <strong>Reflows</strong></div> <div>Performance: <strong class="warning">Poor</strong></div> </div> </div> <div class="stage"> <div class="stage-title">â Optimized Animation</div> <div class="animated-element optimized-element" id="optimizedBox"> đ </div> <div class="performance-info" style="position: absolute; bottom: 1rem; left: 1rem;"> <div>Triggers: <strong>Compositing</strong></div> <div>Performance: <strong class="success">Excellent</strong></div> </div> </div> </div> <div class="code-comparison"> <h3>đ What's Happening Behind the Scenes</h3> <div class="code-block bad-code"> // đĨ EXPENSIVE: Triggers reflows<br> function animateExpensive() {<br> const box = document.getElementById('expensiveBox');<br> box.style.left = newLeft + 'px'; // Reflow!<br> box.style.top = newTop + 'px'; // Reflow!<br> box.offsetHeight; // Force reflow<br> } </div> <div class="code-block good-code"> // đ OPTIMIZED: Uses compositor<br> function animateOptimized() {<br> const box = document.getElementById('optimizedBox');<br> box.style.transform = `translate(${newX}px, ${newY}px)`;<br> // No reflow! GPU accelerated<br> } </div> </div> </div> <script> let animationRunning = false; let expensiveFrameCount = 0; let optimizedFrameCount = 0; let lastExpensiveTime = performance.now(); let lastOptimizedTime = performance.now(); let expensiveFPS = 60; let optimizedFPS = 60; const expensiveBox = document.getElementById('expensiveBox'); const optimizedBox = document.getElementById('optimizedBox'); function startAnimations() { if (animationRunning) return; animationRunning = true; // đ¨ Expensive animation using top/left function animateExpensive() { if (!animationRunning) return; const time = performance.now(); const progress = (time % 4000) / 4000; // đĨ EXPENSIVE: Using top/left (triggers reflow) const angle = progress * Math.PI * 2; const radius = 100; const x = 150 + Math.cos(angle) * radius; const y = 150 + Math.sin(angle) * radius; expensiveBox.style.left = x + 'px'; // Reflow! expensiveBox.style.top = y + 'px'; // Reflow! expensiveFrameCount++; const currentTime = performance.now(); if (currentTime >= lastExpensiveTime + 1000) { expensiveFPS = Math.round((expensiveFrameCount * 1000) / (currentTime - lastExpensiveTime)); document.getElementById('expensiveFPS').textContent = expensiveFPS; expensiveFrameCount = 0; lastExpensiveTime = currentTime; } requestAnimationFrame(animateExpensive); } // đ Optimized animation using transform function animateOptimized() { if (!animationRunning) return; const time = performance.now(); const progress = (time % 4000) / 4000; // â OPTIMIZED: Using transform (no reflow) const angle = progress * Math.PI * 2; const radius = 100; const x = Math.cos(angle) * radius; const y = Math.sin(angle) * radius; optimizedBox.style.transform = `translate(${x}px, ${y}px)`; // No reflow! optimizedFrameCount++; const currentTime = performance.now(); if (currentTime >= lastOptimizedTime + 1000) { optimizedFPS = Math.round((optimizedFrameCount * 1000) / (currentTime - lastOptimizedTime)); document.getElementById('optimizedFPS').textContent = optimizedFPS; optimizedFrameCount = 0; lastOptimizedTime = currentTime; } requestAnimationFrame(animateOptimized); } animateExpensive(); animateOptimized(); } function stopAnimations() { animationRunning = false; } function resetAnimations() { animationRunning = false; // Reset positions expensiveBox.style.left = '50%'; expensiveBox.style.top = '50%'; expensiveBox.style.transform = 'translate(-50%, -50%)'; optimizedBox.style.transform = 'translate(-50%, -50%)'; // Reset FPS counters expensiveFPS = 60; optimizedFPS = 60; document.getElementById('expensiveFPS').textContent = '60'; document.getElementById('optimizedFPS').textContent = '60'; } // Initialize resetAnimations(); </script> </body> </html>
đ Performance Optimization Guide
â Cheap Operations
- transform - GPU accelerated, no reflow
- opacity - Cheap repaint, often hardware accelerated
- filter - Modern browsers optimize well
- will-change - Hint for browser optimization
- backface-visibility - Triggers compositing
â Expensive Operations
- width/height - Triggers reflow
- margin/padding - Affects surrounding elements
- top/left - Forces layout recalc
- font-size - Can affect entire layout
- offsetWidth/Height - Forces synchronous layout
đĄ Pro Optimization Strategies
Code Patterns:
- Batch DOM reads and writes
- Use transform instead of top/left
- Avoid layout thrashing
- Use requestAnimationFrame for animations
Tools & Monitoring:
- Chrome DevTools Performance panel
- Layout Shift visualization
- Paint flashing tool
- Frame rate monitoring
Ready to Master Performance? đ
Experiment with repaints and reflows in real-time. See the dramatic performance difference between optimized and unoptimized code, and learn how to create buttery-smooth web experiences.