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 can host directly.
Prerequisites
- Ren'Py SDK (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:
"$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.
SDK integration
Ren'Py exposes renpy.emscripten.run_script(...) on the web platform — it evals JavaScript on the host page, so you can reach window.Wavedash directly. The host installs it synchronously at the top of <head>, so it's already the SDK by the time Ren'Py's wasm runs. The Promise.resolve(...) wrap below is defensive in case that ever changes.
game/00_wavedash.rpy
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.Wavedash).then(function (sdk) {
sdk.updateLoadProgressZeroToOne(1);
sdk.init({ debug: true });
});
""")
label before_main_menu:
$ wavedash_report_first_playable()
return
renpy.emscripten is the documented Ren'Py module for web JS interop; on desktop builds it's None, so the truthy check doubles as a platform guard.
Adding more SDK methods
Inline more JS in the same run_script block (or add a separate function for each beat). For example, unlocking an achievement:
$ renpy.emscripten.run_script("""
Promise.resolve(window.Wavedash).then(function (sdk) {
sdk.setAchievement('first_chapter_complete', true);
});
""")
Load progress
The snippet above reports 1.0 right before main_menu is shown, which is the earliest moment the game is visible and interactive — the before_main_menu label is Ren'Py's hook for exactly this. For intermediate progress, call updateLoadProgressZeroToOne from earlier init hooks the same way.
wavedash.toml
game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./build"
entrypoint = "index.html"
Notes
main_menulabel vs screen: Ren'Py checks for amain_menulabel before using themain_menuscreen. If you only define a screen, add an explicitlabel main_menu: call screen main_menuso Ren'Py actually mounts it.- 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.