I was working on porting the Ren'Py framework to the HTML5 web browser - so you can publish Visual Novel games online :)

Test it!

Current version: 7.4.1/2021-01-23
The Question - Web Edition Tutorial - Web Edition
(Shitf+O for dev console, F3 for perfs)

Also available for testing at:


Included version: RenPyWeb is now officially included in Ren'Py and can be run from the Launcher!
Ren'Py download - 7.4 changes

Source code: check the official Ren'Py repository at:
Contributions much welcome :)

Ren'Py nightly builds provide the unstable/development version of Ren'Py and RenPyWeb (pending review and build delays)

Another project: I also develop the Ren'Py Translator ToolKit for PO files - give it a try :)


Lemma Soft Forums: community feedback


Here are a few public games that run on RenPyWeb.

This list of games is not curated, use at your own risk.


RenPyWeb's goal is to run your game without additional work. However the web is a very different environment so here are a couple things to know.

Size: this is the web, so things need to be light. Think Flash games. Think metered mobile plan.
RenPyWeb needs to download part of the game before playing. Keep game.zip under 100MB, or your players will complain, because they have to download that everytime they play before it starts! Plus browsers only cache files up to ~50MB.
7.4 now can download some assets while playing (cf. progressive download below).
Even with that, large assets will mean large downloads, so think light and optimize your game.
Solutions include: using layered images, making a lower-res or lossy/jpg version, optimizing images (.webp format, pngquant...), using flat-shading, use higher compression for audio files (don't re-encode already-compressed audio though), dropping some audio, dropping videos (not supported yet), splitting in chapters, etc.
Note on Python environment: the RenPyWeb Python environment is minimal for size reasons. You may
ship additional Python modules in your Ren'Py game. Alternatively you can try placing additional .pyo files (e.g. from your Ren'Py SDK) in /lib/python2.7 in your game.zip.

Speed: was significantly improved in 7.4, though I'm not sure we can do much better at the moment (gory details).

Audio glitches: are fixed in 7.4, though some skipping may still happen when the game is very busy or downloads too many assets at once (see Speed above).

Savegame: user savegames are linked to the webpage domain.
Ideally, provide a stable (sub)domain - like this page.
Some hosts may change your domain on new upload, causing previous savegames to become unreachable.
In addition, a game can read/modify savegames from other games hosted at the same domain. Ren'Py will ignore them, but a malicous game developer could do bad things.
Playing games in incognito/private mode doesn't allow savegames, or will drop them when quitting the browser.
For itch.io (hosts at XXX.ssl.hwcdn.net, previously at commondatastorage.googleapis.com) it's not clear how often the domain may change: see How to save data? and HTML5 game : Save data locally.
For newgrounds all games are hosted at uploads.ungrounded.net so other games could read your player's data. NG passes the user identity and session as querystring, though that only helps for logged-in users.
Last, if the user blocks 3rd-party cookies (by default in iOS and Debian Chromium) and runs the game in a iframe (most web game hosts), savegames are lost on reload (itch.io issue, emscripten discussion). Meanwhile, even though this isn't recommended, you can try to enable 3rd-party cookies.
For reference, error messages are like "a mutation operation was attempted on a database that did not allow mutations" or "the user denied permission to access the database".
In doubt, remind players to export their savegames from RenPyWeb's top-left menu.

Memory/RAM: memory may be scarse, especially on mobile browsers.
To optimize memory usage, make sure config.cache_surfaces is set to False, adjust graphics memory (GPU) using config.image_cache_size_mb, and/or provide a lower-res version of your game. Also make sure to remove any unused files from game.zip (notably videos), since it's extracted in-memory.
Check the system (not GPU) RAM usage using e.g. Firefox's Console > Memory > Snapshot.
For a lighter game.zip, see progressive download below.

