Kamu sudah membuat game yang seru, mekaniknya solid, grafisnya menarik. Tapi begitu di-test di perangkat kelas menengah, fps drop, lag, dan battery drain menjadi masalah. Sound familiar?
Browser bukan environment game yang ideal. Ada garbage collection, single-threaded nature, dan constraint lainnya. Tapi dengan teknik optimasi yang tepat, kamu bisa membuat game browser yang berjalan mulus di 60fps bahkan di device low-end.
1. Profiling: Temukan Bottleneck Sebelum Optimasi
Jangan asal optimasi. Gunakan Chrome DevTools untuk profiling:
- Performance Tab: Record dan analisis frame-by-frame
- Memory Tab: Deteksi memory leaks
- Rendering Tab: Enable "FPS meter" dan "Paint flashing"
🎯 Rule of Thumb
- 60fps = ~16.67ms per frame
- Jika update + render > 16ms, kamu akan drop frame
- Target: Sisakan 2-3ms buffer untuk browser overhead
2. Object Pooling: Hindari Garbage Collection
JavaScript menggunakan garbage collection untuk membebaskan memory. Masalahnya: GC pause bisa membuat game freeze beberapa milidetik—cukup untuk membuat stuttering yang annoying.
Solusinya adalah object pooling: buat pool objek sekali, reuse berulang kali, dan jangan biarkan reference hilang.
class BulletPool {
constructor(size) {
this.pool = [];
this.active = [];
// Pre-create bullets
for (let i = 0; i < size; i++) {
this.pool.push({
x: 0, y: 0,
active: false,
reset() {
this.x = 0;
this.y = 0;
this.active = false;
}
});
}
}
get() {
// Find inactive bullet
for (let bullet of this.pool) {
if (!bullet.active) {
bullet.active = true;
this.active.push(bullet);
return bullet;
}
}
return null; // Pool exhausted
}
release(bullet) {
bullet.reset();
const index = this.active.indexOf(bullet);
if (index > -1) this.active.splice(index, 1);
}
updateAll() {
for (let i = this.active.length - 1; i >= 0; i--) {
const bullet = this.active[i];
bullet.y -= 10;
if (bullet.y < 0) {
this.release(bullet);
}
}
}
}
Kenapa Object Pooling Efektif?
Dengan pooling, kita menghindari new dan delete (yang memicu GC) di game loop. Memory usage tetap konstan, dan tidak ada pause yang mengganggu gameplay.
3. Spatial Hashing untuk Collision Detection
Collision detection O(n²) tidak scalable. Dengan 100 objek, kamu perlu 10,000 checks per frame! Gunakan spatial hashing atau quadtree untuk reduce complexity.
class SpatialHash {
constructor(cellSize) {
this.cellSize = cellSize;
this.cells = new Map();
}
getKey(x, y) {
const cx = Math.floor(x / this.cellSize);
const cy = Math.floor(y / this.cellSize);
return `${cx},${cy}`;
}
insert(obj) {
const key = this.getKey(obj.x, obj.y);
if (!this.cells.has(key)) {
this.cells.set(key, []);
}
this.cells.get(key).push(obj);
}
query(x, y, radius) {
const results = [];
const r = Math.ceil(radius / this.cellSize);
const cx = Math.floor(x / this.cellSize);
const cy = Math.floor(y / this.cellSize);
// Check neighboring cells only
for (let dx = -r; dx <= r; dx++) {
for (let dy = -r; dy <= r; dy++) {
const key = `${cx + dx},${cy + dy}`;
const cell = this.cells.get(key);
if (cell) results.push(...cell);
}
}
return results;
}
clear() {
this.cells.clear();
}
}
💡 Complexities Comparison
- Brute force: O(n²)
- Spatial Hash: O(n) average case
- 1000 objects: 1,000,000 checks → ~10 checks per object
4. Canvas Optimization Techniques
A. Batch Draw Calls
Setiap state change di canvas (fillStyle, strokeStyle, etc.) memiliki overhead. Batch draw calls dengan entity yang sama:
// BAD: State changes setiap iterasi
enemies.forEach(enemy => {
ctx.fillStyle = enemy.color; // State change!
ctx.fillRect(enemy.x, enemy.y, enemy.w, enemy.h);
});
// GOOD: Batch by color
const byColor = groupBy(enemies, 'color');
for (const [color, group] of byColor) {
ctx.fillStyle = color; // State change sekali per color
group.forEach(enemy => {
ctx.fillRect(enemy.x, enemy.y, enemy.w, enemy.h);
});
}
B. Use requestAnimationFrame Tepat
let lastTime = 0;
const targetFPS = 60;
const frameInterval = 1000 / targetFPS;
function gameLoop(timestamp) {
requestAnimationFrame(gameLoop);
const deltaTime = timestamp - lastTime;
// Skip frame jika belum waktunya
if (deltaTime < frameInterval) return;
// Adjust untuk irregular intervals
const adjustedDelta = deltaTime % frameInterval;
lastTime = timestamp - adjustedDelta;
update(deltaTime);
render();
}
C. Offscreen Canvas untuk Static Elements
Jika ada elemen yang jarang berubah (background, UI elements), render sekali ke offscreen canvas, lalu drawImage ke canvas utama.
5. Memory Management Best Practices
- Avoid closures in loops: Setiap closure menyimpan reference ke scope
- Nullify references: Jika object sudah tidak digunakan, set ke null
- Limit event listeners: Use event delegation daripada listener per element
- Dispose resources: Image, Audio, WebGL textures perlu cleanup eksplisit
6. Mobile-Specific Optimizations
Mobile devices memiliki constraint yang berbeda:
// Deteksi mobile
const isMobile = /Android|webOS|iPhone|iPad/i.test(navigator.userAgent);
// Mobile optimizations
if (isMobile) {
// Reduce canvas resolution
const scale = Math.min(window.devicePixelRatio, 1.5);
canvas.width = displayWidth * scale;
canvas.height = displayHeight * scale;
// Reduce particle count
MAX_PARTICLES = 50; // vs 200 on desktop
// Disable expensive effects
enableGlow = false;
enableScreenShake = false;
}
7. Loading dan Asset Management
Game yang bagus harus cepat load. Teknik yang bisa digunakan:
- Sprite atlases: Gabungkan texture ke satu file, reduce draw calls
- Lazy loading: Load assets level-by-level
- Compression: Use WebP untuk images, Ogg untuk audio
- Progressive loading: Tampilkan gameplay essentials dulu
Kesimpulan
Optimasi performa adalah trade-off. Setiap optimasi ada cost-nya—baik dalam bentuk code complexity, memory usage, atau development time. Fokus pada bottleneck yang paling signifikan berdasarkan profiling data.
Remember: Premature optimization is the root of all evil. Buat game yang berfungsi dulu, lalu optimasi berdasarkan data. Game yang fun tapi sedikit lag lebih baik daripada game super-optimized tapi tidak seru dimainkan.
🚀 Lihat Game yang Teroptimasi
Kunjungi Studio Rhadzor untuk melihat berbagai game dan aplikasi dengan berbagai teknik optimasi.
Jelajahi Studio →Ada tips optimasi lain yang ingin dishare? Atau pertanyaan tentang performa game? Hubungi saya di studio.rhadzor.id