Nerd For Tech
Published in

Nerd For Tech

Building Pong with Javascript

A how-to guide on building out Pong with nothing but HTML,CSS and JS.

Image by StartupStockPhotos from Pixabay

As I am a recent software graduate, I have been busy spending my time applying to jobs, networking and learning new technologies. This weekend I decided to do something fun and stay relatively productive to my job search so I built a pong game using vanilla JS and I had a BLAST doing it.

I documented the code from start to finish so I can share with my fellow devs that loves to game like I do and created this guide if you want to learn how to build Pong using nothing but HTML,CSS and JS.

Let’s get started!

Installation

Let’s start by using the terminal to create a folder with our boilerplate files

mkdir pong-app
cd pong-app
touch index.html
touch game.js
touch game.css

Next we go into our HTML file and if you’re using VScode like me, you can simple type html and emmet will build out the html boiler plate for you, we then add a link to our stylesheet in the html head and add the game.js script to our body

<!DOCTYPE html><html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pong Game</title>
<link rel="stylesheet" href="game.css">
</head><body><script src="game.js"></script></body></html>

Next we set some simple css code

//css codebody {margin: 0;background-color: rgb(39, 39, 39);display: flex;justify-content: center;font-family: "Courier New", Courier, monospace;width: 100vw;height: 100vh;}

I chose this gray for my background but if you want a different color scheme, you can always change the values of the app background color.

Time to start coding, the next step would be creating a canvas for our game, you can check out the code below and lets see the results.

If you aren’t familiar working with canvas you can always check out this doc from W3School.

const { body } = document//canvas codeconst canvas = document.createElement('canvas')const context = canvas.getContext('2d')const width = 500const height = 700const screenWidth = window.screen.widthconst canvasPosition = screenWidth / 2 - width / 2const isMobile = window.matchMedia('(max-width: 600px)')//generating canvasconst renderCanvas = () => {    context.fillStyle = 'black'    context.fillRect( 0, 0, width, height)}const createCanvas = () => {    canvas.width = width    canvas.height = height    body.appendChild(canvas)    renderCanvas()}createCanvas()

This is looking great! we got our canvas rendering correctly now.

Next we want to write the logic for the paddles such as the size and positioning .

//paddle codeconst paddleHeight = 10
const paddleWidth = 50
let paddleBottomX = 225
let paddleTopx = 225

This should handle our paddle for the canvas by setting the paddle size and positioning it to the middle, remember since the paddle has a width of 50 so the middle of 500 width would be 225 with that 50 offset from the paddle.

Next let’s go fill in the canvas with out paddles in the render function

const renderCanvas = () => {//canvas background
context.fillStyle = 'black'
context.fillRect( 0, 0, width, height)
//paddle color
context.fillStyle = 'white
//player paddle
context.fillRect(paddleBottomX, height - 20, paddleWidth, paddleHeight)
//bot paddle
context.fillRect(paddleTopX, 10, paddleWidth, paddleHeight)
}

Voila! we have our paddles, next we want to set the middle point of the canvas by setting a stroke path from the left to the right.

//render function//middle line
context.beginPath()
context.setLineDash([4])
context.moveTo(0, 350)
context.lineTo(500,350)
context.strokeStyle = 'lime'
context.stroke()

Here I set the line positioning to the middle of the canvas and set the color to a retro lime green.

Next we create our ball logic in the global variables since the ball’s x and y coordinates will change up. We then update our render function to render a round ball for us.

//global variableslet ballX = 250
let ballY = 350
const ballRadius = 5
// in render function//ballcontext.beginPath()
context.arc(ballX, ballY, ballRadius, 2 * Math.PI, false)
context.fillStyle = 'white'
context.fill()

Now that the ball is rendered, Iwill set a default score for both the player and our bot and set the winning score as well in the global variables.

//globally
// score
let playerScore = 0
let botScore = 0
const winningScore = 7//in render function
// score
context.font = '32px Courier New'
context.fillText(playerScore, 20, canvas.height / 2 + 50)
context.fillText(botScore, 20, canvas.height / 2 - 30)

Now that the canvas is done, I went back to refractor a little and created a start game function that resets the scores and start the canvas function and the animation function as well.

You can see the updated code on the left and the canvas is still there so we know it’s working properly.

Next we want to work on the game logic, we start by setting the ball speed globally.

//global variablelet speedY = -1
let speedX = speedY
let computerSpeed = 3

Next we write out a function to make the ball move and call it within our animate function.

const ballMove = () => {
// Vertical Speed
ballY += - speedY
// Horizontal Speed
if (playerMoved && paddleContact) {
ballX += speedX }
}

Next we make a ball reset function for the game.

const ballReset = () => {ballX = width / 2ballY = height / 2speedY = -3paddleContact = false}

Next we move on to the ball boundaries for deciding if the player gets the score or your bot does and call this function within the animate function as well

Next we want to build out the botAI and we can do that by making a simple function to make the bot follow the ball.

const botAI = () => {  if (playerMoved) {    if (paddleTopX + paddleDiff < ballX) {      paddleTopX += computerSpeed    } else {      paddleTopX -= computerSpeed    }  }}

Once you are done building out the game logic, your animate function should contain the game logic we just built and how often to run this, next we can use the window.requestAnimationFrame to call our animate function once every frame which comes out to 60 frames a second. This wouldn’t be a gamer built game without that 60fps smoothness.

And voila! The ball is animating correctly! our next step is to finish the game logic by writing the game over logic.

First we want to globally declare that the game is a new game and a game start/finish logic.

///global variables
//score
let isNewGame = true
let isGameOver = true

Next we add in ‘isGameOver = false’ within our startGame function to set the game as running when the game starts.

Next we want to write a game over function to stop the game when the winning score is reached and we call this in the animate function.

const gameOver = () => {  if(playerScore === winningScore || botScore === winningScore) {    isGameOver = true  }
}

Now if you test the game, it will stop once the player or bot reaches a score of 7.

Next we create a empty div by creating a element on our document that will contain our game over message.

const gameOverDiv = document.createElement('div')

Next we create a show game over message function that hides our canvas and add another div with a victory message and we also create a button with a onclick listener to run the game again once it’s clicked.

const showGameOverMsg = (winner) => {//hide canvascanvas.hidden = truegameOverDiv.textContent = ''const title = document.createElement('h1')title.textContent = `${winner} Wins!!!`const playAgainBtn = document.createElement('button')playAgainBtn.setAttribute('onclick', 'startGame()')playAgainBtn.textContent = 'Play Again'gameOverDiv.append(title, playAgainBtn)body.appendChild(gameOverDiv)}

Now that’s done we have to finish adding the game over logic inside of our startGame function.

//inside startGame functionif (isGameOver && !isNewGame) {body.removeChild(gameOverDiv)canvas.hidden = false}isGameOver = falseisNewGame = false

That’s it! we are all done! Took awhile but if you followed this article correctly everything should be working. You can see my deployed sample here, my current deployed pong game is a little buggy on mobile but it should run fine on desktop, let’s go pc master race! It might look slight different than yours depending on how you do the CSS and here is the github repo if you need to check out the code.

Thank you for reading and please leave a comment if you‘ve enjoyed and follow for more coding and tech content.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Steven Wu

Steven Wu

I’m a NYC based full stack developer and a part-time gaming nerd https://www.linkedin.com/in/stevenwubc/