1. What does import even do?
Think of a library as a toolbox someone else filled and shared. When you write import, you are opening that toolbox and giving yourself all the tools inside. You did not have to build the hammer; you just picked it up.
Here is the classic first example, rolling a dice. Python does not know how to be random on its own, but the random library does, so we borrow it:
import random roll = random.randint(1, 6) print("You rolled a", roll)
Watch the brackets. It is random.randint(1, 6) with two numbers: the lowest and the highest you want. A common slip is writing random.randint(1) with just one number, which crashes, because the computer has no idea where the range should stop.
Once a library is imported, you reach into it with a dot: the library name, a dot, then the tool you want. So random.randint means "the randint tool from the random toolbox".
2. Batteries included
Python comes with a big pile of libraries already built in. Nothing to download; they are just there. People call this the standard library, or "batteries included". Here are three you will use all the time. Press Run on any of them (Python loads the first time, then it is instant).
random · surprises and shuffling
import random # pick a random whole number from 1 to 6 print("Dice:", random.randint(1, 6)) # pick one item out of a list fruits = ["apple", "banana", "cherry"] print("Snack:", random.choice(fruits))
math · the tricky sums
import math print(math.sqrt(144)) # square root: 12.0 print(math.pi) # the number pi print(round(math.pi, 2)) # 3.14
Three ways to import
They all do a similar job; pick whichever reads best:
import random # then use random.randint(1, 6) from random import randint # then just use randint(1, 6) import random as r # then use r.randint(1, 6)
The first is the safest to read, because random.randint tells you exactly where randint came from. That matters more than you would think once a program gets long.
3. Bigger libraries you install
Not every library ships with Python. Some are made by other programmers around the world and you have to install them first, download them onto your computer, before you can import them. The tool that fetches libraries is called pip, and on a real computer you would type something like this into a terminal:
pip install pygame
After that finishes, import pygame would work in your programs. There are thousands of libraries out there: for drawing charts, reading spreadsheets, controlling robots, building websites, and, yes, making games.
4. pygame, the famous game library
pygame is probably the best known way to make games in Python. It is a real, powerful library that people use to build actual desktop games: platformers, shooters, puzzles, the lot. A pygame program opens a window on your computer and looks a bit like this:
import pygame pygame.init() screen = pygame.display.set_mode((480, 360)) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() # ... draw everything, then flip the screen ... pygame.display.flip()
You can already spot the shape of a game here: open a window, then loop forever, checking for input and redrawing the screen each time around. Hold onto that idea, because our library uses exactly the same shape.
import pygame on this website?
Two reasons, and both are actually good news about how browsers work:
- Browsers are locked down on purpose. A web page is not allowed to install software or pop open windows on your computer. That is a safety feature: you would not want any old website doing that. But it means pygame, which needs to open a real desktop window, has nowhere to go.
- pygame is big and built for desktops. It is megabytes of code that expects a proper operating system underneath it, not a web page.
So instead of forcing a giant desktop library into a place it does not fit, we built a tiny one that speaks the browser's own language.
5. Meet import game, our Hallam library
A browser already knows how to draw shapes and pictures, on something called a canvas, and it already knows when you press a key. So we wrote a small library, just for this website, that hands those powers to you in plain, friendly Python. We called it game.
It is not a cut-down copy of pygame; it is our own little thing. But it teaches the very same ideas, a window, sprites, a game loop, reading the keyboard, spotting collisions, so if you ever move on to pygame or another engine later, it will all feel familiar.
- No install. Just type
import gamein the Sandbox and go. - Ready-made art, or any emoji. Ten built-in Hallam sprites give your game a matching look (see the table below), and every emoji works too. 🚀 👾 take your pick.
- It runs in Chrome or Edge. The trick that keeps the game moving without freezing the tab needs those browsers, the same as
input()andtime.sleep()do here.
6. Your first game, line by line
Here is a whole tiny game: a chicken you steer with the arrow keys. Read the comments, they explain every new piece.
import game # 1. Open the game window (width, height) game.window(480, 360) # 2. Make a sprite: an emoji at a starting spot chicken = game.sprite("🐔", 240, 300, size=48) # 3. The game loop: read keys, move, draw one frame, repeat while game.playing(): if game.pressed("left"): chicken.x = chicken.x - 6 if game.pressed("right"): chicken.x = chicken.x + 6 if game.pressed("up"): chicken.y = chicken.y - 6 if game.pressed("down"): chicken.y = chicken.y + 6 game.frame()
The three steps are always the same: open a window, make your sprites, then loop. Inside the loop you read the keys, change where things are, and call game.frame() to draw one picture and pause a heartbeat before the next one. That is what makes it look like it is moving.
The golden rule of the loop: every game loop ends with game.frame(). Forget it and the loop spins invisibly forever without ever drawing. Press Stop any time to end a game.
7. The game cheat sheet
Everything the game library can do, in one place. Tap a heading to open it.
Starting up
| Call | What it does |
|---|---|
game.window(w, h) |
Opens the game window, w wide and h tall. Always call this first. Add background="#0b1020" to set the colour. |
game.background(color) |
Changes the background colour at any time, e.g. game.background("skyblue"). |
Things you can put on screen
| Call | What it does |
|---|---|
game.sprite(skin, x, y, size=40) |
A sprite at (x, y). The skin can be a built-in art number (0 chicken, 1 dog, 2 bird, 3 egg, 4 coin, 5 basket), its name like "chicken", or any emoji: game.sprite("🚀", 100, 200). |
game.box(x, y, w, h, color) |
A coloured rectangle, w by h, centred on (x, y). Great for platforms, walls or a plain coloured player. |
game.label(text, x, y, size, color, background) |
Draws words on the screen, centred on (x, y). This is how you show a score or a message. color sets the text colour; the optional background draws a filled box behind the words so they stay readable on any scene, e.g. game.label("Score: 0", 80, 24, color="#ffffff", background="#000000"). Keep x away from the left edge (say 60 or more) so longer text is not cut off. |
The game loop
| Call | What it does |
|---|---|
game.playing() |
Is the game still running? Use it as the loop condition: while game.playing(): |
game.frame(fps=30) |
Draws one frame and waits a moment. Put it at the end of the loop. fps is how many frames a second (higher is faster and smoother). |
game.game_over(message) |
Shows a big banner and stops the game. |
game.hide_cursor() |
Hides the real mouse pointer while it is over the game window, so a sprite can be the pointer instead (a watering can, a crosshair). game.show_cursor() brings it back. |
game.debug(True) |
Draws a red outline around every sprite's hit box: the exact area .touches() checks for a collision. Put it right after game.window(...). If a collision fires when things only look close, this shows why. The box is a square around the centre and does not spin or stretch with the sprite, so a rotated car keeps a straight box. Turn it off with game.debug(False). |
Input and score
| Call | What it does |
|---|---|
game.pressed(key) |
True while a key is held down. Keys: "left", "right", "up", "down", "space", or any letter like "a" or "w". |
game.mouse_x() game.mouse_y() |
Where the mouse pointer is, in the same coordinates as your sprites. Make something follow the mouse: pet.x = game.mouse_x(). (game.mouse_in() is True while the pointer is over the game window.) |
game.clicked() |
True once per fresh click on the game window, then False until the next click. One tap, one action: perfect for buttons and menus. Pair it with .at_mouse() to click on a sprite. |
game.mouse_down() |
True while the mouse button is held, like pressed() but for the mouse. |
game.score(points) |
A running total counter. game.score(1) adds one and gives back the new total; game.score() just reads it. It draws nothing on its own, show it with a label (see below). |
game.submit_score(points) |
Gives your game its own leaderboard in the Game Gallery. When someone plays your published game, this saves their best score to it. Call it when the run ends, right before game.game_over(...). In the Sandbox it quietly does nothing, so you can test without filling the board. |
Every sprite, box and label can...
| Property | What it does |
|---|---|
.x .y | Where it is. Change these to move it. |
.size | How big an emoji or label is. |
.w .h | The width and height of a box. |
.content | What it shows. Set it to a skin number, a name or an emoji to swap the picture: player.content = "coin". On a label it is the words shown. |
.angle | Spin it. The angle is in degrees, so coin.angle = 45 tips it over, and adding a little every frame makes it spin. |
.scale_x .scale_y | Stretch or mirror it. 1 is normal, 2 is twice as big, and -1 flips it. Set .scale_x = -1 to make a sprite face the other way, handy for a side-scroller or a fighter. |
.color | The colour of a box or label. (Emoji keep their own colours, so this does nothing to an emoji.) |
.layer | Which things draw on top. A higher number sits above a lower one; things on the same layer keep the order you made them in. Everything starts at 0, except labels, which start at 1000 so a scoreboard stays on top of the action. Want a sprite over the text? Give it a bigger number, like player.layer = 2000. Want the label behind instead? Set board.layer = 0. |
.touches(other) | True if this sprite is overlapping another one. This is how you catch, hit or collide: if basket.touches(egg):. The collision box turns with the sprite's .angle and stretches with its scale, so a rotated car checks a rotated box. Turn on game.debug(True) to see the boxes. |
.hitbox | The size of the collision box, as (width, height). By default it is picked to roughly fit the art (a car is wide, an egg is tall), but you can set your own: car.hitbox = (46, 26) for a tighter fit, or car.hitbox = None to go back to automatic. Pair it with game.debug(True) to line it up. |
.at_mouse() | True while the mouse pointer is inside this sprite's box. Click on a sprite: if plant.at_mouse() and game.clicked(): |
.hide() .show() | Make it vanish or come back. |
.remove() | Delete it for good once it is caught or destroyed. Keep many objects in a list and remove them as the player clears them. |
Coordinates. The top-left corner is (0, 0). x grows to the right, and y grows downward. So bigger y means further down the screen, which catches everyone out at first.
8. Customising: how far can you push it?
You asked what you can change. Quite a lot, actually.
The built-in sprites
The library comes with ten ready-made sprites in a matching style. Give game.sprite the number or the name, whichever you find easier to remember:
| Picture | Number | Name |
|---|---|---|
0 | "chicken" | |
1 | "dog" | |
2 | "bird" | |
3 | "egg" | |
4 | "coin" | |
5 | "basket" | |
6 | "shocked" | |
7 | "calm" | |
8 | "turtle" | |
9 | "car" |
hero = game.sprite("chicken", 100, 200) # by name coin = game.sprite(4, 250, 100) # or by number foe = game.sprite("🦖", 300, 200) # any emoji still works
Swap the sprite
Change what a sprite shows mid-game by setting .content. Give it a skin number, a name, or an emoji, whichever you like:
player = game.sprite("chicken", 240, 180) player.content = "coin" # now it is a coin player.content = 6 # now a shocked face player.content = "🍗" # now an emoji player.size = 80 # and twice as big
Make it spin
Set .angle (in degrees) to turn a sprite, and add a little every frame to make it spin. Nothing fancy under the hood: the screen just turns before it draws the emoji.
import game game.window(480, 360) coin = game.sprite("🪙", 240, 180, size=80) while game.playing(): coin.angle = coin.angle + 6 # turn 6 degrees every frame game.frame()
Face the other way (side-scrollers and fighters)
Set .scale_x = -1 to mirror a sprite left-to-right, so it faces the way it is walking. .scale_y flips it top-to-bottom, and any number bigger than 1 stretches it. This is how a side-scroller makes one emoji face both directions.
import game game.window(480, 360, background="#87ceeb") runner = game.sprite("🐔", 240, 200, size=60) while game.playing(): if game.pressed("left"): runner.x = runner.x - 5 runner.scale_x = 1 # face left if game.pressed("right"): runner.x = runner.x + 5 runner.scale_x = -1 # mirror to face right game.frame()
Coloured squares, no emoji needed
If you would rather have plain coloured shapes, use game.box. It has a real .color you can set, and it moves and collides just like an emoji sprite:
import game game.window(480, 360) # a green square you can drive around player = game.box(240, 180, 40, 40, "#22cc88") wall = game.box(380, 180, 20, 200, "#ff5577") while game.playing(): if game.pressed("right"): player.x = player.x + 5 if game.pressed("left"): player.x = player.x - 5 if player.touches(wall): player.color = "#ffdd00" # flash yellow on a bump game.frame()
A scoreboard is just a label
There is no magic score display. You make a label and update its text yourself, which means you decide where it sits and what it says:
board = game.label("Score: 0", 60, 22, size=22) # ... later, when the player scores ... game.score(1) board.content = "Score: " + str(game.score())
What it can and cannot do
Being honest about the limits, because knowing them helps you plan a game that will actually work:
- Great for: catch and dodge games, mazes, top-down movers, two-player keyboard games, simple arcade fun.
- Not built for: gravity and physics, loading your own picture files, mouse control, or sound. Those are the sort of things a full engine like pygame adds.
That is the trade. Our library is tiny and instant and needs no install, and in return it keeps things simple. For what you make in Year 7 to 10, simple goes a very long way.
9. Now have a go
Open the Sandbox, type import game, and build something. The two example cards there, "move the chicken" and "catch the eggs", are a good place to start: load one, then change a number and see what happens.