Resources
(Content to follow; this section will be filled in later.)
(Content to follow; this section will be filled in later.)
Your game looks a lot more professional straight away with good sprites, sound effects and music, and it doesn’t have to cost you anything. Below you’ll find the best places to look for free assets, neatly sorted by category.
.wav. No licence worries: you generate it yourself.pygame.font.Font).Not all “free” assets are the same. These are the three licences you run into most often:
| Licence | May you use it? | Attribution needed? | Commercial use? |
|---|---|---|---|
| CC0 | Yes | No | Yes |
| CC-BY | Yes | Yes, mention the author | Yes |
| CC-BY-NC | Yes | Yes | No |
Practical advice: If you’re in doubt, use CC0. You don’t need permission, you don’t have to credit anyone and you never risk breaking the rules by accident. Kenney.nl is your best friend.
See how we document assets in this project on the Asset licences page.
Most assets are CC0 (public domain). Exceptions are listed in the table with licence and required credit text.
| File | Source | Licence | Author | Date downloaded |
|---|---|---|---|---|
basket.png | Kenney Space Shooter Redux | CC0 | Kenney (kenney.nl) | 2026-05-11 |
star.png | Kenney Space Shooter Redux | CC0 | Kenney (kenney.nl) | 2026-05-11 |
ship.png | Kenney Space Shooter Redux | CC0 | Kenney (kenney.nl) | 2026-05-11 |
challenges/01-snake/fruits/*.png (9 items) | Kenney Food Kit | CC0 | Kenney (kenney.nl) | 2026-05-24 |
Files live in static/_shared/images/ and are available on the
published URL path /_shared/images/<filename>.
| File | Source | Licence | Author | Date downloaded |
|---|---|---|---|---|
shoot.wav | CC0 | 2026-05-11 | ||
hit.wav | CC0 | 2026-05-11 | ||
explosion.wav | CC0 | 2026-05-11 | ||
pickup.wav | CC0 | 2026-05-11 | ||
jump.wav | CC0 | 2026-05-11 | ||
music.ogg | incompetech.com | CC-BY 4.0 | Kevin MacLeod | 2026-05-17 |
music.ogg“Ethereal Relaxation” Kevin MacLeod (incompetech.com)
Licensed under Creative Commons: By Attribution 4.0
creativecommons.org/licenses/by/4.0
Creative Commons 0).static/_shared/images/ or static/_shared/sounds/.You’ve built a cool game and now you want to share it with the rest of the world. With pygbag you turn your Pygame game into a web page that anyone can play in the browser. No setup, no fuss. Just send a link and you’re done!
Pygbag compiles your Python code to WebAssembly and packs it with a small Pygame runtime that runs in the browser. The player doesn’t have to install anything: just open the page and play. That makes it perfect for sharing your game with friends, family or on itch.io.
Pygbag does not work with Pgzero (the simplified library from the first sessions). You need real Pygame, so this is available from session 4 onwards.
On top of that, a few things don’t work in the browser:
threading or multiprocessing.In a uv project:
uv add pygbagOr in Thonny through the package manager:
pip install pygbagPygbag requires your main loop to be asynchronous. That means: your main function becomes async def main(), and in every frame iteration you add await asyncio.sleep(0). That one line gives the browser the chance to breathe between frames.
Below you’ll find a ready-made basic structure: Pygbag-ready structure for main.py:
import asyncio
import pygame
pygame.init()
screen = pygame.display.set_mode((640, 480))
clock = pygame.time.Clock()
async def main():
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((30, 30, 60))
pygame.display.flip()
clock.tick(60)
await asyncio.sleep(0) # required for pygbag
asyncio.run(main())Adapt your own game code so everything sits inside async def main(), and don’t forget await asyncio.sleep(0) at the bottom of the while loop.
In your terminal, go to the folder with main.py and run:
uv run python -m pygbag --build main.pyThe output appears in the build/web/ folder. That folder contains everything you need to host your game.
Want to see whether it works before you publish? Use the same command without --build:
uv run python -m pygbag main.pyPygbag starts a local web server on http://localhost:8000. Open that link in your browser and you play your own game, exactly the way others will see it.
Once everything works, there are two ways to share your game:
build/web/ folder and give the zip file to your coach. They take care of the hosting.build/web/. Within a few minutes your game has its own page that anyone can visit!Congratulations! You’ve just published a real web game. 🎮
This site exists in Dutch, French and English. This page explains how the translations fit together, so you can maintain them without breaking the structure.
Every page has one file per language, in the same folder:
content/sessions/python/01-catch-the-stars/_index.md ← Dutch (default language)
content/sessions/python/01-catch-the-stars/_index.en.md ← English
content/sessions/python/01-catch-the-stars/_index.fr.md ← French/sessions/...), French and English under /fr/
and /en/ with the same path.title, weight, hidden and description belong in every language;
weight and hidden stay identical.basket, star, ship, score) stay English, because
they live in the Python file.star.png, boom.wav, starter.zip) stay
language-neutral.description fields.| Dutch | English | French |
|---|---|---|
| sessie | session | session |
| uitdaging | challenge | défi |
| werkblad | worksheet | fiche de travail |
| oplossing | solution | solution |
| bronnen | resources | ressources |
| installeren | setup | installation |
| coach-notities | coach notes | notes du coach |
| Wat je vandaag leert | What you’ll learn today | Ce que tu vas apprendre aujourd’hui |
| Neem mee naar huis | Take it home | À emporter chez toi |
| Vastgelopen? | Stuck? | Bloqué ? |
| Tot de volgende keer! | See you next time! | À la prochaine ! |
Tone: Dutch uses “je”, English uses “you”, French uses “tu” (tutoiement) — the texts are for ninjas between 10 and 17 years old.
{{% relref "/sessions/python/01-catch-the-stars" %}},
so they automatically follow the right language.static/ (images, demo JS, ZIPs) are absolute
and language-neutral.starter.zip (Dutch),
starter.en.zip and starter.fr.zip. Every language page links to its own
ZIP. Asset packs without code (images.zip, fruits.zip) have no
language variant.The source code lives in code/. Comments are Dutch; the translations sit
as main.en.py and main.fr.py next to the original. When the
ZIPs are built, <naam>.<taal>.py is renamed to <naam>.py and the
variants of the other languages are dropped. make zips takes care of that.
Every demo JS in static/sessions/ has a STRINGS table at the top with
Dutch, English and French. The language comes from the lang attribute of
the page; without a table or with an unknown language the demo falls back to Dutch.
make buildThe build fails on a broken relref. Then go through the three languages of
the changed page: title, headings, links and downloads have to work the same
in all three.