By the end of this guide, you’ll know how to wire up keyboard controls, keep a live score, and detect collisions in a browser game using JavaScript event listeners. These three mechanics are the backbone of almost every interactive game, and learning them together gives you a mental model you can reuse in any project you build next.
- An event listener waits for a player action and runs a function in response.
- The
keydownevent fires every time a player presses a key, giving you the key name viaevent.key. - Event listeners belong outside your game loop — registering them inside causes duplicate listeners and bugs.
- Score is a plain JavaScript variable that you increment on events and display using
textContent. - Collision detection is a check you write yourself, using
getBoundingClientRect()to compare element positions.
What Event Listeners Actually Do in a Game
An event listener is a piece of code that waits for something to happen and then runs a function in response. Think of it like a security guard watching a door. Nothing happens until someone walks through. The moment they do, the guard reacts.
In JavaScript, you attach event listeners using the built-in addEventListener method. This method takes two arguments: the name of the event you want to watch for, and the function you want to run when that event fires. That function is called a callback, meaning JavaScript calls it back when the moment arrives.
In a game, three things constantly need responses: key presses move your character, collisions update the score, and button clicks reset the game. Keyboard controls trigger movement, movement triggers collision checks, and collisions update the score. Once you see that chain, the whole system clicks into place.
Keyboard Controls: Moving Your Character with keydown
How the keydown Event Works
The keydown event fires every time the player presses a key. JavaScript creates an event object, a bundle of information about what just happened, and passes it to your callback function. Inside that object, event.key tells you exactly which key was pressed, as a string like "ArrowLeft" or "ArrowRight".
Open your browser’s DevTools console (press F12) and paste this to see it in action:
document.addEventListener("keydown", function(event) {
console.log(event.key);
});
Press any key. You’ll see its name printed in the console. Arrow keys show as "ArrowUp", "ArrowDown", "ArrowLeft", and "ArrowRight". This is the exact string you’ll check in your game logic.
Moving a Character with Arrow Keys
We listen on the document object rather than a specific element. That way, the game catches key presses no matter where the player’s focus is on the page.
let playerX = 100;
const player = document.getElementById("player");
document.addEventListener("keydown", function(event) {
if (event.key === "ArrowLeft") playerX -= 10;
if (event.key === "ArrowRight") playerX += 10;
player.style.left = playerX + "px";
});
This code stores the player’s horizontal position in a variable called playerX. Each arrow key press changes that number by 10 pixels, then updates the element’s CSS position to match. Copy this into a local HTML file with a positioned div and watch your character move.
keydown vs. keyup: Which One Should You Use?
Use keydown for movement. It fires continuously while the key is held, which feels natural. Use keyup when you need to stop an action the moment the player releases a key, like stopping a character from running. Mixing them up is a common source of sluggish or unresponsive controls.
Where to Put Your Event Listeners in a Game
The Most Common Beginner Mistake
If you place addEventListener inside a game loop, a function that runs 60 times per second, you register a brand new listener on every single frame. After one second, you have 60 listeners all firing at once. Your game breaks fast.
// WRONG — adds a new listener every frame
function gameLoop() {
document.addEventListener("keydown", movePlayer); // Don't do this
requestAnimationFrame(gameLoop);
}
// RIGHT — register once, outside the loop
document.addEventListener("keydown", movePlayer);
function gameLoop() {
checkCollisions();
requestAnimationFrame(gameLoop);
}
Register your listeners once when the page loads. They’ll keep working for the entire life of the game without any extra effort.
Score Tracking: Updating a Number in Real Time
Score as a JavaScript Variable
Score is just a number stored in memory. You declare it at the top of your script, increment it when something good happens, and display the new value in an HTML element. Three steps, every time.
let score = 0;
const scoreDisplay = document.getElementById("score");
function addPoint() {
score += 1;
scoreDisplay.textContent = "Score: " + score;
}
Here, we’re updating the element’s textContent property, the text visible inside an HTML tag, every time addPoint() runs. The display stays in sync with the actual score because both update together in the same function call.
Wiring Score to a Game Event
Call addPoint() from inside your collision check or any other game event. You don’t need a separate event listener just for score. The score responds to the same events that drive your game logic.
The pattern is always: event fires, score variable increases, display element updates. Repeat that loop every time the player earns a point.
Collision Detection: Checking When Two Things Touch
Collision detection in JavaScript is a check you write yourself and call repeatedly during gameplay. The browser doesn’t fire a built-in “collision” event. You compare the positions of two elements on every frame and decide whether they overlap.
How to Detect a Collision Step by Step
- Call
getBoundingClientRect()on both elements. This browser method returns an object with the element’stop,bottom,left, andrightpixel positions relative to the viewport. - Compare the four edges of both rectangles.
- If the rectangles overlap on all four sides, a collision has occurred.
- Run your response code: add a point, play a sound, or end the game.
function checkCollision(playerEl, itemEl) {
const a = playerEl.getBoundingClientRect();
const b = itemEl.getBoundingClientRect();
return (
a.left < b.right &&
a.right > b.left &&
a.top < b.bottom &&
a.bottom > b.top
);
}
// Inside your game loop:
if (checkCollision(player, collectible)) {
addPoint();
resetCollectiblePosition();
}
This code uses bounding box collision detection, the simplest and most practical approach for DOM-based games. Try changing the position of your collectible element and watch the function catch the overlap the moment it happens.
Adding a Reset Button with a Click Event Listener
Every game needs a way to start over. A reset button uses the exact same addEventListener pattern, just with a "click" event instead of "keydown".
const resetButton = document.getElementById("reset-btn");
resetButton.addEventListener("click", function() {
score = 0;
playerX = 100;
scoreDisplay.textContent = "Score: 0";
player.style.left = playerX + "px";
});
The reset function sets the score back to zero, moves the player to its starting position, and updates the display. This pattern, resetting all state variables and syncing the DOM, applies to any game you build, from simple collectors to platformers.
Your First Working Mini-Game: Putting It Together
Here’s a minimal complete example under 50 lines. Drop this into an HTML file and open it in your browser.
DOM manipulation is a great starting point, but once your game grows beyond a handful of moving elements, you’ll likely run into performance bottlenecks—choppy animations, sluggish updates, and a layout engine working overtime. That’s where the Canvas API earns its place. Rendering directly to a pixel buffer gives you far more control over every frame, making smooth 60fps animation genuinely achievable. If you want to take that next step, this HTML5 Canvas tutorial for beginners walks you through drawing shapes and animating objects with JavaScript from the ground up.
<!-- index.html -->
<div id="game" style="position:relative;width:400px;height:300px;border:2px solid #333;overflow:hidden;">
<div id="player" style="position:absolute;width:40px;height:40px;background:blue;top:130px;left:100px;"></div>
<div id="item" style="position:absolute;width:30px;height:30px;background:gold;top:135px;left:300px;"></div>
</div>
<p>Score: <span id="score">0</span></p>
<button id="reset-btn">Reset</button>
<script>
let playerX = 100, score = 0;
const player = document.getElementById("player");
const item = document.getElementById("item");
const scoreDisplay = document.getElementById("score");
document.addEventListener("keydown", function(e) {
if (e.key === "ArrowLeft") playerX -= 10;
if (e.key === "ArrowRight") playerX += 10;
player.style.left = playerX + "px";
});
document.getElementById("reset-btn").addEventListener("click", function() {
score = 0; playerX = 100;
scoreDisplay.textContent = "0";
player.style.left = "100px";
});
function checkCollision(a, b) {
const r1 = a.getBoundingClientRect(), r2 = b.getBoundingClientRect();
return r1.left < r2.right && r1.right > r2.left && r1.top < r2.bottom && r1.bottom > r2.top;
}
function loop() {
if (checkCollision(player, item)) {
score++; scoreDisplay.textContent = score;
item.style.left = Math.random() * 360 + "px";
}
requestAnimationFrame(loop);
}
loop();
</script>
This code combines everything: a keyboard listener moves the blue square, the game loop checks for collisions on every frame, and each collision increments the score and repositions the gold square. A real game would add multiple enemies, levels, and smooth animation via requestAnimationFrame timing, but this is the exact same foundation those games use.
FAQ: JavaScript Event Listeners for Games
What is addEventListener in JavaScript?
addEventListener is a built-in JavaScript method that attaches a listener to an element or the document. You give it an event name and a callback function. When the event fires, JavaScript runs that function automatically.
How do you detect key presses in a browser game?
Attach a keydown event listener to the document object. Inside the callback, read event.key to find out which key was pressed. Use an if statement to decide what the game should do for each key.
What is the difference between keydown and keyup events?
keydown fires the moment a key is pressed and keeps firing while it’s held down. keyup fires once when the key is released. For player movement, keydown gives you the continuous response players expect.
Why isn’t my collision detection working?
Check that you’re calling getBoundingClientRect() inside your game loop, not just once at startup. Element positions change as the game runs, so you need fresh measurements on every frame.
You’ve now got the three core mechanics that make a game feel alive. Try adding a second collectible, or swap arrow keys for WASD controls. The pattern is the same: pick an event, write a listener, update your game state. From here, explore the MDN Web Docs reference for addEventListener to see the full list of events you can use, then check out our guide on animating your game with requestAnimationFrame for smooth, frame-rate-aware movement.

Brian Taylor is a JavaScript developer and educator, dedicated to demystifying programming for newcomers. With a career spanning over a decade in web development, Brian has a deep understanding of JavaScript and its ecosystem. He is passionate about teaching and has helped countless beginners grasp the fundamentals of JavaScript, enabling them to build their own web applications.



