Code a Bouncing Ball Arcade Game From Scratch — Physics, Particles & Paddle Mechanics
by jnell61072 in Design > Game Design
26 Views, 0 Favorites, 0 Comments
Code a Bouncing Ball Arcade Game From Scratch — Physics, Particles & Paddle Mechanics
I didn't start by trying to build a game. I started by trying to understand how do you make a ball bounce?
It sounds simple. But once I started writing the code, I realized that a convincing bounce involves a lot of moving parts such as collision detection, velocity reversal, the way a ball squishes when it hits something hard, and the burst of particles that makes an impact visually appealing. As I worked on the code for these mechanisms, I wanted to apply it to something that could be used. Thus this game was created.
I took the bounce mechanics and built a game from scratch using HTML, CSS, and JavaScript. This was done without any game engines and no libraries, just code I wrote and tested in CodePen.
The finished game has a physics driven bouncing ball with squash and stretch animation, a paddle you control with the arrow keys, a full grid of breakable bricks, a screen shake effect, a glowing particle system, and a trail effect behind the ball.
In this guide I'll show you every step of that process starting at the first lines of collision code to the finished, polished game. Even if you've never written a game before, I'll walk you through the logic behind each piece so you actually understand what you're building, not just copy pasting something that works (but I'll also attach the finished game).
Let's build it.
Supplies
What You Need:
Tools (all free, all browser based):
- A computer (Windows, Mac, Linux)
- A web browser
- CodePen (codepen.io) — free account, no download needed
- Optionally: a plain text editor (Notepad, TextEdit, VS Code) for drafting code offline
Languages Used:
- HTML (structure)
- CSS (layout and styling)
- JavaScript (all game logic)
Skill Level: Beginner to Intermediate: you should be comfortable with basic HTML and have seen some JavaScript before, but you do not need to have built a game.
Time Required: Approximately 4–6 hours spread across multiple sessions (the physics logic alone is worth sitting with slowly before moving on).
Start With the Physics, Not the Game
Before writing a single line of game code, I started much smaller. My actual goal was to understand bouncing. More specifically, the math of it. I then decided to create the game once I had that figured out.
The core idea behind any bouncing object is this: a ball has a position (x, y) and a velocity (vx, vy). Every frame, you add the velocity to the position. When the ball hits a wall, you reverse the relevant velocity component. If it hits a left or right wall, you flip vx. If it hits a top or bottom wall, you flip vy.
That's it. That's the foundation everything else builds on.
Before opening CodePen, I wrote out this logic on paper:
- Ball moves right and down (vx = 4, vy = 4)
- Ball hits right wall → vx becomes -4 (now moving left)
- Ball hits bottom wall → vy becomes -4 (now moving up)
Understanding this first, before touching a canvas, meant I wasn't confused later when things went wrong. The math never changed. Only the complexity around it grew.
Tip: Sketch the coordinate system before coding. In HTML canvas, (0,0) is the TOP-LEFT corner, and y increases downward. This trips people up constantly when they first try to code a "bounce off the floor."
Open CodePen and Set Up Your Canvas
CodePen (codepen.io) is a browser based code editor that shows you a live preview of your HTML, CSS, and JavaScript side by side as you type. This is extremely useful for game development because you can see your changes instantly.
You'll have three panels: HTML, CSS, and JS.
In the HTML panel, add this to create a canvas element:
In the JS panel, grab the canvas and get its drawing context:
The "context" (ctx) is what you actually draw with. Think of it as your paintbrush.
To confirm everything works, draw a test circle:
If you see a red circle in the preview panel, your setup is working perfectly.
Tip: CodePen auto runs your JS every time you stop typing for a moment. If your canvas is blank, check the browser console (F12 → Console) for error messages.
Write the Basic Bounce Logic (A Ball That Moves and Reflects)
Now we build the core mechanic. This is the step where the real physics learning happens.
First, define the ball as an object with position and velocity:
Next, write a game loop using requestAnimationFrame. This function tells the browser to call your loop roughly 60 times per second which creates a smooth animation:
Hit save in CodePen. You should see a red ball bouncing around the canvas, perfectly reflecting off all four walls.
This is the moment everything else is built on. Congratulations you've succesfully implemented physics.
Tip: Notice we use ball.radius in the collision check (not just ball.x). If you compare against the center point, the ball will visually clip through the wall before bouncing. Always account for the radius.
Make the Bounce Feel Real by Adding Squash and Stretch
A ball that just reverses direction looks mechanical and flat. Real physics, and all great game feel, involves the ball squishing when it hits something. This is called squash and stretch, and it's one of the classic principles of animation. It is important to note: you need to integrate these code chunks into your existing code instead of copying them as is.
The idea: when the ball hits a horizontal surface, it gets wide and short (squash). When moving freely, it's perfectly round. Over a few frames, it snaps back to normal (stretch recovery).
We add two properties to the ball: sqX and sqY (scale factors):
When a bounce happens, set the squish values:
Then in the draw function, use ctx.scale to apply the squish and recover it each frame:
The line ball.sqX += (1 - ball.sqX) * 0.15 is called "lerping" (linear interpolation). It moves the value 15% closer to 1 every frame, creating a smooth, springy recovery.
Tip: The recovery rate (0.15) controls how snappy the bounce feels. Lower = slower, bouncier. Higher = snappier, more rigid.
Add a Screen Shake Effect
Screen shake is one of the most effective ways to make an impact feel powerful. When the ball hits anything, the whole canvas jitters for a split second.
We store a shake intensity value:
When a bounce happens, set it:
Then in the game loop, before drawing anything, shift the canvas transform by a random amount and decay the shake each frame:
The shakePower *= 0.9 is the same lerp trick, it decays toward zero naturally, so the shake fades out smoothly rather than cutting off abruptly.
Tip: Keep the shake power value subtle (6–10). Anything higher is distracting.
Add a Particle System for Impact Effects
Now add the collision particles. Every time the ball hits something, we spawn a burst of small dots that fly outward and fade away.
We store all active particles in an array:
When an impact happens, push new particle objects in:
Each frame, update and draw every particle, then remove the dead ones:
This "filter by survival" pattern is a common game programming technique: you only keep particles that still have alpha > 0.
Tip: Orange particles for brick hits, red for wall/paddle bounces. Small color differences like this give the player useful visual feedback without any extra UI.
Add a Ball Trail Effect
A short trail behind the ball reinforces the sense of speed and direction. We store recent ball positions and draw faded copies of the ball at each:
Each frame, push the current ball position and cap the array length:
Then draw them before the ball, fading from transparent to opaque:
The oldest dots (index 0) are nearly invisible. The newest (highest index) are nearly solid. The ball on top finishes the effect.
Tip: Draw the trail before the ball, not after. Otherwise the trail will appear on top of the ball, which looks wrong.
Now Apply It to Something
At this point I had all the interesting physics pieces working. The next question was: what do I actually build with it?
I wanted something simple enough to finish, but interactive enough to be fun. A classic brick breaker felt perfect because it uses every piece of the bouncing physics I'd already built, and adds one new element: a player controlled paddle.
I sketched the game design on paper before touching the code:
- A grid of bricks across the top of the canvas
- A paddle at the bottom the player controls with arrow keys
- A ball that bounces off walls, the paddle, and the bricks
Key design decision: the paddle angle mechanic. Rather than simply bouncing the ball straight back, I wanted the hit position on the paddle to affect the ball's horizontal direction. Hitting the left edge sends it sharply left; hitting center sends it straight up; hitting the right edge sends it right. This makes the game feel skillful rather than random.
Build the Paddle
The paddle is a simple rectangle. The challenge is making it respond to keyboard input smoothly.
Define the paddle:
Track which keys are currently held down using a state object (not single keypress events because that causes stuttery movement):
Then in the game loop, move the paddle based on which keys are held and clamp it to the canvas edges:
Important: This was one of my biggest early bugs. I initially used the "keydown" event directly to move the paddle. This fires once, pauses, then repeats slowly (like holding a key in a text editor). The fix is the keys object pattern shown above, which tracks "is this key currently held?" and moves every single frame it's true.
When the ball hits the paddle, two things happen: it bounces upward, and its horizontal direction changes based on where it hit.
The hitPos variable is a value between -1 and 1 (left edge to right edge of the paddle). Multiplying by 5 converts that into a horizontal velocity. Hit dead center → ball goes straight up. Hit the far edge → ball fires off at a steep angle.
We also check ball.vy > 0 to ensure we only trigger the collision when the ball is moving downward. Without this, the ball can get "stuck" inside the paddle and bounce rapidly back and forth.
Tip: The ball fell through the paddle on my first attempt because I only checked the ball's center point against the paddle's top edge. Always add the ball's radius to the y check so it triggers when the ball's surface touches the paddle, not when its center does.
Build the Brick Grid
The bricks are stored in a flat array. We calculate their positions based on the canvas size at build time, which means they always fit regardless of screen size.
Each brick has an alive flag. When the ball hits it, we set alive to false and stop drawing it.
For the collision, we use the "nearest point on rectangle" technique. This is a clean way to check if a circle overlaps a rectangle:
This is more accurate than a simple rectangle overlap check and handles corner hits correctly.
Tip: Use orange particles for brick hits (different from the red wall bounce particles). It gives the player instant visual feedback about what they just hit.
Handle Canvas Sizing
This was my most frustrating challenge. When I first set the canvas up, the game looked wrong because the bricks were either cut off at the edges or overlapping the paddle. Sometimes the paddle appeared off screen entirely.
The root cause: HTML canvas has two separate "sizes": the pixel dimensions of its drawing buffer (canvas.width, canvas.height) and the visual size it's displayed at on screen (CSS width and height). If these don't match, your game draws to one coordinate space while displaying at another, and everything gets stretched or clipped.
The fix was a resize function that syncs them:
This must be called before buildBricks(), so the bricks calculate their positions based on the correct canvas dimensions.
In CSS, the canvas is sized with percentage-based dimensions and a max size:
Important: buildBricks() uses canvas.width to calculate positions. If you call buildBricks() before resizeCanvas(), the bricks will be built for the wrong canvas size and the layout will break. Always resize first, then build.
Add a Start Screen and Restart Button
A bare canvas that just starts the game feels unfinished. A start screen gives the player context and a moment to get ready.
We create a start screen overlay div in HTML positioned absolutely over the canvas. Clicking the Start button hides it and begins the game:
The gameRunning flag is checked at the top of the game loop:
This means the game loop is always running (so we can keep responding to resize events), but it only draws and updates when gameRunning is true.
The restart button simply rebuilds bricks and resets the ball.
Tip: Position the restart button as an absolutely positioned element inside the game wrapper div, not outside the canvas. This keeps it visually attached to the game area on all screen sizes.
Polish by Adding Glow Effects and Visual Styling
At this point the game worked. Now the goal was making it look good.
Everything in the game uses the HTML canvas shadow API to create a glow effect:
Always reset shadowBlur to 0 after drawing each glowing element. If you don't, the glow bleeds onto every subsequent drawing operation including the background clear, which creates a hazy film over the whole canvas.
The color scheme was chosen deliberately: black background, cyan paddle, red ball, orange bricks. These are all colors that glow naturally and contrast strongly against black — classic arcade aesthetics that are also easy to read during fast gameplay.
The CSS around the canvas also contributes to the look. A cyan border with the dark background frames the game as its own contained world:
Tip: The shadowBlur effect in canvas is GPU accelerated but still has a cost. If your game starts feeling slow (especially on older devices), reduce shadowBlur values or only apply glow to the main ball and paddle.
Final Testing and Things I'd Do Differently
Problem 1: The paddle didn't move at all. My first attempt used keydown events to directly move the paddle. It worked once, then stuttered. The fix was switching to a key state object (holding keys.left = true on keydown and false on keyup) and moving the paddle in the game loop based on that state. This gives smooth, frame rate consistent movement.
Problem 2: The canvas was the wrong size and bricks were off screen. The CSS was sizing the canvas visually, but canvas.width/height still reflected the default 300×150 pixel buffer size. The fix was a resizeCanvas() function that syncs the drawing buffer to the displayed size, called before anything else.
Problem 3: The ball stuck inside the paddle. The paddle collision fired both on the way in and the way out, causing the ball to rapidly alternate direction while overlapping the paddle. The fix was adding a ball.vy > 0 check which only responded to the collision when the ball is actually moving downward.
What I'd do differently next time:
- Add a score counter (gives the player a number to chase)
- Add increasing ball speed as bricks are cleared (the game never gets harder right now)
- Add multiple lives instead of just resetting the ball when it falls
- Try touch controls so it works on mobile (the arrow keys don't work on a phone)
CONCLUSION
What started as an attempt to understand a single math concept, how do you reverse a velocity when a ball hits a wall, turned into a complete arcade game with particles, screen shake, squash animation, glowing graphics, and a full brick breaking mechanic.
The thing that surprised me most was how much the "game feel" details mattered. The screen shake, the squish on impact, the particle burst: none of these affect the rules of the game at all. But take them out and the game feels dead and flat. Those small additions are the difference between a physics demo and something actually fun to play.
If you build your own version, here are a few directions to take it further: add a high score system using localStorage, swap the paddle for a circle and make it a Pong game instead, or change the brick layout to spell out a word or pattern. The physics engine you've built here can power any of those.
The whole game runs in a browser with zero dependencies: no libraries, no installs, no setup. Just code.