31. From Python to pygame
So far every program you have written runs in the terminal, prints some output, and exits. From here on, programs open a window and keep running until the user closes them. The tool for this is pygame, a Python library for making 2D games and interactive graphical programs.
What pygame is
pygame is a free, open-source library that wraps the SDL graphics and input system. It gives Python programs the ability to:
- Open a resizable window.
- Draw shapes, images, and text.
- Read keyboard and mouse input.
- Play sounds.
- Control frame rate.
You use the same Python you already know — if,
for, functions, classes — and pygame supplies the window
and the tools to fill it.
Installing pygame
Open a terminal (Command Prompt or PowerShell on Windows) and run:
pip install pygame
If that fails with a "permission denied" error, try:
pip install pygame --user
Once installed, test it:
import pygame
print(pygame.version.ver)If a version number prints (e.g., 2.5.2), the install
worked.
The minimal pygame program
Here is the smallest pygame program that opens a window, keeps it open, and closes cleanly when you click the X button.
import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("My Game")
clock = pygame.time.Clock()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((0, 0, 0))
pygame.display.flip()
clock.tick(60)
pygame.quit()Run it with:
python exercises/31/01-minimal-window.py
A black 800x600 window should appear. Close it with the X button.
What each line does
pygame.init()
Starts all pygame subsystems — display, input, sound. Call this once before anything else.
screen = pygame.display.set_mode((800, 600))
Opens a window 800 pixels wide and 600 pixels tall. The return value
is a Surface object — think of it as a blank canvas you
can draw on. The argument is a tuple (width, height), so
the double parentheses ((800, 600)) are: the outer ones are
the function call, the inner ones create the tuple.
pygame.display.set_caption("My Game")
Sets the title bar text. Change this to whatever your game is called.
clock = pygame.time.Clock()
Creates a clock object used to control frame rate. You will call
clock.tick(60) at the end of every frame.
running = True and
while running:
The game loop. This loop runs once per frame,
thousands of times per second if unconstrained. Setting
running = False exits the loop and ends the program.
for event in pygame.event.get():
Every input event that happened since the last frame — key presses, mouse clicks, window events — is collected here. You loop through them and react to whichever ones matter.
if event.type == pygame.QUIT:
pygame.QUIT fires when the user clicks the X button.
Setting running = False tells the loop to stop after this
frame.
screen.fill((0, 0, 0))
Paints the entire canvas black. The argument is an RGB colour tuple:
(red, green, blue), each 0-255. This clears whatever was
drawn last frame, so the new frame starts with a clean slate. Change the
tuple to change the background colour.
pygame.display.flip()
pygame draws to an off-screen buffer first. flip()
copies that buffer to the actual window so the player sees it. Without
flip(), the window stays blank. Call this once at the end
of every frame, after all drawing.
clock.tick(60)
Limits the loop to 60 frames per second. Without this, the loop runs
at full CPU speed — thousands of frames per second on a modern machine —
which wastes power and makes movement speed unpredictable.
60 is the target; on a slow machine, the loop will run
slower, but it will never run faster than 60 fps.
pygame.quit()
Shuts down pygame after the loop exits. On some platforms, skipping this leaves the window open or causes errors.
The game loop pattern
Every pygame program follows the same four-step cycle each frame:
1. Process events (read keyboard, mouse, window events)
2. Update state (move things, check collisions, update score)
3. Draw (fill background, draw all objects)
4. Flip (show the frame; throttle frame rate)
Repeat until running = False.
This is the fundamental difference between a game and a terminal script:
- A terminal script runs top to bottom once, then exits.
- A game loop runs the same four steps over and over — 60 times per second — updating the world a tiny bit each time.
All the interesting game logic goes in steps 2 and 3.
Changing the window size
To get a different window size, change the tuple passed to
set_mode:
screen = pygame.display.set_mode((1024, 768)) # larger
screen = pygame.display.set_mode((400, 300)) # smallerThe width and height are in pixels. Common sizes: 640x480 (classic), 800x600, 1280x720 (HD).
Homework
Problem 1 — Different window size
Open exercises/31/homework/01-window-size.py. Change the
window to 1024x768. Add a caption that reads "Big Window".
Run it and confirm the window is noticeably larger.
Problem 2 — Background colour
Open exercises/31/homework/02-background-colour.py.
Change screen.fill(...) to a colour that is not black. Try
at least two different colours by running the program twice with
different values.
Problem 3 — Two fills
Open exercises/31/homework/03-two-fills.py. Inside the
game loop, call screen.fill twice before
pygame.display.flip() — first with red
(255, 0, 0), then with blue (0, 0, 255). Run
it and observe what colour the background is. Explain why in a
comment.
Challenge — Shrinking window
Open exercises/31/homework/04-shrinking-window.py. Start
with a 500x500 window. Each time the user presses the spacebar, reduce
the window size by 50 pixels in both dimensions, down to a minimum of
100x100. Use pygame.KEYDOWN and
event.key == pygame.K_SPACE to detect the key press, and
pygame.display.set_mode(...) to resize.
Stuck or finished? Open the homework solutions page.