Network: browser outgoing connections are secured by Same-origin policy.
In addition, the browser can only perform XHR and WebSocket (i.e. no direct TCP/IP or UDP/IP connections).
Last, it is dangerous if your game waits on a connection (even in native Ren'Py): any network issue (unplugged cable, out-of-reach WiFi, etc.) will freeze the game for minutes and the user won't even be able to save. Usually this can be worked-around with threads (e.g. Renpytom's analytics) but they are currently not available in the browser.

I wrote an asyncrequest.rpy so you can make asynchronous HTTP requests in both native and web versions, based on urllib2 and XMLHttpRequest respectively. Example code included.

You can also experiment with WebSockets, which are initiated transparently when you make a TCP/IP connection from Python, but you need a specific server, or a service proxy (cf. Introduction to Emscripten Sockets) on the developer's server side.

Avanced usage: avoid calling Emscripten's *_wget* functions from RenPyWeb: pseudo-synchronous functions require custom ASYNCIFY_WHITELIST recompilation, and asynchronous functions may cause memory corruption on callback (Python re-entrancy issues).

Mobile: mobile browsers are a bit behind when it comes to web technologies, and RenPyWeb currently may run slow on these.
iOS in particular has long-standing issues with web games; since iOS requires all browsers to use Apple's limited web engine (webkit), all iOS browsers are impacted (things getting better).
Ren'Py now has a limited on-screen keyboard for mobile browsers; see renpy/common/00touchkeyboard.rpy for details.

Screen Variants (Ren'Py doc): RenPyWeb knows how to adapt screen layout to various devices, like Android and iOS versions:
- the new "web" variant is always set,
- the "mobile" variant is set on mobile browsers,
- "touch" is set on mobile devices with touch support,
- "large/medium/small" is set on mobile depending on screen size and "large" is always set on pc,
- "pc/phone/tv/android/ios" is NOT defined as this is reserved for native access to the device.
You can test with Ren'Py's Emulator or your browser's phone emulator.

Videos: currently not supported. In the future I plan to support renpy.movie_cutscene() first (fullscreen, as an HTML5 video, browser-supported formats only); then we'll see if the web techs evolved enough to support Movie(). Meanwhile, image-based (ATL) animations do work.

IME: complex keyboard input methods are not supported at this point, due to browsers limitations, see #80 for technical details.

Gamepad: game controllers are supported, however they are not detected on start-up, which is a general HTML5 limitation. Just press a button/axis and Ren'Py will immediately detect it. This notably adds a new "Gamepad" tab in the default "Help" screen. Shift+G has tools to further diagnose gamepad status.

Presplash: add a web-presplash.png, web-presplash.jpg or web-presplash.webp (can be animated) at the top-level of your game (above, not inside, your game directory, along with android-presplash.*).

Hosting on your own server: you upload the game files to your website like any other static files. If you use Apache, see also the provided htaccess file.

Invalidating browser cache on game update: when releasing a new version of your game, I suggest you either change the address where you upload the new version (game hosts usually do that), or use cache control (see the provided htaccess.txt). Otherwise, the browser may mix cached and non-cached versions, causing weird errors.

Hosting on game web hosts: zip all the files from the build directory (so index.html, game.zip, index.wasm.gz, etc. are at the top-level of your zip archive).
More precisely: you don't add files to game.zip. You make a new .zip file (e.g. myvisualnovel-web.zip) that contains all the build directory files (e.g. all the files within myvisualnovel-1.0.0-dists/myvisualnovel-1.0.0-web/), including game.zip (in the end, there is a zip in a zip, e.g. game.zip in myvisualnovel-web.zip).
For newgrounds, edit your game again and tick HTML5 Archive (zip) > Edit Properties > Touchscreen friendly otherwise it will be marked as incompatible with mobile.
For itch.io, tick This file will be played in the browser when uploading.

Play offline: browsers now have all kinds of security limitations, and require that you run your game through a local webserver (the Ren'Py launcher runs one automatically for you).
Note: just double-clicking on index.html used to work with Firefox, but not anymore with version 68; it didn't keep saved games anyway.
Note: some other web game engines run offline to some extent but they use different web technologies with different limitations.

Change game.zip extension: apparently cloudflare does not cache .zip files in their basic plan, and maybe you need to change the file extension for whatever reasons.
If you cannot make them change, you can rename game.zip to game.xyz and set DEFAULT_GAME_FILENAME accordingly in your index.html.
(alternatively you can start your game by adding ?game=game.xyz.)

Progressive download: RenPyWeb can download images, music and voices while playing. The game starts-up faster (smaller .zip), and uses less memory.
A progressive_download.txt file is created in your game directory so you can filter (by type and path) what is pre-loaded on start-up, and what is downloaded while playing.
This uses prediction to download images and sounds in advance. If your code is complex and/or use translations, you may need to increase config.predict_statements. Otherwise, you may use renpy.start_predict/stop_predict. If you use creator-defined statements, make sure they support prediction.
If a download didn't finish in time (e.g. when network is slow, when loading a savegame, or when using skip mode), images are replaced with a pixellated preview and looping music plays silence (temporarily); voices and sound effects are just ignored.
To avoid consuming all RAM, image files are kept for 1mn, voice files for 3mn. They may be downloaded again if needed, leveraging the browser's cache. Image files, once loaded in the GPU, stay available longer through Ren'Py's graphics cache.
Note: auto-voicing currently not supported
itch.io note: if you get a message about too many files, you can ask the support to lift the limit.

Interaction with JavaScript: the Python port comes with an emscripten module that itself has some JavaScript integration, e.g.:
import emscripten


(in no definite order)

Status reports

2021 week 12 (2021-03-27)

2021 week 10 (2021-03-13)

2021 week 9 (2021-03-06)

2021 week 8 (2021-02-27)

2021 week 4 (2021-01-30)

2021 week 3 (2021-01-23)

2021 week 1 (2021-01-09)

2020 week 53 (2021-01-02)

2020 week 52 (2020-12-26)

2020 week 51 (2020-12-19)

2020 week 47 (2020-11-21)

2020 week 46 (2020-11-14)

2020 week 45 (2020-11-07)

2020 week 43 (2020-10-24)

2020 week 42 (2020-10-17)

2020 week 41 (2020-10-10)

2020 week 40 (2020-10-03)

2020 week 39 (2020-09-26)

2020 week 38 (2020-09-19)

2020 week 37 (2020-09-12)

2020 week 36 (2020-09-05)

2020 week 35 (2020-08-29)

2020 week 33 (2020-08-15)

2020 week 32 (2020-08-08)

2020 week 31 (2020-08-01)

2020 week 30 (2020-07-25)

2020 week 29 (2020-07-18)

2020 week 28 (2020-07-11)

2020 week 27 (2020-07-04)

2020 week 26 (2020-06-27)

2020 week 25 (2020-06-20)

2020 week 24 (2020-06-13)

2020 week 23 (2020-06-06)

2020 week 22 (2020-05-30)

2020 week 21 (2020-05-23)

2020 week 19 (2020-05-09)

2020 week 18 (2020-05-02)

2020 week 17 (2020-04-25)

2020 week 16 (2020-04-18)

2020 week 15 (2020-04-11)

2020 week 14 (2020-04-04)

2020 week 13 (2020-03-28)

2020 week 12 (2020-03-21)

2020 week 11 (2020-03-14)

2020 week 10 (2020-03-07)

2020 week 09 (2020-02-29)

2020 week 08 (2020-02-22)

On-Demand Resource Download has been my prototype code name for "don't download everything on start-up, download later". Progressive download may be less of a mouthful, so maybe we'll use that as a final name :)

Whatever the name, with this week's additions, I think the feature is fully working. Next week I'll clean-up the code and submit it to Renpytom for inclusion in the next Ren'Py. If you need something more, now is the time to speak up!

2020 week 07 (2020-02-15)

2 weeks ago I shared a screenshot of pixellated placeholders, which are shown when a download didn't finish in time. I want to clarify: the player will normally directly see the final image, they only get the pixellated preview briefly when on narrow bandwidth, or when starting from a savegame.

I'd like to share with you a little case study I did this week. A patron showed me their large-ish game (a point-and-click with 720p 3D renders) working with RenPyWeb and on-demand resource download (fast start-up). You can roam from room to room in a big house, and there are some dialogue scenes with lots of additional full-screen renders.

While the scenes worked seamlessly, I noticed that I got the pixellated placeholders often when moving from room to room, while you normally shouldn't see them. So I took some time to ensure RenPyWeb was working alright.

It turns out that the game had a complex scene management system which didn't play well with Ren'Py's screen prediction, so (down)loading could not be done in advance. In addition, each room had lots of large clickable elements, each with idle&hover variants, which easily took 50MB graphic RAM. Multiply that by ~10 locations, and the game quickly exceeded config.image_cache_size_mb, so the house didn't "fit" in graphic memory all at once.
(Incidentally I also found a memory issue in the game's click masks, which I reported and may help some.)

So Ren'Py had to regularly unload rooms visited last, and reload them when going back there. Since prediction was prevented, the player got the pixellated placeholders for a brief moment. Importantly, when reloading a room, image files were downloaded again but leveraged the browser's cache, so this was fast while remaining memory-efficient.

All in all, RenPyWeb (and on-demand resource download) behaved robustly even in non-optimal situations :)

In other news:

2020 week 05 (2020-02-01)

2020 week 04 (2020-01-25)

I fixed bugs in "On-demand resource download" (which shortens loading time) that you didn't even find and report, this is good sign, things are stabilizing! I plan to finish automatic low-res image placeholders (rather than same-old stretched download icon), and add some configuration in the Ren'Py launcher. Next I have some overdue compiler upgrades (plus Renpytom is revamping the build system). And next... some of you expressed interest in supporting videos in RenPyWeb. This ain't gonna be easy, but we'll try!

2020 week 03 (2020-01-18)

2020 week 02 (2020-01-11)

Hopefully this time I'm done with downloading images on-demand, massively reducing the initial game download on start-up. Every time I think I'm done, I get happy early users who find new corner cases, let's see how this update holds up :)

2020 week 01 (2020-01-04)

Where are we? I continue my work on downloading images and musics as-needed (rather than everything on game loading). There are lots of corner cases to address for images, and I fixed some of them. There's one more to fix, then hopefully I'll focus on musics again, as well as better configurability.

2019 week 52 (2019-12-29)

On-Demand Resource Download: experiments with downloadable manipulated images, following discussion with Renpytom + reworking downloads clean-ups; first results are promising, meaning more games should support downloadable images without changes.

2019 week 51 (2019-12-22)

2019 week 50 (2019-12-15)

This week I mostly tracked down issues with on-demand resource download, reported by users; asynchronous errors are so tricky and time-consuming!
I also converted my local patches to a longer-term public Git branch, available for browsing.

2019 week 49 (2019-12-08)

2019 week 48 (2019-12-01)

More progress with on-demand resource download. Initial support for images is stable, so I'll move to sound support next, then start polishing (control what to keep preloaded, tackle title screen readiness, better placeholders with identical dimensions so loaded images don't "jump", etc.).

2019 week 47 (2019-11-24)

Progress with on-demand resource download:

2019 week 46 (2019-11-17)

2019 week 45 (2019-11-10)

2019 week 44 (2019-11-03)

2019 week 43 (2019-10-27)

2019 week 42 (2019-10-20)

2019 week 41 (2019-10-13)

2019 week 40 (2019-10-06)

2019 week 39 (2019-09-29) - nearly a year!

2019 week 38 (2019-09-22)

2019 week 37 (2019-09-15)

2019 week 36 (2019-09-08)

2019 week 35 (2019-09-01)

2019 week 34 (2019-08-25)

Emscripten upgrade (1.38.32->1.38.37, with SDL port 2.0.7->2.0.9; final target: 1.38.42)

2019 week 33 (2019-08-18)

2019 week 32 (2019-08-11)

2019 week 31 (2019-08-04)

2019 week 30 (2019-07-28)

2019 week 29 (2019-07-21)

2019 week 28 (2019-07-14)

2019 week 27 (2019-07-07)

2019 week 26 (2019-06-30) [renpy-]

2019 week 25 (2019-06-23)

2019 week 24 (2019-06-16) [renpy-]

2019 week 23 (2019-06-09)

2019 week 22 (2019-06-02)

2019 week 21 (2019-05-26)

2019 week 20 (2019-05-19)

2019 week 19 (2019-05-12)

2019 week 18 (2019-05-05)

2019 week 17 (2019-04-28)

2019 week 16 (2019-04-21)

2019 week 15 (2019-04-14)

2019 week 14 (2019-04-07)

2019 week 13 (2019-03-31)

2019 week 12 (2019-03-25)

2019 week 9 (2019-03-03)

2019 week 8 (2019-02-24)

2019 week 6 (2019-02-10)

2019 week 5 (2019-02-03)

2019 week 4 (2019-01-27)

2019 week 3 (2019-01-20)

2018 week 52 (2018-12-30)

2018 week 50 (2018-12-16)

2018 week 49 (2018-12-09)

2018 week 48 (2018-12-02)

2018 week 47 (2018-11-25)

2018 week 46 (2018-11-18)

2018 week 45 (2018-11-11)

2018 week 44 (2018-11-04)

2018 week 43 (2018-10-29)

2018 week 42 (2018-10-21)

2018 week 41 (2018-10-14)

2018 week 40 (2018-10-07)

2018 week 39 (2018-09-30)