Restructured project from nested workspace pattern to flat single-repo layout. This eliminates redundant nesting and consolidates all project files under version control. ## Migration Summary **Before:** ``` alex/ (workspace, not versioned) ├── chess-game/ (git repo) │ ├── js/, css/, tests/ │ └── index.html └── docs/ (planning, not versioned) ``` **After:** ``` alex/ (git repo, everything versioned) ├── js/, css/, tests/ ├── index.html ├── docs/ (project documentation) ├── planning/ (historical planning docs) ├── .gitea/ (CI/CD) └── CLAUDE.md (configuration) ``` ## Changes Made ### Structure Consolidation - Moved all chess-game/ contents to root level - Removed redundant chess-game/ subdirectory - Flattened directory structure (eliminated one nesting level) ### Documentation Organization - Moved chess-game/docs/ → docs/ (project documentation) - Moved alex/docs/ → planning/ (historical planning documents) - Added CLAUDE.md (workspace configuration) - Added IMPLEMENTATION_PROMPT.md (original project prompt) ### Version Control Improvements - All project files now under version control - Planning documents preserved in planning/ folder - Merged .gitignore files (workspace + project) - Added .claude/ agent configurations ### File Updates - Updated .gitignore to include both workspace and project excludes - Moved README.md to root level - All import paths remain functional (relative paths unchanged) ## Benefits ✅ **Simpler Structure** - One level of nesting removed ✅ **Complete Versioning** - All documentation now in git ✅ **Standard Layout** - Matches open-source project conventions ✅ **Easier Navigation** - Direct access to all project files ✅ **CI/CD Compatible** - All workflows still functional ## Technical Validation - ✅ Node.js environment verified - ✅ Dependencies installed successfully - ✅ Dev server starts and responds - ✅ All core files present and accessible - ✅ Git repository functional ## Files Preserved **Implementation Files:** - js/ (3,517 lines of code) - css/ (4 stylesheets) - tests/ (87 test cases) - index.html - package.json **CI/CD Pipeline:** - .gitea/workflows/ci.yml - .gitea/workflows/release.yml **Documentation:** - docs/ (12+ documentation files) - planning/ (historical planning materials) - README.md **Configuration:** - jest.config.js, babel.config.cjs, playwright.config.js - .gitignore (merged) - CLAUDE.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
882 lines
23 KiB
Markdown
882 lines
23 KiB
Markdown
# 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 <use> tags to reference pieces
|
|
- [ ] Optimize SVG with SVGO
|
|
- [ ] Inline critical SVG in HTML
|
|
|
|
```html
|
|
<!-- ✅ SVG Sprite -->
|
|
<svg style="display: none;">
|
|
<symbol id="piece-king-white" viewBox="0 0 45 45">
|
|
<path d="..."/>
|
|
</symbol>
|
|
<!-- ... other pieces ... -->
|
|
</svg>
|
|
|
|
<!-- Usage -->
|
|
<svg class="piece"><use href="#piece-king-white"/></svg>
|
|
```
|
|
|
|
- [ ] **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
|