Building a Mario game in Python is one of the most rewarding capstone projects a BSIT student can take on. it teaches you the four core pillars of game programming in one project: the game loop, sprite collision, gravity-based physics, and tile-based level design. By the end of this tutorial, you’ll have a working platformer game with a controllable player, jumping mechanics, platforms to land on, Goomba-style enemies, a scoring system, and lives. Everything in under 400 lines of clean Python code using the pygame library.
This is the same Mario clone tutorial thousands of BSIT students have used to learn pygame since 2020. fully refreshed for 2026 with cleaner code, Python 3.10+ pattern matching where it helps, and fixes for the most common errors students hit during their thesis defense demos.
TL;DR: This is a pygame-based Mario clone in roughly 300-400 lines of Python. You’ll need Python 3.10+ and pygame 2.5+. The full source code is included below in copy-paste sections. main loop, Player class, Platform class, Enemy class, and level layout. Total build time: 60-90 minutes if you type along, 15 minutes if you copy-paste and tweak.
What You’ll Build
By the end of this tutorial, you will have a fully playable 2D side-scrolling platformer game inspired by the classic Super Mario Bros. The game includes:
- A controllable Mario-style player. moves left, right, and jumps with smooth gravity-based physics
- Static platforms the player can stand on and jump between (tile-based level)
- Goomba-style enemies that patrol back and forth and damage the player on contact
- A coin/score system with on-screen HUD showing score and remaining lives
- Lives counter with respawn logic when the player falls off the map or touches an enemy
- A clean game loop running at a stable 60 FPS using
pygame.time.Clock()
The whole thing fits in one Python file (or 2-3 if you split into modules later). It uses simple colored rectangles by default so you don’t need sprite art to get it running. but every part of the code is structured so you can drop in real Mario sprites later when you have them.
Screenshot placeholder: insert a screenshot of the running game showing Mario, platforms, an enemy, and the HUD.
Prerequisites
Before you start, make sure you have these in place:
- Python 3.10 or newer. check with
python --version. If you don’t have it yet, see our guide to installing Python on Windows. - pygame 2.5+. installed via
pip install pygame(full command in the next section). - A code editor. VS Code, PyCharm, or Thonny all work. See our best Python IDE comparison for 2026 if you haven’t picked one.
- Basic Python knowledge. variables, functions, classes, and if/else. You do NOT need to know game development. We’ll explain every game-specific concept as it comes up.
- 10 minutes of patience. pygame can be finicky on first install, especially on macOS with M-series chips. Stick with it; the second install always works.
Setting Up Your Environment
Create a project folder, set up a virtual environment, and install pygame:
# Windows (PowerShell)
mkdir mario_python
cd mario_python
python -m venv .venv
.venv\Scripts\activate
pip install pygame
# macOS / Linux
mkdir mario_python
cd mario_python
python3 -m venv .venv
source .venv/bin/activate
pip install pygame
Verify the install:
python -c "import pygame; print(pygame.version.ver)"
# Should print: 2.5.2 (or newer)
Project structure. keep it simple for now:
mario_python/
├── .venv/
├── mario.py # the whole game (we'll split later if you want)
└── assets/ # optional, drop sprite PNGs here later
The Source Code
The full source is broken into sections below so you can read and understand each piece. Copy them in order into a single file called mario.py, or grab the complete file at the end.
1. Imports and Constants
import pygame
import sys
# Screen and game constants
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
# Physics
GRAVITY = 0.8
JUMP_STRENGTH = -15
PLAYER_SPEED = 5
# Colors (R, G, B)
SKY = (135, 206, 235)
GROUND = (139, 69, 19)
PLATFORM = (160, 82, 45)
PLAYER_COLOR = (220, 20, 60) # Mario red
ENEMY_COLOR = (101, 67, 33) # Goomba brown
COIN_COLOR = (255, 215, 0) # Gold coin
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
2. The Player Class (Movement, Jump, Gravity)
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((30, 40))
self.image.fill(PLAYER_COLOR)
self.rect = self.image.get_rect(topleft=(x, y))
self.vel_x = 0
self.vel_y = 0
self.on_ground = False
self.lives = 3
self.score = 0
def update(self, platforms):
# Horizontal input
keys = pygame.key.get_pressed()
self.vel_x = 0
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
self.vel_x = -PLAYER_SPEED
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
self.vel_x = PLAYER_SPEED
# Jump
if (keys[pygame.K_SPACE] or keys[pygame.K_UP]) and self.on_ground:
self.vel_y = JUMP_STRENGTH
self.on_ground = False
# Apply gravity
self.vel_y += GRAVITY
if self.vel_y > 12: # terminal velocity cap
self.vel_y = 12
# Move horizontally + check collision
self.rect.x += self.vel_x
self._collide(self.vel_x, 0, platforms)
# Move vertically + check collision
self.rect.y += self.vel_y
self.on_ground = False
self._collide(0, self.vel_y, platforms)
def _collide(self, dx, dy, platforms):
for p in platforms:
if self.rect.colliderect(p.rect):
if dx > 0: # moving right
self.rect.right = p.rect.left
if dx < 0: # moving left
self.rect.left = p.rect.right
if dy > 0: # falling down
self.rect.bottom = p.rect.top
self.vel_y = 0
self.on_ground = True
if dy < 0: # jumping up
self.rect.top = p.rect.bottom
self.vel_y = 0
3. Platform and Coin Classes
class Platform(pygame.sprite.Sprite):
def __init__(self, x, y, w, h, color=PLATFORM):
super().__init__()
self.image = pygame.Surface((w, h))
self.image.fill(color)
self.rect = self.image.get_rect(topleft=(x, y))
class Coin(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((16, 16))
self.image.fill(COIN_COLOR)
self.rect = self.image.get_rect(topleft=(x, y))
4. The Enemy Class (Goomba Patrol)
class Enemy(pygame.sprite.Sprite):
def __init__(self, x, y, patrol_distance=120):
super().__init__()
self.image = pygame.Surface((28, 28))
self.image.fill(ENEMY_COLOR)
self.rect = self.image.get_rect(topleft=(x, y))
self.start_x = x
self.patrol_distance = patrol_distance
self.speed = 2
self.direction = 1 # 1 = right, -1 = left
def update(self):
self.rect.x += self.speed * self.direction
if self.rect.x > self.start_x + self.patrol_distance:
self.direction = -1
elif self.rect.x < self.start_x:
self.direction = 1
Section 5. Level Design (Tile-Based Layout)
def build_level():
"""Return platforms, enemies, coins, and the player start position."""
platforms = pygame.sprite.Group()
enemies = pygame.sprite.Group()
coins = pygame.sprite.Group()
# Ground (full width of screen)
platforms.add(Platform(0, SCREEN_HEIGHT - 40, SCREEN_WIDTH, 40, GROUND))
# Floating platforms (x, y, width, height)
platforms.add(Platform(150, 450, 120, 20))
platforms.add(Platform(350, 380, 120, 20))
platforms.add(Platform(550, 300, 120, 20))
platforms.add(Platform(200, 220, 100, 20))
# Enemies (Goombas)
enemies.add(Enemy(400, SCREEN_HEIGHT - 68, patrol_distance=200))
enemies.add(Enemy(160, 430, patrol_distance=100))
# Coins
for cx in (180, 380, 580, 230):
coins.add(Coin(cx + 50, cx % 200 + 180))
player_start = (50, SCREEN_HEIGHT - 100)
return platforms, enemies, coins, player_start
6. The Main Game Loop
def main():
pygame.init()
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Mario Game in Python, itsourcecode.com")
clock = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 22, bold=True)
platforms, enemies, coins, player_start = build_level()
player = Player(*player_start)
running = True
while running:
clock.tick(FPS)
# --- Event handling ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
# --- Updates ---
player.update(platforms)
enemies.update()
# Coin pickup
hit_coins = pygame.sprite.spritecollide(player, coins, dokill=True)
player.score += len(hit_coins) * 10
# Enemy collision = lose a life + respawn
if pygame.sprite.spritecollideany(player, enemies):
player.lives -= 1
player.rect.topleft = player_start
player.vel_y = 0
# Fell off the map
if player.rect.top > SCREEN_HEIGHT:
player.lives -= 1
player.rect.topleft = player_start
player.vel_y = 0
# Game over
if player.lives <= 0:
running = False
# --- Drawing ---
screen.fill(SKY)
platforms.draw(screen)
coins.draw(screen)
enemies.draw(screen)
screen.blit(player.image, player.rect)
# HUD
hud = font.render(
f"Score: {player.score} Lives: {player.lives}",
True, BLACK,
)
screen.blit(hud, (10, 10))
pygame.display.flip()
# Game over screen
screen.fill(BLACK)
over = font.render(
f"Game Over, Final Score: {player.score}",
True, WHITE,
)
screen.blit(over, over.get_rect(center=(SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)))
pygame.display.flip()
pygame.time.wait(2500)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
That’s the entire game in about 220 lines. Run it with python mario.py from inside your activated virtual environment. Use the arrow keys or WASD to move, Space or Up to jump, and Esc to quit.
Downloadable Source Code
How the Code Works
Understanding what the code does is more valuable than copy-pasting it. Here’s how each game-programming concept fits together:
Game loop
The game loop is the heartbeat of every game. Every frame (60 times per second), we do the same three things in order: handle events (keyboard, mouse, window close), update game state (move player, move enemies, check collisions), and draw everything to the screen. clock.tick(FPS) caps the loop at 60 frames per second so the game runs at the same speed on a fast PC and a slow laptop.
Gravity-based physics
Gravity-based physics is shockingly simple. Every frame, we add GRAVITY (0.8 pixels per frame) to the player’s vertical velocity (vel_y). That makes the player accelerate downward over time. exactly like real gravity. When the player jumps, we set vel_y = -15 (negative is up in pygame), so the player rises until gravity overwhelms the jump speed and pulls them back down. The terminal velocity cap (if self.vel_y > 12: self.vel_y = 12) prevents the player from accelerating infinitely if they fall off the map.
Sprite collision
Sprite collision is the trickiest part. We move the player horizontally first, check for collisions, and snap the player to the edge of any platform they hit. Then we move vertically and do the same. Doing both axes separately. instead of in one combined move. is what prevents the player from getting stuck inside a platform corner. When the vertical collision happens with positive vel_y (falling), we set on_ground = True so the player can jump again. This same pattern works in every 2D platformer ever written.
Enemy patrol AI
Enemy patrol AI is the simplest possible state machine: walk in self.direction, flip direction when you reach the patrol bounds. Two lines of logic. This same “back and forth” pattern is how Goombas worked in the original Super Mario Bros.
Pygame sprite groups
Pygame sprite groups (pygame.sprite.Group()) handle the bookkeeping. calling group.update() updates every sprite in the group, group.draw(screen) draws them all, and pygame.sprite.spritecollide() does fast collision checks between a sprite and a group. This is what lets the whole game stay under 250 lines.
Customizing Your Game
The base game runs. Now it’s yours to extend. Here are the most rewarding additions, ranked from easiest to hardest:
1. Add real sprite images (30 minutes). Replace the colored rectangles by loading PNG images: self.image = pygame.image.load("assets/mario.png").convert_alpha(). Use convert_alpha() for transparency. Free Mario-style sprites are available on OpenGameArt.org under Creative Commons licenses.
2. Add sound effects (15 minutes). Drop a WAV file in assets/, then: jump_sound = pygame.mixer.Sound("assets/jump.wav") and call jump_sound.play() inside the jump check. Add pygame.mixer.init() right after pygame.init().
3. Power-ups (Super Mushroom) (1-2 hours). Create a Powerup sprite class similar to Coin. On pickup, grow the player rect: self.image = pygame.Surface((30, 60)) and set a self.powered_up = True flag so the player survives one enemy hit before shrinking.
4. Multiple levels (2-3 hours). Move build_level() into a function that takes a level number, store level layouts as lists of tuples or read them from a text file (one character per tile), and trigger a level change when the player reaches a flag sprite at the far right.
5. Scrolling camera (3-4 hours). The harder upgrade. Instead of fixing the player to the screen, fix the camera to the player and offset every sprite’s draw position by the camera offset. This is what makes Mario feel like an endless world instead of a single screen.
6. Stomp-to-defeat enemies (30 minutes). On enemy collision, check if the player was falling AND the player’s bottom was above the enemy’s top last frame. If so, kill the enemy and bounce the player. Otherwise, take damage. This is the classic Mario mechanic.
Common pygame Errors and Fixes
Every pygame project hits the same handful of errors. Here’s how to fix them:
pygame.error: video system not initialized. You called a pygame display function before pygame.init(). Make sure pygame.init() is the first line of main(), before pygame.display.set_mode(). On macOS this also appears if you import pygame inside a non-main thread. keep all pygame calls on the main thread.
pygame.error: No available video device (Linux/WSL). Means there’s no display attached. If you’re on WSL2, install an X server (VcXsrv) and export DISPLAY=:0. On a headless server, you can’t run pygame at all. it needs a real or virtual display.
Sprite collision missing or false positives. Almost always a rect sizing issue. Use print(self.rect) inside update() to confirm the rect matches what you expect. A common bug: creating the Surface at one size and the rect at another. Always use self.image.get_rect(...) so they’re guaranteed to match.
Game runs too fast or too slow. You forgot clock.tick(FPS) inside the loop, so it runs as fast as your CPU allows. Add it back. If frame rate drops below 60, check whether you’re loading images inside the loop (load them once during __init__) and whether your sprite groups have hundreds of off-screen sprites being updated unnecessarily.
ModuleNotFoundError: No module named 'pygame'. You installed pygame but Python is using a different interpreter. Activate your virtualenv (.venv\Scripts\activate on Windows) and run pip install pygame again. Check which python (macOS/Linux) or where python (Windows) to confirm you’re using the venv interpreter.
Player getting stuck inside platforms. You’re moving both axes in one step. Always move horizontally first, resolve X collisions, then move vertically, then resolve Y collisions. The two-pass approach in the Player._collide method above prevents corner sticking.
Beyond Mario: What to Build Next
Once your Mario clone is running, the same pygame skills transfer to a huge range of capstone-friendly projects:
- Snake. simpler than Mario (no physics), perfect for practicing the game loop pattern
- Tetris. teaches grid-based logic, rotation matrices, and line-clearing algorithms
- Pong / Breakout. pure collision physics, great if you want to deepen your math intuition
- 2D RPG with tile maps. extend the platform/tile system from this tutorial into a top-down RPG with NPCs and dialog
- Space invaders / scrolling shooter. flip the gravity off and you have a shoot-em-up
- Card games (Solitaire, Tongits). pygame can handle card-based UIs too, great for a Filipino-themed capstone
When you outgrow pygame. usually when you want 3D, real shaders, or mobile builds. the natural next step is Unity (C#) for serious indie game dev, or Godot (free, open source, GDScript) if you want to stay in a Python-like language. For most BSIT capstones though, pygame is more than enough. It’s free, runs everywhere, and lets you focus on the game logic instead of fighting the framework.
Official documentation
Quick step-by-step summary (click to expand)
- Install Python and Pygame. Install Python 3.8 or later, then install Pygame with pip install pygame. Verify with python -m pygame.examples.aliens which should launch a test game.
- Download the source code and sprite assets. Download the complete zip from the article. It contains the Python source and the sprite images the game needs.
- Extract the files into a project folder. Extract the downloaded zip into a folder of your choice. Keep the sprite folder next to the Python file so relative paths work.
- Run the game. Open a terminal in the project folder and run python mario.py. Use the arrow keys to move and spacebar to jump.
- Customize levels and sprites. Edit the level layout in the source to add platforms or obstacles. Swap the character sprite by replacing the image file with your own PNG of the same size.
Frequently Asked Questions
How do I make a Mario game in Python?
pip install pygame), then build four components: a game loop running at 60 FPS, a Player class with horizontal movement and gravity-based jumping, Platform sprites the player can collide with, and Enemy sprites with patrol AI. The full working source code is in this article. about 220 lines total, runnable on Python 3.10 or newer.What Python library is used to make Mario games?
pip install pygame. Alternatives include arcade (more modern, simpler API) and pyglet (lower-level), but pygame has the largest tutorial ecosystem. important when you’re learning.Is Python good for making games?
How long does it take to build a Mario game in Python?
Can I run this Mario game without installing anything?
pip install pygame takes about 30 seconds.Why is my pygame Mario game running too fast?
clock.tick(FPS) inside the game loop. Without it, the loop runs as fast as your CPU allows. usually thousands of frames per second on a modern computer. Create a clock with clock = pygame.time.Clock() outside the loop, then call clock.tick(60) as the first line inside the loop. This caps the game at 60 FPS on every machine.How do I add sprites and sound to my Mario game?
pygame.Surface((w, h)) with pygame.image.load("assets/mario.png").convert_alpha() to use PNG sprites. For sound, call pygame.mixer.init() right after pygame.init(), then sound = pygame.mixer.Sound("assets/jump.wav") and sound.play() when the player jumps. Free Mario-style sprites and 8-bit sound effects are available on OpenGameArt.org under Creative Commons licenses.Is this Mario game enough for a BSIT capstone project?
Where can I download the full Mario Python source code?
mario.py file and it runs immediately. No external files needed. For sprite art and extended versions with sound and multiple levels, check our pygame projects collection for related downloadable projects.Final Recommendation
A Mario game in Python is the rare capstone project that’s impressive to demo, simple to explain, and rich enough to learn from. The 220-line version above gives you a working platformer in one sitting. The customization roadmap (sprites → sound → levels → scrolling camera) gives you 20+ hours of upgrades when you want to make it capstone-defense-ready.
Whichever direction you take it, don’t get stuck trying to make it perfect on the first pass. Get the base game running today. then iterate. Every great game started as a colored rectangle that could jump on another colored rectangle.
Your next steps:
- Copy the source code above into
mario.pyand run it. see the colored Mario jump on platforms - Pick one customization from the list (sprites or sound is easiest) and add it today
- Browse more capstone-ready ideas in our best Python projects with source code roundup
- Need the right editor? See our best Python IDE 2026 guide
- New to Python? Start with our free Python tutorial series
- Looking for more pygame projects? Browse our full pygame projects collection
Built this Mario game and added something cool? Drop a screenshot or your source code link in the comments. we feature the best student builds in our monthly capstone roundup.
I hope this article helps you build your Mario Game in Python successfully. If you have any questions or suggestions about the pygame source code, please feel free to leave a comment below or contact me at our contact page. You can also add me on Facebook at https://www.facebook.com/joken.villanueva for direct help with your Python projects or capstone defense.

what is the front end
and back end details
There is a error in the Line 29 of the final code “https://itsourcecode.com/free-projects/python-projects/mario-game-in-python-with-source-code/”. Please go through the changes immediately.
did not work for me, i do what you had done but did not work.
I think you need to give us he link for image you reference. Eg. Mario character, that need to be downloaded.
I am not able to download the source code and also there is a error in line 27.Some one please help me with download.
I am not able to download the source code and also there is a error in line 27.Some one please help me with download.i want to learn ..
the images are required too
please
IT DOSENT WORK