Debugging
Table of Contents
Debugging is the process of finding and fixing problems in your code. Every developer spends a huge amount of time debugging β the key is knowing which tools to use and where to look.
Console Debugging
console.log is the most basic and most used debugging tool.
Code Runner Challenge
Console Debugging
View IPYNB Source
%%js
//CODE_RUNNER: Console Debugging
class Player {
constructor(data) {
this.x = data.x;
this.y = data.y;
this.velocityY = 0;
this.health = 100;
this.isJumping = false;
this.state = "idle";
}
update() {
// Strategic log β tracks position and velocity every frame
console.log(`[update] x: ${this.x}, y: ${this.y.toFixed(1)}, velocityY: ${this.velocityY.toFixed(2)}, state: ${this.state}`);
this.velocityY += 0.4; // gravity
this.y += this.velocityY;
}
handleCollision(other, direction) {
// Strategic log β tracks what was hit and from which direction
console.log(`[collision] hit: ${other.label}, direction: ${direction}, health before: ${this.health}`);
if (other.label === "Enemy" && direction === "side") {
this.health -= other.damage;
this.state = "hurt";
}
// Strategic log β tracks outcome after collision logic runs
console.log(`[collision] health after: ${this.health}, state: ${this.state}`);
}
}
const player = new Player({ x: 100, y: 300 });
console.log("--- Simulating 3 frames ---");
player.update();
player.update();
player.update();
console.log("\n--- Simulating a collision ---");
player.handleCollision({ label: "Enemy", damage: 20 }, "side");
player.handleCollision({ label: "Platform", damage: 0 }, "top");
Lines: 1
Characters: 0
Output
Click "Run" in code control panel to see output ...
Explanation β The logs are prefixed with [update] and [collision] so you can instantly see which method they came from in the console. Running this shows velocityY growing each frame as gravity accumulates, and then the collision log shows health dropping from 100 to 80 after hitting the enemy. Without these logs you would have no way to know what values the game had at the exact moment a bug occurred.
Hitbox visualization
A hit box is the invisible rectangle the game uses for collision detection. When collisions feel wrong β the player dies before touching an enemy, or walks through a platform β the hit box is usually misaligned. Visualizing it draws the rectangle on screen so you can see exactly what the game thinks.
Code Runner Challenge
Hitbox visualization
View IPYNB Source
%%js
//CODE_RUNNER: Hitbox visualization
// Run this to see hit box data being calculated and logged
class GameObject {
constructor(data) {
this.x = data.x;
this.y = data.y;
this.width = data.width;
this.height = data.height;
this.label = data.label;
this.showHitBox = false; // toggle this to visualize
}
// Returns the collision rectangle
getHitBox() {
return {
left: this.x,
right: this.x + this.width,
top: this.y,
bottom: this.y + this.height,
};
}
// In a real game this draws a red rectangle on the canvas
// Here we log what it would draw
drawHitBox() {
if (!this.showHitBox) return;
const hb = this.getHitBox();
console.log(`[hitbox] ${this.label} β left:${hb.left} right:${hb.right} top:${hb.top} bottom:${hb.bottom}`);
}
// Check overlap with another object's hit box
isOverlapping(other) {
const a = this.getHitBox();
const b = other.getHitBox();
return a.left < b.right &&
a.right > b.left &&
a.top < b.bottom &&
a.bottom > b.top;
}
}
const player = new GameObject({ x: 100, y: 300, width: 48, height: 48, label: "Player" });
const enemy = new GameObject({ x: 130, y: 300, width: 40, height: 40, label: "Enemy" });
// Toggle hit boxes on for debugging
player.showHitBox = true;
enemy.showHitBox = true;
console.log("--- Hit box positions ---");
player.drawHitBox();
enemy.drawHitBox();
console.log("\n--- Overlap check ---");
const overlapping = player.isOverlapping(enemy);
console.log(`Player and enemy overlapping: ${overlapping}`);
// Move enemy away and check again
enemy.x = 300;
console.log(`\nAfter moving enemy to x=300 β overlapping: ${player.isOverlapping(enemy)}`);
Lines: 1
Characters: 0
Output
Click "Run" in code control panel to see output ...
Explanation β getHitBox() returns the four edges of the collision rectangle as named values β left, right, top, bottom. drawHitBox() would normally draw a red rectangle on the canvas; here it logs the coordinates so you can verify the numbers match where the sprite appears on screen. isOverlapping() uses those four edges to check if two rectangles intersect.
Source Debugging
The DevTools Sources tab lets you pause execution at any line of code and step through it one line at a time. This is more powerful than console.log because you can inspect every variable at the exact moment the code is paused β without adding any extra logging.
// Run this β then open DevTools Sources tab and set a breakpoint on handleCollision
class Player {
constructor(data) {
this.x = data.x;
this.y = data.y;
this.health = 100;
this.state = "idle";
}
handleCollision(other, direction) {
// Set your breakpoint on the line below in DevTools Sources tab
// Execution will pause here and you can inspect: other, direction, this.health
let tookDamage = false;
if (other.label === "Enemy" && direction === "side") {
this.health -= other.damage;
this.state = "hurt";
tookDamage = true;
}
// Step through to here and check tookDamage in the Scope panel
console.log(`tookDamage: ${tookDamage}, health: ${this.health}`);
return tookDamage;
}
}
const player = new Player({ x: 100, y: 300 });
player.handleCollision({ label: "Enemy", damage: 20 }, "side");
player.handleCollision({ label: "Platform", damage: 0 }, "top");
Explanation β When execution pauses at the breakpoint, the Scope panel on the right shows every variable in the current function β other, direction, this.health β with their live values. You can hover over any variable in the code and a tooltip shows its current value. This is how you catch bugs where a variable has the wrong value at the wrong time.
Network Debugging
The DevTools Network tab records every HTTP request the page makes. For the leaderboard, this shows you the fetch POST and GET calls, the exact data sent, the serverβs response, and any errors like CORS or 404.
Code Runner Challenge
Network debugging
View IPYNB Source
%%js
//CODE_RUNNER: Network debugging
// Run this to simulate what the Network tab would show for a leaderboard POST
const requestDetails = {
url: "https://spring.opencodingsociety.com/api/events/SCORE_COUNTER",
method: "POST",
headers: { "Content-Type": "application/json" },
payload: { payload: { user: "mario", score: 4500, gameName: "MarioGame" } }
};
console.log("=== What the Network tab shows ===");
console.log("URL:", requestDetails.url);
console.log("Method:", requestDetails.method);
console.log("Payload:", JSON.stringify(requestDetails.payload, null, 2));
// Simulating different status codes you might see
const statusCodes = [
{ code: 200, meaning: "OK β score saved successfully" },
{ code: 401, meaning: "Unauthorized β not logged in, check cookies" },
{ code: 403, meaning: "Forbidden β logged in but no permission" },
{ code: 404, meaning: "Not found β wrong URL, check the endpoint" },
{ code: 500, meaning: "Server error β backend crashed, check server logs" },
];
console.log("\n=== Common status codes and what they mean ===");
statusCodes.forEach(s => {
console.log(`${s.code}: ${s.meaning}`);
});
Lines: 1
Characters: 0
Output
Click "Run" in code control panel to see output ...
Explanation β The Network tab is essential for debugging API issues. If the leaderboard is not saving scores, the first place to look is the status code β 401 means you are not logged in, 404 means the URL is wrong, 500 means the backend has a problem. The Payload tab shows exactly what JSON was sent, so you can confirm the data shape matches what the server expects. CORS errors appear in red in the console and also show up as failed requests in the Network tab.
Aplication Debugging
The DevTools Application tab shows everything stored in the browser β localStorage, sessionStorage, and cookies. The leaderboard uses localStorage as a fallback when the backend is unavailable, so this tab lets you verify scores are actually being saved locally.
Code Runner Challenge
Application Debugging
View IPYNB Source
%%js
//CODE_RUNNER: Application Debugging
// Run this to write and read localStorage the same way Leaderboard.js does
const gameName = "MarioGame";
const storageKey = `score_counter_${gameName}`;
// Write a score to localStorage (same as the fallback in Leaderboard.js)
const entry = {
id: `local-${Date.now()}`,
payload: { user: "mario", score: 4500, gameName: gameName },
timestamp: new Date().toISOString()
};
const stored = JSON.parse(localStorage.getItem(storageKey) || "[]");
stored.push(entry);
localStorage.setItem(storageKey, JSON.stringify(stored));
console.log("Score saved to localStorage under key:", storageKey);
// Read it back (same as fetchLeaderboard fallback)
const retrieved = JSON.parse(localStorage.getItem(storageKey) || "[]");
console.log("Retrieved from localStorage:", retrieved.length, "entries");
retrieved.forEach((e, i) => {
console.log(`${i + 1}. ${e.payload.user} β ${e.payload.score} pts`);
});
// In DevTools Application tab you can now see this key and its value
console.log("\nNow open DevTools β Application β Local Storage to see this data live");
Lines: 1
Characters: 0
Output
Click "Run" in code control panel to see output ...
Explanation β localStorage.setItem(key, value) stores a string under a named key that persists even after the page is closed. localStorage.getItem(key) reads it back. Because localStorage only stores strings, JSON.stringify converts the object to text before saving and JSON.parse converts it back after reading. The Application tab in DevTools shows these keys and their raw values, so you can confirm the fallback is working and see exactly what data is stored.
Element Inspection
The DevTools Elements tab shows the full HTML structure of the page while the game runs. You can click on the canvas element or any game UI element and see its exact size, position, and CSS styles β useful for fixing layout issues or verifying the canvas is the right dimensions.
// Run this to log the same info the Elements tab shows about the canvas
// Simulating what DevTools Elements tab reveals about the canvas
const canvasInfo = {
id: "gameCanvas",
tagName: "CANVAS",
width: 800,
height: 450,
className: "game-canvas",
style: {
display: "block",
margin: "0 auto",
border: "2px solid #333"
}
};
console.log("=== Element tab info for the canvas ===");
console.log("Tag:", canvasInfo.tagName);
console.log("ID:", canvasInfo.id);
console.log("Width:", canvasInfo.width);
console.log("Height:", canvasInfo.height);
console.log("Styles:", JSON.stringify(canvasInfo.style, null, 2));
// In a real game you would use:
// const canvas = document.getElementById("gameCanvas");
// console.log("Actual width:", canvas.width);
// console.log("Actual height:", canvas.height);
// console.log("Position:", canvas.getBoundingClientRect());
console.log("\nOpen DevTools β Elements tab β click the canvas to see this live");
Explanation β The Elements tab is useful when the game canvas is the wrong size, positioned incorrectly, or not showing up at all. Clicking the canvas in DevTools highlights it on the page and shows the box model β margin, border, padding, and content size β as a coloured diagram. The Styles panel shows every CSS rule applied and where it came from, so you can see which rule is overriding another. The Properties panel shows JavaScript properties directly on the DOM element, including width and height.