Four complete pygame example games in under 80 lines each: catch the falling coins, Snake, Pong against the computer, and a cave flyer. Play each one on the page, read the code, and change it.
▶This page includes runnable editors. Edit, run, and see output instantly.
Four complete games, each short enough to read in a few minutes. Press Play, click the game to give it the keyboard, and play. Then change a number and play again: the best way to learn pygame is to break a working game and fix it.
Catch the coins
The arrow keys move a basket; catch coins, miss three and the game is over. It uses Rect collisions, a random spawn and a game over state.
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Catch")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 32)
def new_game():
return pygame.Rect(200, 330, 80, 16), [], 0, 3
basket, coins, score, lives = new_game()
playing = True
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE and not playing:
basket, coins, score, lives = new_game()
playing = True
if playing:
keys = pygame.key.get_pressed()
basket.x += (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 7
basket.clamp_ip(screen.get_rect())
if random.random() < 0.03:
coins.append(pygame.Rect(random.randint(0, 460), -20, 20, 20))
for coin in coins[:]:
coin.y += 4
if coin.colliderect(basket):
coins.remove(coin)
score += 1
elif coin.top > 360:
coins.remove(coin)
lives -= 1
playing = lives > 0
screen.fill((25, 30, 55))
for coin in coins:
pygame.draw.ellipse(screen, (255, 210, 60), coin)
pygame.draw.rect(screen, (120, 200, 255), basket, border_radius=6)
screen.blit(font.render(f"Score {score} Lives {lives}", True, "white"), (10, 10))
if not playing:
msg = font.render("Game over. Press Space", True, (255, 120, 120))
screen.blit(msg, msg.get_rect(center=(240, 180)))
pygame.display.flip()
clock.tick(60)
raise SystemExit ends the program from inside the loop, and the window closes with it. It saves the running flag in a short game.
Snake
The snake moves one grid cell every few frames. The body is a list of cells: each step adds a new head and removes the tail, unless the snake just ate.
import random
import pygame
pygame.init()
CELL, COLS, ROWS = 20, 24, 18
screen = pygame.display.set_mode((CELL * COLS, CELL * ROWS))
pygame.display.set_caption("Snake")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 30)
snake = [(5, 9), (4, 9), (3, 9)]
direction = (1, 0)
food = (15, 9)
frame = 0
alive = True
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
if event.type == pygame.KEYDOWN:
turns = {pygame.K_UP: (0, -1), pygame.K_DOWN: (0, 1),
pygame.K_LEFT: (-1, 0), pygame.K_RIGHT: (1, 0)}
new = turns.get(event.key)
if new and (new[0] != -direction[0] or new[1] != -direction[1]):
direction = new
frame += 1
if alive and frame % 7 == 0:
head = (snake[0][0] + direction[0], snake[0][1] + direction[1])
if head in snake or not (0 <= head[0] < COLS and 0 <= head[1] < ROWS):
alive = False
else:
snake.insert(0, head)
if head == food:
while food in snake:
food = (random.randrange(COLS), random.randrange(ROWS))
else:
snake.pop()
screen.fill((20, 30, 20))
pygame.draw.rect(screen, (240, 80, 80), (food[0] * CELL, food[1] * CELL, CELL, CELL), border_radius=6)
for x, y in snake:
pygame.draw.rect(screen, (110, 220, 110), (x * CELL + 1, y * CELL + 1, CELL - 2, CELL - 2), border_radius=4)
screen.blit(font.render(f"Length {len(snake)}", True, "white"), (8, 8))
if not alive:
screen.blit(font.render("Game over", True, (255, 140, 140)), (190, 170))
pygame.display.flip()
clock.tick(60)
The check new[0] != -direction[0] or new[1] != -direction[1] stops the snake from turning straight back into itself. frame % 7 sets the speed: a smaller number is a faster snake.
Pong against the computer
You play the left paddle with Up and Down; the computer follows the ball, a little slower than it could.
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 320))
pygame.display.set_caption("Pong")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 48)
player = pygame.Rect(16, 130, 10, 60)
cpu = pygame.Rect(454, 130, 10, 60)
ball = pygame.Rect(234, 154, 12, 12)
velocity = pygame.Vector2(4, 3)
score = [0, 0]
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
keys = pygame.key.get_pressed()
player.y += (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 6
player.clamp_ip(screen.get_rect())
if cpu.centery < ball.centery - 10:
cpu.y += 4
elif cpu.centery > ball.centery + 10:
cpu.y -= 4
cpu.clamp_ip(screen.get_rect())
ball.x += velocity.x
ball.y += velocity.y
if ball.top <= 0 or ball.bottom >= 320:
velocity.y = -velocity.y
if ball.colliderect(player) and velocity.x < 0:
velocity.x = -velocity.x * 1.05
if ball.colliderect(cpu) and velocity.x > 0:
velocity.x = -velocity.x * 1.05
if ball.right < 0 or ball.left > 480:
score[ball.left > 480] += 1
ball.center = (240, 160)
velocity.update(4 if ball.left > 480 else -4, 3)
screen.fill((15, 15, 25))
pygame.draw.line(screen, (60, 60, 80), (240, 0), (240, 320), 2)
for paddle in (player, cpu):
pygame.draw.rect(screen, "white", paddle)
pygame.draw.ellipse(screen, (255, 220, 90), ball)
screen.blit(font.render(f"{score[0]} {score[1]}", True, "white"), (196, 10))
pygame.display.flip()
clock.tick(60)
Each hit speeds the ball up by 5%. score[ball.left > 480] uses the comparison as an index: False is 0 and True is 1.
Cave flyer
Space lifts the drone; gravity pulls it down; rock columns slide past with a gap to fly through.
import random
import pygame
pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Cave Flyer")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 40)
drone = pygame.Rect(100, 160, 26, 20)
speed = 0.0
rocks = []
frame = 0
score = 0
alive = True
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
raise SystemExit
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
if alive:
speed = -6.5
else:
drone.y, speed, rocks, score, alive = 160, 0.0, [], 0, True
if alive:
frame += 1
speed += 0.35
drone.y += int(speed)
if frame % 90 == 0:
gap = random.randint(80, 260)
rocks.append(pygame.Rect(480, 0, 50, gap - 60))
rocks.append(pygame.Rect(480, gap + 60, 50, 360))
for rock in rocks:
rock.x -= 3
if rocks and rocks[0].right < 0:
rocks = rocks[2:]
score += 1
if drone.collidelist(rocks) != -1 or not 0 < drone.centery < 360:
alive = False
screen.fill((120, 190, 240))
for rock in rocks:
pygame.draw.rect(screen, (120, 110, 100), rock)
pygame.draw.ellipse(screen, (80, 220, 255), drone)
screen.blit(font.render(str(score), True, "white"), (230, 16))
if not alive:
msg = font.render("Space to try again", True, (40, 40, 60))
screen.blit(msg, msg.get_rect(center=(240, 180)))
pygame.display.flip()
clock.tick(60)
drone.collidelist(rocks) returns the index of a rock the drone touches, or -1 when it touches none, so one line checks every rock.
Turn the objects into sprites once there are many kinds.
The pygame playground has more example games and a larger editor for your own.
Frequently Asked Questions
What is a good first pygame project?
A game with one rule and one control: catching falling objects, Pong or Snake. Each fits in under 100 lines and uses the whole loop (input, update, collisions, drawing, score) without needing images.
How do I add a game over screen?
Keep a state variable ("playing" or "over"). In the update step, only move things while playing; in the draw step, show the score and a "press Space" message when over, and reset the game on Space.
Can I use these examples in my own game?
Yes. They are short starting points: copy one into your editor, run it with pygame installed, and change the rules, speeds and colors.