# Ren'Py

Publish Ren'Py visual novels with the built-in HTML5 web build target.

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

Ren'Py supports web builds through its Emscripten-based HTML5 export. The output is a folder of HTML, JS, WASM, and game data that Wavedash hosts directly. Wavedash detects Ren'Py automatically and drives the loading overlay from the engine's boot phases; calling the SDK from inside your script is done through `renpy.emscripten.run_script(...)`.

<GithubLink href="https://github.com/wvdsh/example-renpy" />
<PlaytestLink href="https://wavedash.com/playtest/ren-py-example/33c27522-b97e-4deb-899e-cd0ad912d34a" />

## Prerequisites

- [Ren'Py SDK](https://www.renpy.org/latest.html) (8.x)
- The **Ren'Py web platform package** installed into the SDK (download from the same page; it unpacks a `web/` folder into the SDK root)

## Build for web

From the Ren'Py launcher: select your project → **Build Distributions** → check **Web** → build to a destination folder.

From the command line:

```bash
"$RENPY_SDK/renpy.sh" launcher web_build ./my-project --destination ./build
```

The output contains `index.html`, `renpy.wasm`, `renpy.js`, `game.zip`, and icons. Point `upload_dir` at that folder.

## Loading progress

You don't have to wire this up — Wavedash detects `?engine=RENPY` from the iframe URL and installs a progress shim that watches Ren'Py's own boot phases (engine download, game data download, unpacking, script load). The Wavedash loading overlay tracks them automatically.

## Initialize the SDK

<Urgent>
**Calling `Wavedash.init()` is required.** It opts your game into Wavedash platform features and dismisses the loading overlay. Call it from `before_main_menu`, which is the earliest hook where the game is interactive.
</Urgent>

Call `sdk.updateLoadProgressZeroToOne(1)` before `sdk.init()` to report load completion; `init()` signals the overlay to dismiss, so call it last.

### `game/00_wavedash.rpy`

```renpy
default wavedash_first_playable_reported = False

init -100 python:
    import renpy.store as store

    def wavedash_report_first_playable():
        if store.wavedash_first_playable_reported:
            return
        store.wavedash_first_playable_reported = True

        if not renpy.emscripten:
            return

        renpy.emscripten.run_script("""
            Promise.resolve(window.WavedashJS).then(function (sdk) {
                sdk.updateLoadProgressZeroToOne(1);
                sdk.init({ debug: true });
            });
        """)

label before_main_menu:
    $ wavedash_report_first_playable()
    return
```

`renpy.emscripten` is the [documented](https://www.renpy.org/doc/html/web.html) Ren'Py module for web JS interop. On desktop builds it's `None`, so the truthy check doubles as a platform guard. The `Promise.resolve(window.WavedashJS)` pattern handles the case where the SDK script is still loading when `before_main_menu` fires.

## Calling SDK methods

Inline more JS in the same `run_script` block (or write a helper for each beat). Unlocking an achievement:

```renpy
$ renpy.emscripten.run_script("""
    if (window.Wavedash) {
        window.Wavedash.setAchievement('first_chapter_complete', true);
    }
""")
```

Reading the current player's username (sync round-trip via `run_script_string`):

```renpy
$ raw = renpy.emscripten.run_script_string("JSON.stringify(window.Wavedash?.getUsername() ?? null)")
$ import json
$ username = json.loads(raw) or "Player"
```

Any other JS-SDK method (`setStat`, `toggleFullscreen`, `toggleOverlay`, `getUserAvatarUrl`, etc.) is reachable the same way — they're all on `window.Wavedash`. See the [JS SDK reference](/sdk/functions) for the full list.

## Fullscreen

Ren'Py's built-in `Preference("display", "fullscreen")` doesn't work on Wavedash web — the iframe doesn't own the real fullscreen target. Use the Wavedash helper instead, from a preferences screen or button:

```renpy
screen wavedash_fullscreen_button():
    textbutton "Fullscreen":
        action Function(renpy.emscripten.run_script, "window.Wavedash?.toggleFullscreen()")
```

## wavedash.toml

```toml
game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./build"

[renpy]
version = "8.6.0"
executable = "game.zip"
```

The `[renpy]` section tells the Wavedash CLI to treat the build as a Ren'Py 8.x web export.

## Notes

- **`main_menu` label vs screen:** Ren'Py checks for a `main_menu` *label* before using the `main_menu` screen. If you only define a screen, add an explicit `label main_menu: call screen main_menu` so Ren'Py actually mounts it.
- **Template screens required:** Real Ren'Py games include `game/screens.rpy`, `game/gui.rpy`, and `game/gui/` images from the SDK template. If you started from a bare `script.rpy`, the ESC menu (Save / Load / Preferences) will be missing.
- **Web build size:** Ren'Py web builds can be 50MB+ with audio/image assets. Compress audio to OGG and resize images for web before building.
