Robert Johns | 17 Feb, 2025
Fact checked by Jim Markus

Build a Python Pong Arcade Game with Turtle (Step-by-Step)

In this tutorial, we will build a Pong-style arcade game using Python and the Turtle module. This project is perfect for beginners who want to learn about handling user input, screen updates, and object movement in a simple game environment.

By the end of this tutorial, you will have a fully functional Pong game where two players can compete by controlling paddles to bounce a ball back and forth.

Step 1: Setting Up the Project

Before we start coding, let’s set up our Python project:

1. Make sure Python is installed on your computer. If not, download it from the official Python website.
2. Open your favorite code editor or IDE.
3. Create a new Python file, for example, pong_game.py.

Great, now, let's dive head first into our Python editor to get this build started.

Step 2: Understanding the Game Mechanics

Pong is a classic two-player game where:

  • Each player controls a paddle (left and right) to prevent the ball from going past their side.
  • The ball bounces off the top and bottom walls.
  • If a player misses the ball, the opposing player scores a point.
  • The game continues indefinitely with score tracking.

We will use Python’s built-in Turtle module to create the paddles, ball, and scoreboard while implementing simple keyboard controls for movement, and when we're done, we should have something that looks like this:

GUI for Pong arcade game built with Turtle in Python.

Step 3: Importing Required Modules

First, we need to import the Turtle module, which provides all the necessary functions to draw objects and handle movement in our game.

import turtle

Step 4: Setting Up the Game Window and Components

We will now implement the game layout using Turtle to create the game window, paddles, ball, and scoreboard.

# Initialize Turtle Screen
def setup_game():
    screen = turtle.Screen()
    screen.title('Pong Arcade Game')  # Set window title
    screen.bgcolor('white')  # Set background color
    screen.setup(width=1000, height=600)  # Set window dimensions

    # Define paddles
    l_paddle = turtle.Turtle()
    l_paddle.speed(0)
    l_paddle.shape('square')
    l_paddle.color('red')
    l_paddle.shapesize(stretch_wid=6, stretch_len=2)
    l_paddle.penup()
    l_paddle.goto(-400, 0)

    r_paddle = turtle.Turtle()
    r_paddle.speed(0)
    r_paddle.shape('square')
    r_paddle.color('black')
    r_paddle.shapesize(stretch_wid=6, stretch_len=2)
    r_paddle.penup()
    r_paddle.goto(400, 0)

    # Define ball
    ball = turtle.Turtle()
    ball.speed(40)
    ball.shape('circle')
    ball.color('blue')
    ball.penup()
    ball.goto(0, 0)
    ball.dx = 5
    ball.dy = -5

    # Define scoreboard
    score_board = turtle.Turtle()
    score_board.speed(0)
    score_board.color('blue')
    score_board.penup()
    score_board.hideturtle()
    score_board.goto(0, 260)
    score_board.write('Left Player: 0 -- Right Player: 0', align='center', font=('Arial', 24, 'normal'))

    return screen, ball, l_paddle, r_paddle, score_board

Breakdown:

  • We initialize Turtle and set up the game window with dimensions (1000x600 pixels) and a white background.

  • Constants for paddle and ball properties:

    • Two paddles (l_paddle, r_paddle) are created with stretch_wid=6, stretch_len=2 to form rectangular paddles.

    • The ball is created as a small circle with an initial movement speed (dx = 5, dy = -5).

  • Scoreboard initialization:

    • The scoreboard starts with both players at zero points.

    • It updates dynamically as the game progresses.

This structure allows us to easily render the paddles, ball, and scoreboard, handling movement and scoring dynamically as the game runs!

Step 5: Handling Player Input

Now, we define functions for the controls that allow players to move their paddles using keyboard inputs.

