pgzero: teaching 2D games in Python without an event loop or an import
A zero-boilerplate games programming framework for Python 3, based on Pygame.
At a glance
- What is it?
- Pygame Zero wraps Pygame in a runner and a set of builtins so a beginner can write a complete game with no imports, no class and no explicit main loop, which is why it became a standard choice for teaching Python game programming rather than for shipping games.
- Who is it for?
- Adopt pgzero when you are teaching Python to people who have never written a game, or when you want a weekend prototype that runs with a single command and no class boilerplate. Do not adopt it for a commercial game, since the abstraction that makes it teachable removes the control you would need for anything beyond simple 2D work.
- Can I use it commercially?
- Yes, with conditions. LGPL-3.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 12 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The runner is the whole architecture
Pygame Zero is described as a zero-boilerplate games programming framework for Python 3, based on Pygame, and the project description calls it a zero-boilerplate 2D games framework. The mechanism is one sentence in the README: Pygame Zero consists of a runner, pgzrun, that will run a Pygame Zero script with a full game loop and a range of useful builtins. That is the entire design. You write a script, the runner finds it, starts the loop, and injects the builtins into its namespace. The consequence the README highlights is that each example is a self-contained script, with no need for any imports or anything else in the file. The audience is explicit in the repository topics, which include education and python-game-development, and the documentation is written for a first-time programmer rather than for someone who already knows Pygame's event loop.
Draw, update and named functions instead of a loop
The examples in the README show what that means in practice. Graphics are drawn in a function named draw, where you clear the screen and blit an image, assuming a file like images/dog.png or images/dog.jpg exists. The game loop picks that function up automatically.
def draw():
screen.clear()
screen.blit('dog', (10, 50))Motion is expressed in a function named update, and an actor is created by calling Actor with a sprite name, then positioned by assigning to its pos. The update function moves the actor and wraps it around the screen when it goes past the width.
alien = Actor('alien')
alien.pos = 10, 10
def draw():
screen.clear()
alien.draw()
def update():
alien.x += 1
if alien.left > WIDTH:
alien.right = 0The actor's attributes are read and written as if they were plain properties, and the boundary check uses a named constant WIDTH rather than a magic number, which is a small piece of pedagogy baked into the example. Input is handled the same way, with a function named on_mouse_down that plays a sound file.
def on_mouse_down():
sounds.eep.play()The builtins are a fixed global vocabulary
The names available in a Pygame Zero script are not discovered at runtime, they are a fixed list, and the project configuration states it. The builtins are Actor, Rect, ZRect, animate, clock, exit, images, keyboard, keymods, keys, mouse, music, screen, sounds, storage and tone. That list is the API surface of the framework, and it is short enough to print on a sheet of paper, which is the point for a teaching tool. It also tells you what the framework has deliberately left out. There is no camera object, no scene manager, no sprite group, no particle system, no shader, and no explicit surface or render layer beyond the single screen. A learner who moves beyond the examples will eventually hit the edge of that vocabulary, and the honest path at that point is to drop down to Pygame, which is what the built on Pygame framing prepares you for.
Installing it and running your first script
The package is on PyPI as pgzero, and the manifest declares the requirements: Python 3.6 or newer, Pygame 2.1 or later, numpy, and pyfxr, the latter being a sound effect generator rather than a sound player. The requirements file lists the same set. Installation runs through pip in the usual way, and the console entry point is registered as pgzrun, mapping to pgzero.runner:main, so the runner is invoked by name rather than by a Python module path.
The README does not spell out the install command itself, and instead refers to the installation instructions in the documentation, so the definitive steps live at the installation page of the docs site. Once installed, the usage pattern is to write a script and hand it to the runner as a filename, which is what the pgzrun entry point is for. The documentation is hosted at pygame-zero.readthedocs.io, with a tutorial at the introduction page and a contributing page that covers setting up a development version of the project, which is the path to take if you intend to change the framework rather than use it.
Eleven example games that double as the curriculum
The examples directory is the teaching material, and there are eleven of them, which tells you how the framework expects people to learn. There is a basic example, then asteroids, flappybird, lander, maze, memory, mines, pong, snake, tetra_puzzle and tron. Read as a progression, that is a move from one moving object to a complete arcade game, then to a puzzle, then to a maze, then to a memory match, then to a minesweeper clone with mouse input, then to paddle games, then to a falling block puzzle and finally to a networked-looking arcade game. Each of those exercises a different corner of the builtins, and each is a single self-contained script with no imports, which means a learner can read one file end to end and understand a complete program. The repository also carries source-vectors/ for artwork, doc/ for the documentation sources, an AUTHORS file, and a tox.ini plus a pre-commit configuration for development.
Maintenance, packaging and the shape of the project
The project is MIT licensed, with a COPYING file at the top level, and the licence is listed as LGPL-3.0 in the repository metadata, so the terms are worth confirming from the COPYING file before you build on it. The last push was on 2026-10-11 and the repository is not archived, so the tree is being worked on, but the only recent release listed is 1.2.1 from 2025-01-18, which suggests releases are infrequent while maintenance commits continue. The build system moved to modern packaging: setuptools 64 or newer with setuptools-scm 8 or newer as the build backend, with the version derived from the Git tags and written to src/pgzero/_version.py, which is why the manifest lists the version as dynamic. The ruff configuration in the same file declares the builtins, which is how the linter knows about the injected names in user scripts. The test suite sits in test/, driven by tox.ini, and there is a GitHub Actions workflow for tests.
Where Pygame Zero is the wrong tool
The limits follow from the abstraction. Because the game loop and the event handling are implicit, you cannot easily take over the loop, and a game that needs precise frame timing, multiple screens with their own state, or a large number of entities has nowhere to put that logic inside the builtins list. Because everything is global, the builtins shadow anything you name the same, and a script is a flat namespace rather than a set of classes, which is fine for a lesson and wrong for a codebase. The dependency on Pygame also means the framework inherits Pygame's platform limits and its own setup burden, so the zero-boilerplate promise applies to your game code and not to the environment. The alternative worth naming is writing Pygame directly, which the framework itself prepares you for, and the difference in approach is that Pygame Zero trades the explicit event loop for automatic callbacks, so learning the underlying library afterwards is an extra step rather than a wasted one. For a shipped game, most teams end up in plain Pygame or a different engine entirely, and pgzero's real value stays in the classroom and the first hour of a project.
Editorial conclusion
Adopt pgzero when you are teaching Python to people who have never written a game, or when you want a weekend prototype that runs with a single command and no class boilerplate. Do not adopt it for a commercial game, since the abstraction that makes it teachable removes the control you would need for anything beyond simple 2D work. Verify first that Python 3.6 or newer and a working Pygame install are available, and read the contributing instructions before setting up a development checkout, because the project publishes a separate setup path for contributors.
Frequently asked questions
What is Pygame Zero and what does pgzrun do?
Pygame Zero is a zero-boilerplate 2D games framework for Python 3 built on Pygame. It consists of a runner, pgzrun, which runs a Pygame Zero script with a full game loop and a range of useful builtins, so a script needs no imports at all.
How do I install Pygame Zero?
The package is on PyPI as pgzero and requires Python 3.6 or newer with Pygame 2.1 or later, numpy and pyfxr. The README refers to the installation instructions in the documentation, and the project registers a console entry point named pgzrun.
Which builtins does Pygame Zero provide to a script?
Actor, Rect, ZRect, animate, clock, exit, images, keyboard, keymods, keys, mouse, music, screen, sounds, storage and tone. The list is fixed in the project configuration, which is also what the linter is told about.
How do I write a moving character in Pygame Zero?
Create an Actor with a sprite name, set its pos, draw it in a draw function and change its position in an update function. The example wraps the actor around by setting alien.right to 0 when alien.left passes WIDTH.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/lordmauve-pgzero)Was this useful?