From 34fc0833051eac27e3a46ce66f6101176ccedf4d Mon Sep 17 00:00:00 2001 From: Anders Lehmann Pier Date: Fri, 13 Jun 2025 09:45:13 +0200 Subject: [PATCH] Add comprehensive performance optimization guide for Canvas and mobile development --- .../technical/performance-optimization.md | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 knowledge-base/technical/performance-optimization.md diff --git a/knowledge-base/technical/performance-optimization.md b/knowledge-base/technical/performance-optimization.md new file mode 100644 index 0000000..258b632 --- /dev/null +++ b/knowledge-base/technical/performance-optimization.md @@ -0,0 +1,224 @@ +# Performance Optimization Guide + +## Current Performance Targets + +### Frame Rate Goals +- **Desktop**: Consistent 60fps on mid-range hardware (2019+) +- **Mobile**: Minimum 30fps on devices from 2020+ +- **Tablet**: 45-60fps for smooth touch interaction +- **Degradation Strategy**: Reduce particle effects before dropping frame rate + +### Memory Usage Targets +- **Desktop**: <100MB during gameplay +- **Mobile**: <50MB during gameplay +- **Peak Usage**: <150MB during intensive scenes (level completion effects) + +### Loading Performance +- **Initial Load**: <3 seconds on 3G connection +- **Level Transition**: <500ms between levels +- **Asset Loading**: Progressive where possible + +## Canvas Optimization Techniques + +### Efficient Rendering +```javascript +// Use requestAnimationFrame for smooth animation +function gameLoop() { + // Limit updates to 60fps maximum + requestAnimationFrame(gameLoop); + + // Only redraw if something changed + if (gameState.needsRedraw) { + render(); + gameState.needsRedraw = false; + } +} + +// Efficient canvas clearing +function clearCanvas() { + // Fastest method for full clear + ctx.clearRect(0, 0, canvas.width, canvas.height); +} +``` + +### Drawing Optimization +```javascript +// Batch similar drawing operations +function drawConnections() { + ctx.lineWidth = 3; + + gameState.connections.forEach(connection => { + const gradient = ctx.createLinearGradient( + connection.nodeA.x, connection.nodeA.y, + connection.nodeB.x, connection.nodeB.y + ); + gradient.addColorStop(0, '#00d4ff'); + gradient.addColorStop(0.5, '#ff00ff'); + gradient.addColorStop(1, '#00d4ff'); + + ctx.strokeStyle = gradient; + ctx.beginPath(); + ctx.moveTo(connection.nodeA.x, connection.nodeA.y); + ctx.lineTo(connection.nodeB.x, connection.nodeB.y); + ctx.stroke(); + }); +} +``` + +## Memory Management + +### Object Pooling for Particles +```javascript +class ParticlePool { + constructor(initialSize = 100) { + this.pool = []; + this.active = []; + + // Pre-create objects + for (let i = 0; i < initialSize; i++) { + this.pool.push(new Particle()); + } + } + + acquire() { + let particle = this.pool.pop(); + if (!particle) { + particle = new Particle(); + } + this.active.push(particle); + return particle; + } + + release(particle) { + const index = this.active.indexOf(particle); + if (index !== -1) { + this.active.splice(index, 1); + particle.reset(); + this.pool.push(particle); + } + } +} +``` + +## Performance Monitoring + +### Frame Rate Monitoring +```javascript +class PerformanceMonitor { + constructor() { + this.frameTimes = []; + this.lastTime = performance.now(); + this.frameCount = 0; + } + + update() { + const currentTime = performance.now(); + const frameTime = currentTime - this.lastTime; + this.lastTime = currentTime; + + this.frameTimes.push(frameTime); + if (this.frameTimes.length > 60) { + this.frameTimes.shift(); + } + + this.frameCount++; + + // Log performance every 5 seconds + if (this.frameCount % 300 === 0) { + this.logPerformance(); + } + } + + logPerformance() { + const avgFrameTime = this.frameTimes.reduce((a, b) => a + b) / this.frameTimes.length; + const fps = 1000 / avgFrameTime; + + console.log(`Performance: ${fps.toFixed(1)} fps (${avgFrameTime.toFixed(1)}ms avg frame time)`); + + if (performance.memory) { + const memory = performance.memory.usedJSHeapSize / 1024 / 1024; + console.log(`Memory usage: ${memory.toFixed(1)} MB`); + } + } +} +``` + +### Memory Leak Prevention +```javascript +// Proper cleanup patterns +function cleanupLevel() { + // Remove all event listeners + gameState.nodes.forEach(node => { + if (node.cleanup) node.cleanup(); + }); + + // Clear arrays properly + gameState.nodes.length = 0; + gameState.connections.length = 0; + gameState.particles.length = 0; + + // Clear any timers + clearInterval(gameState.timerId); + gameState.timerId = null; +} +``` + +## Mobile Optimizations + +### Touch Performance +```javascript +// Throttle touch events to 60fps max +const throttledTouchMove = throttle((e) => { + e.preventDefault(); + handleTouchMove(e); +}, 16); + +canvas.addEventListener('touchmove', throttledTouchMove, {passive: false}); +``` + +### Battery Optimization +```javascript +// Reduce frame rate when tab is not visible +document.addEventListener('visibilitychange', () => { + if (document.hidden) { + gameState.targetFPS = 15; // Reduce to save battery + } else { + gameState.targetFPS = 60; // Resume full speed + } +}); +``` + +## Performance Testing Checklist + +### Manual Testing +- [ ] 60fps on desktop Chrome/Firefox/Safari +- [ ] 30fps+ on iPhone 12 Safari +- [ ] 30fps+ on Android Chrome (mid-range device) +- [ ] No frame drops during particle effects +- [ ] Smooth level transitions +- [ ] Memory usage stable over 20+ levels + +### Automated Monitoring +```javascript +// Performance testing helper +if (location.hostname === 'localhost') { + window.performanceTest = { + monitor: new PerformanceMonitor(), + logMemory: () => { + if (performance.memory) { + const used = Math.round(performance.memory.usedJSHeapSize / 1024 / 1024); + const total = Math.round(performance.memory.totalJSHeapSize / 1024 / 1024); + console.log(`Memory: ${used}MB / ${total}MB`); + } + }, + startProfiling: () => { + console.profile('Game Performance'); + }, + stopProfiling: () => { + console.profileEnd('Game Performance'); + } + }; +} +``` + +This guide should be updated as new optimization techniques are discovered and performance targets evolve. \ No newline at end of file