def pong_game():
    game_components = setup_game()
    screen, ball, l_paddle, r_paddle, score_board = game_components
    l_score, r_score = 0, 0

    # Define movement functions
    def l_paddle_up():
        l_paddle.sety(l_paddle.ycor() + 20)

    def l_paddle_down():
        l_paddle.sety(l_paddle.ycor() - 20)

    def r_paddle_up():
        r_paddle.sety(r_paddle.ycor() + 20)

    def r_paddle_down():
        r_paddle.sety(r_paddle.ycor() - 20)

    # Map keyboard inputs to paddle movement
    screen.listen()
    screen.onkeypress(l_paddle_up, 'e')  # Left paddle moves up with 'e'
    screen.onkeypress(l_paddle_down, 'x')  # Left paddle moves down with 'x'
    screen.onkeypress(r_paddle_up, 'Up')  # Right paddle moves up with 'Up Arrow'
    screen.onkeypress(r_paddle_down, 'Down')  # Right paddle moves down with 'Down Arrow'

Breakdown:

  • Defines movement functions for both paddles:

    • l_paddle_up(), l_paddle_down() move the left paddle up and down.

    • r_paddle_up(), r_paddle_down() move the right paddle up and down.

  • Uses screen.listen() to start listening for keyboard inputs.

  • Maps keys to functions:

    • The left paddle is controlled with 'e' (up) and 'x' (down).

    • The right paddle is controlled with 'Up' and 'Down' arrow keys.

This setup allows players to move their paddles smoothly, ensuring responsive gameplay!

Step 6: Game Loop and Ball Movement

The game while loop is responsible for updating the game state, moving the ball, checking for collisions, and tracking scores.

    while True:
        screen.update()
        ball.setx(ball.xcor() + ball.dx)
        ball.sety(ball.ycor() + ball.dy)

        if ball.ycor() > 280 or ball.ycor() < -280:
            ball.dy *= -1

        if ball.xcor() > 500 or ball.xcor() < -500:
            ball.goto(0, 0)
            ball.dy *= -1

        if ((ball.xcor() > 360 and ball.xcor() < 370 and ball.ycor() < r_paddle.ycor() + 40 and ball.ycor() > r_paddle.ycor() - 40) or
            (ball.xcor() < -360 and ball.xcor() > -370 and ball.ycor() < l_paddle.ycor() + 40 and ball.ycor() > l_paddle.ycor() - 40)):
            ball.dx *= -1

Breakdown:

  • Moves the ball by updating its x and y coordinates.

  • Bounces off walls by reversing dy when hitting the top or bottom.

  • Resets the ball when a player misses.

  • Detects paddle collisions and reverses direction accordingly.

This loop ensures smooth gameplay by continuously updating the game state!

Step 7: Running the Game

Now that we have set up the game window, handled player input, and implemented ball movement, we can run the game by calling the pong_game() function inside the main block.

if __name__ == "__main__":
    pong_game()

Breakdown:

  • Ensures the script runs independently: The if __name__ == "__main__" condition ensures that the game only runs when the script is executed directly, not when it is imported as a module in another program.

  • Calls the pong_game() function: This function initializes the game components and enters the main game loop, where player input, ball movement, and scoring are handled in real time.

Final Code: Pong Arcade Game

import turtle


def update_score(l_score, r_score, player, score_board):
   if player == 'l':
       l_score += 1
   else:
       r_score += 1

   score_board.clear()
   score_board.write('Left Player: {} -- Right Player: {}'.format(
       l_score, r_score), align='center',
       font=('Arial', 24, 'normal'))
   return l_score, r_score, score_board


def setup_game():
   screen = turtle.Screen()
   screen.title('Pong Arcade Game')
   screen.bgcolor('white')
   screen.setup(width=1000, height=600)

   l_paddle = turtle.Turtle()
   l_paddle.speed(0)
   l_paddle.shape('square')
   l_paddle.color('red')
   l_paddle.shapesize(stretch_wid=6, stretch_len=2)
   l_paddle.penup()
   l_paddle.goto(-400, 0)

   r_paddle = turtle.Turtle()
   r_paddle.speed(0)
   r_paddle.shape('square')
   r_paddle.color('black')
   r_paddle.shapesize(stretch_wid=6, stretch_len=2)
   r_paddle.penup()
   r_paddle.goto(400, 0)

   ball = turtle.Turtle()
   ball.speed(40)
   ball.shape('circle')
   ball.color('blue')
   ball.penup()
   ball.goto(0, 0)
   ball.dx = 5
   ball.dy = -5

   score_board = turtle.Turtle()
   score_board.speed(0)
   score_board.color('blue')
   score_board.penup()
   score_board.hideturtle()
   score_board.goto(0, 260)
   score_board.write('Left Player: 0 -- Right Player: 0',
                     align='center', font=('Arial', 24, 'normal'))

   return screen, ball, l_paddle, r_paddle, score_board


