# Performance Optimization Checklist - HTML Chess Game ## Overview This checklist provides a comprehensive guide for implementing performance optimizations throughout the development lifecycle. Use this as a companion to the [Performance Budget](/docs/analysis/performance-budget.md) document. --- ## Pre-Implementation Optimizations ### ✅ Architecture Planning - [ ] **Choose optimal data structures** - [ ] Use typed arrays for board representation (faster than objects) - [ ] Implement bitboards for advanced features (optional) - [ ] Plan for immutable game state (easier undo/redo) - [ ] Design for minimal object creation in hot paths - [ ] **Plan component interfaces** - [ ] Define clear boundaries between modules - [ ] Minimize cross-module dependencies - [ ] Design for lazy loading (AI module separate) - [ ] Plan event system to reduce coupling - [ ] **Optimize build pipeline** - [ ] Set up code splitting (core vs AI vs sounds) - [ ] Configure tree shaking (ES6 modules) - [ ] Enable minification and compression - [ ] Set up source maps for debugging ### ✅ Development Environment - [ ] **Performance tooling setup** - [ ] Install Chrome DevTools extensions - [ ] Set up Lighthouse CI - [ ] Configure bundle size analyzer - [ ] Add performance test suite - [ ] Set up memory profiling - [ ] **Budgets configuration** - [ ] Add webpack-bundle-analyzer - [ ] Configure size-limit package - [ ] Set up performance budgets in webpack - [ ] Create pre-commit hooks for budget checks ### ✅ Code Quality Standards - [ ] **Establish coding patterns** - [ ] Use const/let (no var) for better optimization - [ ] Prefer pure functions for testability - [ ] Avoid premature abstraction - [ ] Document performance-critical sections - [ ] Use consistent naming conventions - [ ] **Performance-aware coding** - [ ] Avoid nested loops where possible - [ ] Use early returns to reduce nesting - [ ] Cache expensive calculations - [ ] Minimize DOM access in loops - [ ] Prefer CSS classes over inline styles --- ## During-Implementation Best Practices ### ⚡ JavaScript Performance #### 1. General Optimizations - [ ] **Variable usage** - [ ] Use local variables over object properties - [ ] Cache array/string lengths in loops - [ ] Minimize global variable access - [ ] Use destructuring sparingly (creates overhead) ```javascript // ❌ Bad - Property access in loop for (let i = 0; i < pieces.length; i++) { if (pieces[i].color === this.currentColor) { ... } } // ✅ Good - Cached values const pieceCount = pieces.length; const currentColor = this.currentColor; for (let i = 0; i < pieceCount; i++) { if (pieces[i].color === currentColor) { ... } } ``` - [ ] **Function calls** - [ ] Minimize function call overhead in loops - [ ] Inline trivial functions (< 3 lines) - [ ] Use arrow functions for callbacks - [ ] Avoid creating functions inside loops ```javascript // ❌ Bad - Function creation in loop moves.forEach(function(move) { validateMove(move); }); // ✅ Good - Predefined function moves.forEach(validateMove); ``` - [ ] **Object operations** - [ ] Use Object.create(null) for dictionaries - [ ] Prefer Map for key-value pairs with many operations - [ ] Use WeakMap for memory-sensitive caches - [ ] Avoid delete operator (use null instead) ```javascript // ❌ Bad - delete causes hidden class change delete obj.property; // ✅ Good - null maintains hidden class obj.property = null; ``` #### 2. Array Operations - [ ] **Efficient array methods** - [ ] Use for loops for performance-critical paths - [ ] Prefer forEach/map/filter for readability (non-critical) - [ ] Use Array.from() sparingly (creates new array) - [ ] Preallocate arrays when size is known ```javascript // ❌ Bad - Dynamic growth const moves = []; for (let i = 0; i < 64; i++) { moves.push(generateMove(i)); } // ✅ Good - Preallocated const moves = new Array(64); for (let i = 0; i < 64; i++) { moves[i] = generateMove(i); } ``` - [ ] **Array searching** - [ ] Use indexOf/includes for small arrays - [ ] Use Set for large arrays (O(1) lookup) - [ ] Break early from loops when found - [ ] Consider binary search for sorted arrays #### 3. String Operations - [ ] **String concatenation** - [ ] Use template literals for readability - [ ] Use array.join() for multiple concatenations - [ ] Avoid + in loops - [ ] Use String.prototype methods efficiently ```javascript // ❌ Bad - Multiple concatenations let notation = ''; notation += piece; notation += fromSquare; notation += toSquare; // ✅ Good - Template literal const notation = `${piece}${fromSquare}${toSquare}`; ``` ### ⚡ AI Performance #### 4. Search Algorithm Optimizations - [ ] **Alpha-Beta Pruning** (CRITICAL - 10-100x improvement) - [ ] Implement fail-soft alpha-beta - [ ] Use proper window management - [ ] Track pruning statistics - [ ] Test with various positions ```javascript // ✅ Alpha-Beta Implementation function alphaBeta(depth, alpha, beta, maximizing, gameState) { if (depth === 0 || gameState.isTerminal()) { return evaluate(gameState); } const moves = generateMoves(gameState); if (maximizing) { let maxEval = -Infinity; for (const move of moves) { const newState = makeMove(gameState, move); const eval = alphaBeta(depth - 1, alpha, beta, false, newState); maxEval = Math.max(maxEval, eval); alpha = Math.max(alpha, eval); if (beta <= alpha) break; // Beta cutoff } return maxEval; } else { // Similar for minimizing } } ``` - [ ] **Move Ordering** (HIGH - 2-3x improvement on top of alpha-beta) - [ ] Evaluate captures first (MVV/LVA) - [ ] Try check-giving moves early - [ ] Use killer move heuristic - [ ] Use hash move from transposition table ```javascript // ✅ Move Ordering function orderMoves(moves, gameState) { return moves.sort((a, b) => { // 1. Hash move first if (a === hashMove) return -1; if (b === hashMove) return 1; // 2. Captures (MVV/LVA - Most Valuable Victim, Least Valuable Attacker) const aScore = getCaptureScore(a); const bScore = getCaptureScore(b); if (aScore !== bScore) return bScore - aScore; // 3. Killer moves if (isKillerMove(a)) return -1; if (isKillerMove(b)) return 1; // 4. History heuristic return getHistoryScore(b) - getHistoryScore(a); }); } ``` - [ ] **Transposition Table** (HIGH - 1.5-2x improvement) - [ ] Use Zobrist hashing for positions - [ ] Implement replacement strategy (depth-preferred) - [ ] Store bounds (exact, lower, upper) - [ ] Size table based on device memory ```javascript // ✅ Transposition Table class TranspositionTable { constructor(maxSize = 10_000_000) { this.table = new Map(); this.maxSize = maxSize; } store(hash, depth, score, flag, bestMove) { // Replacement strategy: prefer deeper searches const existing = this.table.get(hash); if (!existing || depth >= existing.depth) { this.table.set(hash, { depth, score, flag, bestMove }); } // Evict oldest entries if too large if (this.table.size > this.maxSize) { const firstKey = this.table.keys().next().value; this.table.delete(firstKey); } } probe(hash, depth, alpha, beta) { const entry = this.table.get(hash); if (!entry || entry.depth < depth) return null; // Check if we can use this score if (entry.flag === EXACT) return entry.score; if (entry.flag === LOWER && entry.score >= beta) return entry.score; if (entry.flag === UPPER && entry.score <= alpha) return entry.score; return null; } } ``` - [ ] **Iterative Deepening** (MEDIUM - Better UX) - [ ] Start at depth 1, increment each iteration - [ ] Use previous iteration for move ordering - [ ] Support time-based termination - [ ] Return best move from completed depth ```javascript // ✅ Iterative Deepening function iterativeDeepening(gameState, maxTime = 2000) { const startTime = performance.now(); let bestMove = null; let depth = 1; while (performance.now() - startTime < maxTime) { try { const result = alphaBeta(depth, -Infinity, Infinity, true, gameState); bestMove = result.move; depth++; } catch (e) { if (e instanceof TimeoutError) break; throw e; } } return bestMove; } ``` #### 5. Evaluation Function Optimizations - [ ] **Incremental Updates** (HIGH - 5x improvement) - [ ] Track material score incrementally - [ ] Update positional scores on move/unmove - [ ] Only recalculate complex metrics when needed - [ ] Cache king safety calculations ```javascript // ✅ Incremental Evaluation class IncrementalEvaluator { constructor() { this.materialScore = 0; this.positionalScore = 0; } makeMove(move) { // Update material if (move.captured) { this.materialScore -= PIECE_VALUES[move.captured]; } // Update positional (only changed squares) this.positionalScore -= PIECE_SQUARE_TABLES[move.piece][move.from]; this.positionalScore += PIECE_SQUARE_TABLES[move.piece][move.to]; } unmakeMove(move) { // Reverse updates if (move.captured) { this.materialScore += PIECE_VALUES[move.captured]; } this.positionalScore += PIECE_SQUARE_TABLES[move.piece][move.from]; this.positionalScore -= PIECE_SQUARE_TABLES[move.piece][move.to]; } getScore() { return this.materialScore + this.positionalScore; } } ``` - [ ] **Piece-Square Tables** (MEDIUM - 2x improvement) - [ ] Precompute all positional values - [ ] Use O(1) array lookups - [ ] Separate tables for opening/middlegame/endgame - [ ] Mirror tables for black pieces ```javascript // ✅ Piece-Square Tables const PAWN_TABLE = [ [ 0, 0, 0, 0, 0, 0, 0, 0], [ 50, 50, 50, 50, 50, 50, 50, 50], [ 10, 10, 20, 30, 30, 20, 10, 10], [ 5, 5, 10, 25, 25, 10, 5, 5], [ 0, 0, 0, 20, 20, 0, 0, 0], [ 5, -5,-10, 0, 0,-10, -5, 5], [ 5, 10, 10,-20,-20, 10, 10, 5], [ 0, 0, 0, 0, 0, 0, 0, 0] ]; function getPieceSquareScore(piece, square, color) { const [rank, file] = squareToCoords(square); const tableRank = color === 'white' ? rank : 7 - rank; return PAWN_TABLE[tableRank][file]; } ``` #### 6. Memory Optimizations - [ ] **Object Pooling** (MEDIUM - 20-30% less GC) - [ ] Pool move objects - [ ] Pool position objects - [ ] Reuse evaluation contexts - [ ] Monitor pool size ```javascript // ✅ Object Pool class ObjectPool { constructor(factory, initialSize = 100) { this.factory = factory; this.pool = []; for (let i = 0; i < initialSize; i++) { this.pool.push(factory()); } } acquire() { return this.pool.length > 0 ? this.pool.pop() : this.factory(); } release(obj) { obj.reset(); // Clear object state this.pool.push(obj); } } // Usage const movePool = new ObjectPool(() => new Move(), 1000); const move = movePool.acquire(); // ... use move ... movePool.release(move); ``` - [ ] **Garbage Collection Minimization** - [ ] Avoid creating objects in hot loops - [ ] Reuse arrays instead of creating new ones - [ ] Use primitive values where possible - [ ] Clear references when done ### ⚡ DOM Performance #### 7. Rendering Optimizations - [ ] **Virtual DOM / Diffing** (CRITICAL - 5-10x improvement) - [ ] Track previous board state - [ ] Only update changed squares - [ ] Batch DOM updates - [ ] Use DocumentFragment for batch inserts ```javascript // ✅ DOM Diffing function updateBoard(oldState, newState) { const changedSquares = []; for (let i = 0; i < 64; i++) { if (oldState[i] !== newState[i]) { changedSquares.push(i); } } // Only update changed squares requestAnimationFrame(() => { changedSquares.forEach(index => { updateSquare(index, newState[index]); }); }); } ``` - [ ] **CSS Transform Animations** (CRITICAL - GPU acceleration) - [ ] Use transform instead of top/left - [ ] Use translate3d for GPU acceleration - [ ] Avoid animating layout properties - [ ] Use will-change sparingly ```css /* ❌ Bad - Triggers layout */ .piece { transition: top 0.3s, left 0.3s; } /* ✅ Good - GPU accelerated */ .piece { transition: transform 0.3s; will-change: transform; } ``` ```javascript // ✅ Transform Animation function animatePiece(piece, fromSquare, toSquare) { const fromPos = getSquarePosition(fromSquare); const toPos = getSquarePosition(toSquare); const deltaX = toPos.x - fromPos.x; const deltaY = toPos.y - fromPos.y; piece.style.transform = `translate3d(${deltaX}px, ${deltaY}px, 0)`; } ``` - [ ] **CSS Classes over Inline Styles** (MEDIUM - 2x improvement) - [ ] Define CSS classes for all states - [ ] Use classList API for toggling - [ ] Avoid style.property = value - [ ] Batch class changes ```javascript // ❌ Bad - Inline styles square.style.backgroundColor = '#f0d9b5'; square.style.boxShadow = '0 0 10px rgba(0,0,0,0.3)'; // ✅ Good - CSS classes square.classList.add('highlighted'); ``` - [ ] **RequestAnimationFrame** (MEDIUM - Smooth animations) - [ ] Use RAF for all animations - [ ] Batch reads and writes - [ ] Avoid layout thrashing - [ ] Cancel RAF on cleanup ```javascript // ✅ RequestAnimationFrame function smoothUpdate() { // Read phase (all DOM reads together) const positions = pieces.map(p => p.getBoundingClientRect()); requestAnimationFrame(() => { // Write phase (all DOM writes together) positions.forEach((pos, i) => { pieces[i].style.transform = `translate3d(${pos.x}px, ${pos.y}px, 0)`; }); }); } ``` #### 8. Event Handling - [ ] **Event Delegation** (MEDIUM - Reduces listeners) - [ ] Use single listener on board container - [ ] Identify target square from event.target - [ ] Avoid listeners on every square - [ ] Clean up listeners on destroy ```javascript // ❌ Bad - 64 event listeners squares.forEach(square => { square.addEventListener('click', handleSquareClick); }); // ✅ Good - 1 event listener board.addEventListener('click', (event) => { const square = event.target.closest('.square'); if (square) handleSquareClick(square); }); ``` - [ ] **Debouncing and Throttling** - [ ] Debounce window resize handlers - [ ] Throttle scroll handlers - [ ] Use passive listeners for scroll/touch - [ ] Remove listeners when not needed ```javascript // ✅ Passive Listeners board.addEventListener('touchstart', handleTouch, { passive: true }); // ✅ Throttle function throttle(func, delay) { let lastCall = 0; return function(...args) { const now = Date.now(); if (now - lastCall >= delay) { lastCall = now; func.apply(this, args); } }; } window.addEventListener('resize', throttle(handleResize, 200)); ``` ### ⚡ Asset Optimization #### 9. Image Optimization - [ ] **SVG Sprites** (MEDIUM - 50% size reduction) - [ ] Combine all pieces into single SVG - [ ] Use tags to reference pieces - [ ] Optimize SVG with SVGO - [ ] Inline critical SVG in HTML ```html ``` - [ ] **Lazy Loading** (MEDIUM - Faster initial load) - [ ] Load sounds on first interaction - [ ] Load AI module on game start - [ ] Use loading="lazy" for images - [ ] Prefetch critical assets ```javascript // ✅ Lazy Load AI let aiModule = null; async function loadAI() { if (!aiModule) { aiModule = await import('./ai-engine.js'); } return aiModule; } ``` #### 10. Code Splitting - [ ] **Module Splitting** (HIGH - 2x faster initial load) - [ ] Separate core UI from AI - [ ] Split by route (if multi-page) - [ ] Use dynamic imports - [ ] Analyze bundle with webpack-bundle-analyzer ```javascript // ✅ Code Splitting // main.js - Core UI only (35KB) import { ChessBoard } from './core/ChessBoard.js'; import { GameController } from './core/GameController.js'; // Lazy load AI when needed async function startAIGame() { const { AIPlayer } = await import('./ai/AIPlayer.js'); // +28KB return new AIPlayer(); } ``` - [ ] **Tree Shaking** (MEDIUM - 20-30% reduction) - [ ] Use ES6 modules (not CommonJS) - [ ] Mark side-effect-free packages - [ ] Avoid default exports for better shaking - [ ] Import only what you need ```javascript // ❌ Bad - Imports everything import _ from 'lodash'; // ✅ Good - Imports only needed functions import { debounce, throttle } from 'lodash-es'; ``` ### ⚡ Web Workers (CRITICAL) #### 11. Background AI Computation - [ ] **Web Worker Setup** - [ ] Move AI calculation to worker - [ ] Use structured clone for messages - [ ] Handle worker errors gracefully - [ ] Terminate worker when done ```javascript // ✅ Web Worker for AI // main.js const aiWorker = new Worker('ai-worker.js'); function calculateAIMove(gameState) { return new Promise((resolve, reject) => { aiWorker.onmessage = (e) => resolve(e.data.move); aiWorker.onerror = reject; aiWorker.postMessage({ type: 'calculate', gameState }); }); } // ai-worker.js self.onmessage = function(e) { if (e.data.type === 'calculate') { const move = calculateBestMove(e.data.gameState); self.postMessage({ move }); } }; ``` - [ ] **Message Optimization** - [ ] Minimize message size - [ ] Use Transferable objects for large data - [ ] Batch messages when possible - [ ] Use SharedArrayBuffer for shared state (advanced) --- ## Post-Implementation Optimization ### 🔍 Performance Testing - [ ] **Automated Performance Tests** - [ ] Add performance test suite - [ ] Test AI calculation time - [ ] Test rendering frame rate - [ ] Test memory usage - [ ] Test bundle size ```javascript // ✅ Performance Test Example describe('Performance', () => { it('should render in <16ms', () => { const duration = measurePerformance(() => renderBoard()); expect(duration).toBeLessThan(16); }); it('should calculate AI move in <1s', () => { const duration = measurePerformance(() => ai.calculateMove(position)); expect(duration).toBeLessThan(1000); }); }); ``` - [ ] **Lighthouse Audits** - [ ] Run Lighthouse on every build - [ ] Target score > 90 - [ ] Fix all critical issues - [ ] Document score in README - [ ] **Real Device Testing** - [ ] Test on 3+ different desktop browsers - [ ] Test on 3+ different mobile devices - [ ] Test on slow 3G connection - [ ] Test with throttled CPU (6x slowdown) ### 🔍 Profiling - [ ] **Chrome DevTools Profiling** - [ ] Record CPU profile during AI calculation - [ ] Record performance timeline during gameplay - [ ] Take heap snapshots to find leaks - [ ] Analyze network waterfall - [ ] Check for memory leaks with allocation timeline - [ ] **Performance API** - [ ] Add performance.mark() for key operations - [ ] Measure critical paths - [ ] Log performance metrics - [ ] Set up Real User Monitoring (future) ```javascript // ✅ Performance Measurement performance.mark('ai-start'); const move = calculateBestMove(position); performance.mark('ai-end'); performance.measure('ai-calculation', 'ai-start', 'ai-end'); const measures = performance.getEntriesByName('ai-calculation'); console.log(`AI calculation took ${measures[0].duration}ms`); ``` ### 🔍 Optimization Opportunities - [ ] **Bottleneck Analysis** - [ ] Identify slowest operations - [ ] Profile with Chrome DevTools - [ ] Measure before/after optimization - [ ] Document improvements - [ ] **Low-Hanging Fruit** - [ ] Cache expensive calculations - [ ] Reduce unnecessary re-renders - [ ] Minimize network requests - [ ] Compress assets - [ ] Enable gzip/brotli compression ### 🔍 Long-Term Monitoring - [ ] **Regression Prevention** - [ ] Add performance budgets to CI - [ ] Fail builds that exceed budgets - [ ] Track performance over time - [ ] Create performance dashboard - [ ] **Continuous Improvement** - [ ] Review performance quarterly - [ ] Update budgets as needed - [ ] Adopt new browser features - [ ] Monitor web performance best practices --- ## Platform-Specific Optimizations ### 📱 Mobile Optimizations - [ ] **Touch Interactions** - [ ] Use touch events (touchstart, touchmove, touchend) - [ ] Add passive: true to touch listeners - [ ] Implement touch-action CSS - [ ] Provide larger touch targets (44x44px minimum) - [ ] **Responsive Performance** - [ ] Use CSS media queries for layout - [ ] Reduce AI depth on mobile - [ ] Disable animations on low-end devices - [ ] Use smaller transposition table ```javascript // ✅ Device-Specific Configuration function getDeviceConfig() { const cores = navigator.hardwareConcurrency || 2; const memory = navigator.deviceMemory || 2; if (cores >= 8 && memory >= 6) { return { depth: 6, tableSize: 50_000_000, animations: true }; } else if (cores >= 4 && memory >= 2) { return { depth: 5, tableSize: 10_000_000, animations: true }; } else { return { depth: 4, tableSize: 3_000_000, animations: false }; } } ``` ### 🖥️ Desktop Optimizations - [ ] **Keyboard Shortcuts** - [ ] Implement keyboard navigation - [ ] Add undo/redo shortcuts (Ctrl+Z, Ctrl+Y) - [ ] Support arrow keys for piece selection - [ ] Add accessibility shortcuts - [ ] **Advanced Features** - [ ] Enable deeper AI search - [ ] Use larger transposition tables - [ ] Add advanced animations - [ ] Support multiple game modes --- ## Final Performance Checklist ### Before Release - [ ] **Performance Metrics** - [ ] Lighthouse score > 90 - [ ] Bundle size < 150KB gzipped - [ ] FCP < 500ms - [ ] TTI < 1s - [ ] 60fps rendering - [ ] AI response < 1s (depth 5) - [ ] **Cross-Browser Testing** - [ ] Chrome (latest 2 versions) - [ ] Firefox (latest 2 versions) - [ ] Safari (latest 2 versions) - [ ] Edge (latest 2 versions) - [ ] **Device Testing** - [ ] Desktop (1920x1080, 1366x768) - [ ] Tablet (iPad, Android tablet) - [ ] Mobile (iPhone, Android phone) - [ ] Low-end device (throttled) - [ ] **Network Testing** - [ ] 5G / Fast connection - [ ] 4G / Regular connection - [ ] 3G / Slow connection - [ ] Offline mode (service worker) --- ## Performance Optimization Priority ### Critical (Do First) 1. Alpha-Beta Pruning 2. Web Workers 3. DOM Diffing 4. CSS Transforms 5. Code Splitting ### High (Do Second) 6. Move Ordering 7. Transposition Tables 8. Bundle Optimization 9. Mobile Optimization ### Medium (Do Third) 10. Object Pooling 11. Iterative Deepening 12. SVG Optimization --- ## Resources - [Web.dev Performance](https://web.dev/performance/) - [Chrome DevTools Performance](https://developer.chrome.com/docs/devtools/performance/) - [MDN Performance](https://developer.mozilla.org/en-US/docs/Web/Performance) - [Chess Programming Wiki](https://www.chessprogramming.org/) - [Webpack Bundle Analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) --- **Remember**: "Premature optimization is the root of all evil, but planning for performance is wisdom." **Document Version**: 1.0.0 **Last Updated**: 2025-11-22 **Owner**: Performance Optimizer Agent