Performance Optimization Skill
This is a real-time game requiring 60fps. Apply these patterns for performance-critical code.
Performance Budget
| Operation |
Budget |
Notes |
| Full frame |
16ms |
60fps target |
| Physics update |
2-4ms |
Wave simulation |
| Rendering |
8-10ms |
Canvas draw calls |
| UI updates |
2-3ms |
React reconciliation |
| Overhead |
2ms |
GC, browser, etc. |
Game Loop Patterns
RequestAnimationFrame
function gameLoop(timestamp) {
const deltaTime = timestamp - lastTimestamp;
lastTimestamp = timestamp;
update(deltaTime);
render();
requestAnimationFrame(gameLoop);
}
Fixed Time Step (for physics)
const PHYSICS_STEP = 1000 / 60; // 16.67ms
let accumulator = 0;
function update(deltaTime) {
accumulator += deltaTime;
while (accumulator >= PHYSICS_STEP) {
physicsUpdate(PHYSICS_STEP);
accumulator -= PHYSICS_STEP;
}
}
Memory Optimization
Object Pooling
// Pre-allocate reusable objects
const foamRowPool = [];
function getFoamRow() {
return foamRowPool.pop() || createFoamRow();
}
function releaseFoamRow(row) {
foamRowPool.push(row);
}
Typed Arrays
// Use for numeric data
const grid = new Float32Array(width * height);
const indices = new Uint16Array(count);
Avoid Allocation in Hot Paths
// Bad - creates new object every frame
function update() {
const vec = { x: 0, y: 0 };
}
// Good - reuse scratch objects
const scratchVec = { x: 0, y: 0 };
function update() {
scratchVec.x = 0;
scratchVec.y = 0;
}
Canvas Rendering Optimization
Batch Draw Calls
// Bad - many small paths
for (const segment of segments) {
ctx.beginPath();
ctx.moveTo(segment.x1, segment.y1);
ctx.lineTo(segment.x2, segment.y2);
ctx.stroke();
}
// Good - single path
ctx.beginPath();
for (const segment of segments) {
ctx.moveTo(segment.x1, segment.y1);
ctx.lineTo(segment.x2, segment.y2);
}
ctx.stroke();
Layer Canvases
// Static background on separate canvas
const bgCanvas = document.createElement('canvas');
// Only redraw when needed, composite over game canvas
Avoid State Changes
// Bad - changes style every iteration
for (const item of items) {
ctx.fillStyle = item.color;
ctx.fillRect(item.x, item.y, item.w, item.h);
}
// Good - group by style
const byColor = groupBy(items, 'color');
for (const [color, items] of byColor) {
ctx.fillStyle = color;
for (const item of items) {
ctx.fillRect(item.x, item.y, item.w, item.h);
}
}
Profiling
Browser DevTools
- Performance tab: flame chart, frame timing
- Memory tab: heap snapshots, allocation timeline
In-Code Timing
const start = performance.now();
expensiveOperation();
console.log(`Operation: ${performance.now() - start}ms`);
Performance Tests
// src/render/foamRendering.perf.test.js
it('renders under 16ms budget', () => {
const start = performance.now();
renderFoam(testData);
expect(performance.now() - start).toBeLessThan(16);
});
Common Pitfalls
- GC Pressure: Creating objects in game loop
- Layout Thrashing: Reading/writing DOM in alternation
- Unthrottled Events: Mouse/touch events firing faster than frames
- Unnecessary Work: Recalculating unchanged values
Reference Plans
plans/tooling/128-react-performance-optimization.md
plans/bugfixes/130-bathymetry-heatmap-performance.md
plans/perf-test-separation.md