def pong_game():
   game_components = setup_game()
   screen = game_components[0]
   ball = game_components[1]
   l_paddle = game_components[2]
   r_paddle = game_components[3]
   score_board = game_components[4]
   l_score = 0
   r_score = 0

   def l_paddle_up():
       l_paddle.sety(l_paddle.ycor() + 20)

   def l_paddle_down():
       l_paddle.sety(l_paddle.ycor() - 20)

   def r_paddle_up():
       r_paddle.sety(r_paddle.ycor() + 20)

   def r_paddle_down():
       r_paddle.sety(r_paddle.ycor() - 20)

   screen.listen()
   screen.onkeypress(l_paddle_up, 'e')
   screen.onkeypress(l_paddle_down, 'x')
   screen.onkeypress(r_paddle_up, 'Up')
   screen.onkeypress(r_paddle_down, 'Down')

   while True:
       screen.update()
       ball.setx(ball.xcor()+ball.dx)
       ball.sety(ball.ycor()+ball.dy)

       if ball.ycor() > 280:
           ball.sety(280)
           ball.dy *= -1

       if ball.ycor() < -280:
           ball.sety(-280)
           ball.dy *= -1

       if ball.xcor() > 500:
           ball.goto(0, 0)
           ball.dy *= -1
           l_score, r_score, score_board = update_score(
               l_score, r_score, 'l', score_board)
           continue

       elif ball.xcor() < -500:
           ball.goto(0, 0)
           ball.dy *= -1
           l_score, r_score, score_board = update_score(
               l_score, r_score, 'r', score_board)
           continue

       if ((ball.xcor() > 360) and
           (ball.xcor() < 370) and
           (ball.ycor() < r_paddle.ycor()+40) and
               (ball.ycor() > r_paddle.ycor()-40)):
           ball.setx(360)
           ball.dx *= -1

       if ((ball.xcor() < -360) and
               (ball.xcor() > -370) and
               (ball.ycor() < l_paddle.ycor()+40) and
               (ball.ycor() > l_paddle.ycor()-40)):
           ball.setx(-360)
           ball.dx *= -1


if __name__ == '__main__':
   pong_game()

Wrapping Up

Congratulations! You’ve built a Pong Arcade Game using Python and Turtle. This project introduced you to key game development concepts such as handling user input, managing game state, rendering graphics, and implementing real-time movement mechanics.

Next Steps:

  • Enhance Gameplay: Increase the ball speed after every few hits to make the game more challenging.

  • Add Sound Effects: Play a sound whenever the ball hits a paddle or scores a point.

  • Improve Animations: Implement smoother paddle and ball movements using sprite-based animations.

  • Track High Scores: Implement a scoring system that saves the player’s best runs.

  • Introduce AI Opponent: Add a single-player mode where an AI-controlled paddle competes against the player.

By expanding on this foundation, you can create a more engaging and challenging Pong experience. Keep coding, and have fun building games!

Happy coding!

By Robert Johns

Technical Editor for Hackr.io | 15+ Years in Python, Java, SQL, C++, C#, JavaScript, Ruby, PHP, .NET, MATLAB, HTML & CSS, and more... 10+ Years in Networking, Cloud, APIs, Linux | 5+ Years in Data Science | 2x PhDs in Structural & Blast Engineering

View all post by the author

Subscribe to our Newsletter for Articles, News, & Jobs.

I accept the Terms and Conditions.

Disclosure: Hackr.io is supported by its audience. When you purchase through links on our site, we may earn an affiliate commission.

In this article

Learn More

Please login to leave comments