Vibe Coding Pong with Python and pygame

Pong is one of the first computer games ever created, way back in 1972. If you have never heard of Pong, you can think of it as a kind of “tennis” game. There are two paddles, on each side of the screen. They move up and down. The goal is to bounce a ball between the two paddles. If you miss the ball, your opponent scores a point.

Atari created Pong and put it in arcades. The name of the game is derived from the game of ping-pong. Pong has been remade many times, with different features and gameplay elements added or removed.

Want to know more? Get the Book!

You will use Anthropic’s Claude AI to create this game in Python using the pygame package.

Let’s get started!

Creating the Game

Prompt: Please create the pong videogame using Python and pygame. Give the user a way to start a new game and keep score.

I’ll create a classic Pong game for you using Python and pygame. This will include two paddles, a ball, scoring, and the ability to start new games.

import pygame
import sys
import random

# Initialize Pygame
pygame.init()

# Constants
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
PADDLE_WIDTH = 15
PADDLE_HEIGHT = 90
BALL_SIZE = 15
PADDLE_SPEED = 7
BALL_SPEED_X = 6
BALL_SPEED_Y = 6

# Colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (128, 128, 128)

class Paddle:
    def __init__(self, x, y):
        self.rect = pygame.Rect(
            x, y, PADDLE_WIDTH, PADDLE_HEIGHT)
        self.speed = PADDLE_SPEED
    
    def move_up(self):
        if self.rect.top > 0:
            self.rect.y -= self.speed
    
    def move_down(self):
        if self.rect.bottom < SCREEN_HEIGHT:
            self.rect.y += self.speed
    
    def draw(self, screen):
        pygame.draw.rect(screen, WHITE, self.rect)

