32. Drawing on screen

With a window open and a game loop running, the next step is putting something visible on the screen. pygame provides drawing functions for rectangles, circles, lines, and polygons. All of them follow the same pattern: tell pygame which surface to draw on, what colour to use, and where to put it.

The coordinate system

pygame uses a coordinate system where (0, 0) is the top-left corner of the window. The x-axis increases to the right and the y-axis increases downward. This is common in 2D graphics but opposite to the maths convention where y increases upward.

(0,0) ──────────────────► x
  │
  │
  │
  ▼
  y

For an 800x600 window:

  • Top-left corner: (0, 0)
  • Top-right corner: (799, 0)
  • Bottom-left corner: (0, 599)
  • Bottom-right corner: (799, 599)
  • Centre: (400, 300)

When you position a shape, you are giving pixel coordinates in this system.

Colours as RGB tuples

Every colour in pygame is a tuple of three integers: (red, green, blue). Each component ranges from 0 (none) to 255 (full).

Colour Tuple
Black (0, 0, 0)
White (255, 255, 255)
Red (255, 0, 0)
Green (0, 255, 0)
Blue (0, 0, 255)
Yellow (255, 255, 0)
Cyan (0, 255, 255)
Magenta (255, 0, 255)
Orange (255, 165, 0)
Dark grey (64, 64, 64)

Mix components to get any colour you want. (200, 100, 50) is a brownish orange; (30, 30, 80) is a dark navy.

Store colours in variables at the top of your file to avoid repeating the tuple everywhere:

RED   = (255, 0, 0)
GREEN = (0, 200, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
SKY   = (135, 206, 235)

Drawing functions

All drawing functions live in pygame.draw. Call them inside the game loop, after screen.fill(...) (which clears the canvas) and before pygame.display.flip() (which shows the frame).

Rectangle

pygame.draw.rect(screen, color, (x, y, width, height))
  • x, y — position of the top-left corner of the rectangle.
  • width, height — size in pixels.
pygame.draw.rect(screen, (255, 0, 0), (100, 150, 200, 80))

Draws a red rectangle with its top-left corner at (100, 150), 200 pixels wide and 80 pixels tall.

Add a fifth argument for the line thickness. Thickness 0 (the default) fills the shape; any positive value draws only the outline:

pygame.draw.rect(screen, (255, 255, 255), (100, 150, 200, 80), 3)

Circle

pygame.draw.circle(screen, color, (center_x, center_y), radius)
  • center_x, center_y — position of the centre.
  • radius — radius in pixels.
pygame.draw.circle(screen, (255, 255, 0), (400, 300), 50)

Draws a yellow filled circle centred at (400, 300) with radius 50. Same as rectangles, an optional thickness argument draws an outline only.

Line

pygame.draw.line(screen, color, (x1, y1), (x2, y2), thickness)
pygame.draw.line(screen, (255, 255, 255), (0, 0), (799, 599), 2)

Draws a white line from the top-left to the bottom-right corner, 2 pixels thick.

Polygon

pygame.draw.polygon(screen, color, [(x1,y1), (x2,y2), (x3,y3), ...])

Takes a list of vertex tuples and draws a filled shape connecting them in order. Good for triangles:

pygame.draw.polygon(screen, (200, 100, 50), [(400, 100), (300, 250), (500, 250)])

This draws a triangle pointing upward.

Drawing order matters

pygame draws shapes in the order you call the draw functions. A shape drawn later appears on top of a shape drawn earlier. There is no z-layer concept — just call order.

screen.fill((0, 0, 0))

# Draw the background rectangle first
pygame.draw.rect(screen, (0, 100, 200), (50, 50, 300, 200))

# Draw the circle on top of it
pygame.draw.circle(screen, (255, 255, 0), (200, 150), 60)

The yellow circle is partially over the blue rectangle. Swap the two calls and the rectangle covers the circle.

Using variables for position and size

Hard-coding numbers directly in draw calls makes the code brittle. Store position and size in variables instead:

x = 100
y = 200
width = 60
height = 60
color = (255, 0, 0)

pygame.draw.rect(screen, color, (x, y, width, height))

Now you can move the square by changing x and y. Later chapters will update these variables every frame to make things move.

Open exercises/32/01-scene.py. Draw a simple house scene:

  • A large rectangle for the house body.
  • A triangle (polygon) for the roof above it.
  • A small rectangle for the door.
  • A yellow circle for the sun in one corner.

Adjust the coordinates and colours until the scene looks reasonable. No artistic talent required — getting the coordinates in the right order is the exercise.

A complete drawing example

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Drawing")
clock = pygame.time.Clock()

SKY    = (135, 206, 235)
GROUND = (34, 139, 34)
BROWN  = (139, 90, 43)
RED    = (200, 50, 50)
YELLOW = (255, 220, 0)

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Clear
    screen.fill(SKY)

    # Ground
    pygame.draw.rect(screen, GROUND, (0, 450, 800, 150))

    # House body
    pygame.draw.rect(screen, BROWN, (300, 280, 200, 170))

    # Roof (triangle)
    pygame.draw.polygon(screen, RED, [(280, 280), (400, 160), (520, 280)])

    # Sun
    pygame.draw.circle(screen, YELLOW, (700, 80), 50)

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Homework

Problem 1 — Flag

Open exercises/32/homework/01-flag.py. Draw a simple three-stripe flag filling most of the window. Each stripe should be a different colour. Use pygame.draw.rect for each stripe.

Problem 2 — Target

Open exercises/32/homework/02-target.py. Draw a target: three concentric circles (one inside the other, centred at the same point) in alternating colours — for example red, white, red, or any colours you choose. The outermost circle should be the largest; the innermost the smallest.

Problem 3 — Grid

Open exercises/32/homework/03-grid.py. Draw a 4x4 grid of squares covering the window. Use nested for loops to calculate each square's position. All squares can be the same colour, but draw their outlines (use a positive thickness argument) so you can see the grid lines.

Challenge — Chessboard

Open exercises/32/homework/04-chessboard.py. Draw an 8x8 chessboard. Squares at positions where (row + col) % 2 == 0 are white; the rest are dark. Use nested for loops. The board should fill the window neatly.

Stuck or finished? Open the homework solutions page.