Cheatsheet
A printable game development cheatsheet: game loop, fixed timestep, collision, input mapping, and the smallest snippets to get each working.
Game dev — printable cheatsheet
EXAMPLE
// ===== The game loop =====
// Naive (frame-locked):
// while (running) { input(); update(dt); render(); }
//
// Fixed timestep (decouples physics from render):
let lastTime = performance.now();
let acc = 0;
const STEP = 1 / 60; // 60 Hz physics
function frame(now) {
const dt = Math.min(0.25, (now - lastTime) / 1000); // clamp big stalls
lastTime = now;
acc += dt;
while (acc >= STEP) { update(STEP); acc -= STEP; }
const alpha = acc / STEP;
render(alpha); // interpolate render between physics steps
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
// ===== Vec2 quick math =====
const v = (x, y) => ({ x, y });
const add = (a, b) => v(a.x + b.x, a.y + b.y);
const sub = (a, b) => v(a.x - b.x, a.y - b.y);
const mul = (a, s) => v(a.x * s, a.y * s);
const len = (a) => Math.hypot(a.x, a.y);
const norm = (a) => { const L = len(a) || 1; return v(a.x / L, a.y / L); };
const dot = (a, b) => a.x * b.x + a.y * b.y;
// ===== AABB collision =====
function aabb(a, b) {
return a.x < b.x + b.w && a.x + a.w > b.x &&
a.y < b.y + b.h && a.y + a.h > b.y;
}
// ===== Circle vs circle =====
function circ(a, b) {
const r = a.r + b.r;
return (a.x - b.x) ** 2 + (a.y - b.y) ** 2 <= r * r;
}
// ===== Input mapping =====
const keys = new Set();
addEventListener('keydown', e => keys.add(e.code));
addEventListener('keyup', e => keys.delete(e.code));
function axis(neg, pos) { return (keys.has(pos) ? 1 : 0) - (keys.has(neg) ? 1 : 0); }
// move: const vx = axis('ArrowLeft', 'ArrowRight'); const vy = axis('ArrowUp', 'ArrowDown');
// ===== Spatial partition (grid) =====
class Grid {
constructor(cell) { this.cell = cell; this.map = new Map(); }
key(x, y) { return ((x / this.cell) | 0) + ',' + ((y / this.cell) | 0); }
insert(o) {
const k = this.key(o.x, o.y);
if (!this.map.has(k)) this.map.set(k, []);
this.map.get(k).push(o);
}
near(o) {
const cx = (o.x / this.cell) | 0, cy = (o.y / this.cell) | 0;
const out = [];
for (let dy = -1; dy <= 1; dy++)
for (let dx = -1; dx <= 1; dx++) {
const list = this.map.get((cx + dx) + ',' + (cy + dy));
if (list) out.push(...list);
}
return out;
}
}
// ===== Easings =====
const easeOutCubic = t => 1 - (1 - t) ** 3;
const easeInOutQuad = t => t < 0.5 ? 2 * t * t : 1 - (-2 * t + 2) ** 2 / 2;
// ===== Sound (Web Audio) =====
const audio = new AudioContext();
async function load(url) {
const buf = await fetch(url).then(r => r.arrayBuffer());
return audio.decodeAudioData(buf);
}
function play(buffer, vol = 1) {
const src = audio.createBufferSource();
const gain = audio.createGain();
gain.gain.value = vol;
src.buffer = buffer; src.connect(gain).connect(audio.destination); src.start();
}
// ===== Pitfalls =====
// - Frame-locked physics on uneven hardware -> jitter
// - Using deltaTime without clamping the big stall after tab returns
// - O(N^2) collisions on large worlds -> use a grid or quadtree
// - Audio without user gesture -> blocked on web (resume() after a click)
// - Floating-point comparisons for grid keys -> bugs at cell boundaries
Why it matters
Pin the cheatsheet, drill the fixed-timestep loop, and reach for spatial partitions before the world grows. The same skeleton ports across engines: physics on a tick, render with interpolation, input as discrete events.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// Game loop | Delta time | Sprites | Input | Collisions | State machine | A*Try it Yourself »
Discussion
Loading…