class Ball:
    def __init__(self):
        self.rect = pygame.Rect(
            SCREEN_WIDTH // 2, 
            SCREEN_HEIGHT // 2, 
            BALL_SIZE,
            BALL_SIZE)
        self.reset_ball()
    
    def reset_ball(self):
        self.rect.center = (SCREEN_WIDTH // 2, 
            SCREEN_HEIGHT // 2)
        self.speed_x = BALL_SPEED_X * random.choice([-1, 1])
        self.speed_y = BALL_SPEED_Y * random.choice([-1, 1])
    
    def move(self):
        self.rect.x += self.speed_x
        self.rect.y += self.speed_y
        
        # Bounce off top and bottom walls
        if (self.rect.top <= 0 
            or self.rect.bottom >= SCREEN_HEIGHT):
            self.speed_y = -self.speed_y
    
    def draw(self, screen):
        pygame.draw.rect(screen, WHITE, self.rect)

class PongGame:
    def __init__(self):
        self.screen = pygame.display.set_mode(
            (SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("Pong Game")
        self.clock = pygame.time.Clock()
        
        # Create game objects
        self.left_paddle = Paddle(
            30, SCREEN_HEIGHT // 2 - PADDLE_HEIGHT // 2)
        self.right_paddle = Paddle(
            SCREEN_WIDTH - 30 - PADDLE_WIDTH,
            SCREEN_HEIGHT // 2 - PADDLE_HEIGHT // 2)
        self.ball = Ball()
        
        # Scores
        self.left_score = 0
        self.right_score = 0
        self.font = pygame.font.Font(None, 74)
        self.small_font = pygame.font.Font(None, 36)
        
        # Game state
        self.game_state = "menu"  # "menu", "playing", "paused"
    
    def handle_collision(self):
        # Ball collision with paddles
        if self.ball.rect.colliderect(self.left_paddle.rect):
            # Only bounce if moving toward paddle
            if self.ball.speed_x < 0:  
                self.ball.speed_x = -self.ball.speed_x
                # Add some variation based on where ball hits paddle
                hit_pos = (self.ball.rect.centery - self.left_paddle.rect.centery
                          ) / (PADDLE_HEIGHT / 2)
                self.ball.speed_y += hit_pos * 2
        
        if self.ball.rect.colliderect(self.right_paddle.rect):
            # Only bounce if moving toward paddle
            if self.ball.speed_x > 0:  
                self.ball.speed_x = -self.ball.speed_x
                # Add some variation based on where ball hits paddle
                hit_pos = (self.ball.rect.centery - self.right_paddle.rect.centery
                          ) / (PADDLE_HEIGHT / 2)
                self.ball.speed_y += hit_pos * 2
        
        # Limit ball speed
        max_speed = 12
        if abs(self.ball.speed_y) > max_speed:
            self.ball.speed_y = max_speed if self.ball.speed_y > 0 else -max_speed
    
    def check_scoring(self):
        # Check if ball went off screen
        if self.ball.rect.left <= 0:
            self.right_score += 1
            self.ball.reset_ball()
        elif self.ball.rect.right >= SCREEN_WIDTH:
            self.left_score += 1
            self.ball.reset_ball()
    
    def handle_input(self):
        keys = pygame.key.get_pressed()
        
        if self.game_state == "playing":
            # Left paddle controls (W/S)
            if keys[pygame.K_w]:
                self.left_paddle.move_up()
            if keys[pygame.K_s]:
                self.left_paddle.move_down()
            
            # Right paddle controls (UP/DOWN arrows)
            if keys[pygame.K_UP]:
                self.right_paddle.move_up()
            if keys[pygame.K_DOWN]:
                self.right_paddle.move_down()
    
    def draw_menu(self):
        self.screen.fill(BLACK)
        
        title_text = self.font.render("PONG", True, WHITE)
        title_rect = title_text.get_rect(
            center=(SCREEN_WIDTH // 2, 150))
        self.screen.blit(title_text, title_rect)
        
        start_text = self.small_font.render(
            "Press SPACE to Start", True, WHITE)
        start_rect = start_text.get_rect(
            center=(SCREEN_WIDTH // 2, 250))
        self.screen.blit(start_text, start_rect)
        
        controls_text = [
            "Controls:",
            "Left Player: W (Up) / S (Down)",
            "Right Player: Arrow Keys",
            "Press R to restart during game",
            "Press ESC to return to menu"
        ]
        
        for i, text in enumerate(controls_text):
            rendered_text = self.small_font.render(
                text, True, GRAY if i == 0 else WHITE)
            text_rect = rendered_text.get_rect(
                center=(SCREEN_WIDTH // 2, 320 + i * 40))
            self.screen.blit(rendered_text, text_rect)
    
    def draw_game(self):
        self.screen.fill(BLACK)
        
        # Draw center line
        for i in range(0, SCREEN_HEIGHT, 20):
            if i % 40 == 0:
                pygame.draw.rect(
                    self.screen, 
                    WHITE, 
                    (SCREEN_WIDTH // 2 - 2, i, 4, 10))
        
        # Draw paddles and ball
        self.left_paddle.draw(self.screen)
        self.right_paddle.draw(self.screen)
        self.ball.draw(self.screen)
        
        # Draw scores
        left_score_text = self.font.render(
            str(self.left_score), True, WHITE)
        right_score_text = self.font.render(
            str(self.right_score), True, WHITE)
        
        self.screen.blit(left_score_text, (SCREEN_WIDTH // 4, 50))
        self.screen.blit(
            right_score_text, 
            (3 * SCREEN_WIDTH // 4 - right_score_text.get_width(),
             50))
        
        # Draw instructions
        instruction_text = self.small_font.render(
            "Press ESC for menu, R to restart", True, GRAY)
        instruction_rect = instruction_text.get_rect(
            center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT - 30))
        self.screen.blit(instruction_text, instruction_rect)
    
    def reset_game(self):
        self.left_score = 0
        self.right_score = 0
        self.ball.reset_ball()
        self.left_paddle.rect.y = (SCREEN_HEIGHT // 2 - 
            PADDLE_HEIGHT // 2)
        self.right_paddle.rect.y = (SCREEN_HEIGHT // 2 - 
            PADDLE_HEIGHT // 2)
    
    def run(self):
        running = True
        
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                
                if event.type == pygame.KEYDOWN:
                    if (event.key == pygame.K_SPACE 
                        and self.game_state == "menu"):
                        self.game_state = "playing"
                        self.reset_game()
                    elif event.key == pygame.K_ESCAPE:
                        self.game_state = "menu"
                    elif (event.key == pygame.K_r 
                        and self.game_state == "playing"):
                        self.reset_game()
            
            if self.game_state == "menu":
                self.draw_menu()
            elif self.game_state == "playing":
                self.handle_input()
                self.ball.move()
                self.handle_collision()
                self.check_scoring()
                self.draw_game()
            
            pygame.display.flip()
            self.clock.tick(60)
        
        pygame.quit()
        sys.exit()

if __name__ == "__main__":
    game = PongGame()
    game.run()

I’ve created a complete Pong game in Python using pygame! Here are the key features:

Game Features:

  • Classic Pong gameplay with two paddles and a bouncing ball
  • Score tracking for both players
  • Menu system with game start option
  • Ability to restart games and return to menu

Controls:

  • Left Player: W (up) / S (down)
  • Right Player: Arrow keys (up/down)
  • SPACE: Start new game from menu
  • R: Restart current game
  • ESC: Return to main menu

Game Mechanics:

  • Ball bounces off top/bottom walls and paddles
  • Ball speed varies slightly based on where it hits the paddle
  • Scores increase when ball goes off either side
  • Ball resets to center after each score

To run the game:

  • Make sure you have pygame installed: pip install pygame
  • Save the code to a .py file (e.g., pong.py)
  • Run it with: python pong.py

The game includes a clean menu system, smooth 60 FPS gameplay, and responsive controls. The ball physics include some randomization to keep gameplay interesting, and the paddle collisions add a bit of strategy by allowing players to influence the ball’s trajectory based on where they hit it.

Prompt: Can you explain how the code works?

I’ll break down how the Pong game code works, explaining each major component and how they work together.

Code Structure Overview

 

The code is organized using object-oriented programming with three main classes:

  • Paddle – represents the player paddles
  • Ball – handles the bouncing ball
  • PongGame – manages the overall game logic and flow

1. Initialization and Constants

pygame.init()
# Constants define game dimensions and speeds
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
PADDLE_SPEED = 7

The game starts by initializing pygame and defining constants for screen size, object dimensions, and movement speeds. Using constants makes the code easier to modify and maintain.

2. The Paddle Class

class Paddle:
    def __init__(self, x, y):
        self.rect = pygame.Rect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT)

Each paddle is represented as a pygame.Rect object, which provides built-in collision detection and positioning. The paddle has methods to:

  • move_up() and move_down() – handle movement with boundary checking
  • draw() – renders the paddle as a white rectangle

The boundary checking ensures paddles can’t move off-screen:

if self.rect.top > 0:  # Don't go above screen
if self.rect.bottom < SCREEN_HEIGHT:  # Don't go below screen

3. The Ball Class

class Ball:
    def reset_ball(self):
        self.speed_x = BALL_SPEED_X * random.choice([-1, 1])
        self.speed_y = BALL_SPEED_Y * random.choice([-1, 1])

The ball maintains its position and velocity. Key features:

  • reset_ball() – centers the ball and gives it a random direction
  • move() – updates position and handles wall bouncing
  • Wall collision detection flips the Y velocity: self.speed_y = -self.speed_y

4. Main Game Class Structure

 

The PongGame class manages everything:

Game States

self.game_state = "menu"  # "menu", "playing", "paused"

The game uses states to control what’s displayed and what input is accepted.

Game Loop

def run(self):
    while running:
        # Handle events (keyboard, quit)
        # Update game logic based on current state
        # Draw everything
        # Control frame rate (60 FPS)

5. Collision Detection

 

The most complex part handles ball-paddle collisions:

def handle_collision(self):
    if self.ball.rect.colliderect(self.left_paddle.rect):
        # Only bounce if moving toward paddle
        if self.ball.speed_x < 0:  
            self.ball.speed_x = -self.ball.speed_x

Key points:

  • Uses pygame’s built-in colliderect() for collision detection
  • Checks ball direction to prevent multiple bounces
  • Adds spin based on where the ball hits the paddle:
hit_pos = (self.ball.rect.centery - 
           self.left_paddle.rect.centery
           ) / (PADDLE_HEIGHT / 2)
self.ball.speed_y += hit_pos * 2

6. Input Handling

def handle_input(self):
    keys = pygame.key.get_pressed()
    if keys[pygame.K_w]:
        self.left_paddle.move_up()

The game checks which keys are currently pressed each frame, allowing for smooth continuous movement. Different keys are handled based on the game state.

7. Scoring System

def check_scoring(self):
    if self.ball.rect.left <= 0:  # Ball went off left side
        self.right_score += 1
        self.ball.reset_ball()

Scoring is checked every frame by seeing if the ball has moved past the screen edges.

8. Rendering System

The game uses different drawing functions for different states:

  • draw_menu() – shows title and instructions
  • draw_game() – renders paddles, ball, scores, and game elements

The center dashed line is drawn using a loop:

for i in range(0, SCREEN_HEIGHT, 20):
    if i % 40 == 0:  # Only draw every other dash
        pygame.draw.rect(
            self.screen, 
            WHITE, 
            (SCREEN_WIDTH // 2 - 2, i, 4, 10))

9. Game Flow

  1. Initialization: Create paddles, ball, set up pygame
  2. Main Loop:
  • Process input events
  • Update game logic (if playing)
  • Draw current state
  • Maintain 60 FPS with clock.tick(60)
  1. State Management: Switch between menu and gameplay based on user input

Key Programming Concepts Used

  • Object-Oriented Design: Separate classes for different game elements
  • Event-Driven Programming: Responding to keyboard and quit events
  • Game Loop Pattern: Continuous update-draw cycle
  • State Management: Different behaviors based on current game state
  • Collision Detection: Using pygame’s rectangle collision methods
  • Vector Math: Ball movement with X/Y velocity components

The code is structured to be easily extensible – you could add features like AI opponents, power-ups, or different game modes by building on this foundation.

Running Pong

Now that you have the code and understand how to use it, you are ready to run the code. You can run your code in your Python IDE or on the command line like this:

python pong.py

When you first launch your game, you will see the following welcome screen:

Pong main screen

If you press the SPACE key, you will see the actual game, which will look something like this:

Pong game play

Grab a friend or play against yourself. The game works pretty well, although playing solo can be rather challenging.

Wrapping Up

Pong is a fun little game. Much like ping-pong or table tennis, the game is designed to be played with someone else. Technically, you could also create a computer opponent, but you usually play Pong between two people instead.

You should play the game with a friend or family member. You can also study the code to discover how it works. Then, when you are ready, you can start improving the game.

Get the Book

This article is from my latest Python book, Vibe Coding Video Games with Python.

You can get Vibe Coding Video Games with Python at the following websites: