# Python

Run Python games in the browser with Pyodide — no build step, just an HTML shell.

Source: https://docs.wavedash.com/engines/python

<GithubLink href="https://github.com/wvdsh/examples/tree/main/example-python" />
<PlaytestLink href="https://wavedash.com/playtest/python-example/b7acd118-e26d-42d6-88eb-9089cc8042b1" />

You can ship a Python game to Wavedash by running CPython in the browser with [Pyodide](https://pyodide.org/). There's no build step — Pyodide loads from a CDN, reads your `.py` file at runtime, and exposes the browser globals (`document`, `window`, and `Wavedash`) to Python code through the `js` module.

## Setup

Create a `web/` folder with an `index.html` that boots Pyodide and a `main.py` with your game.

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>My Python Game</title>
    <style>
      html, body { margin: 0; height: 100%; background: #0a0a0a; }
      #gameCanvas { width: 100%; height: 100%; display: block; }
    </style>
  </head>
  <body>
    <canvas id="gameCanvas"></canvas>
    <script type="module">
      import { loadPyodide } from "https://cdn.jsdelivr.net/pyodide/v0.29.3/full/pyodide.mjs";

      const pyodide = await loadPyodide({
        indexURL: "https://cdn.jsdelivr.net/pyodide/v0.29.3/full/",
      });

      const code = await fetch("./main.py").then((r) => r.text());
      await pyodide.runPythonAsync(code);
    </script>
  </body>
</html>
```

## SDK integration

Inside your `main.py`, import `Wavedash` from the `js` module. Use Pyodide's `to_js` helper when you need to pass Python dicts (SDK config, etc.) that JavaScript sees as real objects.

<Urgent>
**Calling `Wavedash.init()` is required.** Your game stays hidden behind the Wavedash loading screen until you do. Call it once your game is ready to play.
</Urgent>

```python
from js import Wavedash, document, window
from pyodide.ffi import to_js

# Pyodide and the standard library have finished loading (~10 MB of WASM).
Wavedash.updateLoadProgressZeroToOne(0.5)

canvas = document.getElementById("gameCanvas")
ctx = canvas.getContext("2d")

def game_loop(now):
    # update + draw
    window.requestAnimationFrame(game_loop_proxy)

# ...set up input handlers, kick off the loop...

# Game state and input handlers are ready — signal fully loaded.
Wavedash.updateLoadProgressZeroToOne(1.0)
Wavedash.init(to_js({"debug": True}))
```

All SDK methods work through this same bridge — `Wavedash.setAchievement("first_win", True)`, `Wavedash.requestStats()`, and so on. See [SDK setup](/sdk/setup) and [Functions reference](/sdk/functions) for the full list.

## wavedash.toml

```toml
game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./web"
entrypoint = "index.html"
```

<Note>
Pyodide is about 10 MB of WebAssembly plus standard-library bundles. It's great for prototypes and turn-based games, but if you're targeting 60fps action, budget load time carefully and use `updateLoadProgressZeroToOne` to keep the player oriented while Pyodide boots.
</Note>
