RenPy to OrbitEngine


Devlog: Bringing your Ren'Py game into OrbitEngine

Ren'Py is the reference engine for visual novels, and a lot of you already have a project in it: a finished game, a demo, or a prototype you left in a drawer. Telling those people "start over from scratch" was never a good answer. So this week OrbitEngine got a Ren'Py importer.

You point it at your Ren'Py folder, and you get a real OrbitEngine project. It isn't a frozen copy. It's a project you can open in the editor, edit visually, and export to Windows, Linux and the Web.


How to use it

In the editor:

File > Import, export and translations > Import from Ren'Py…

You can also pick it straight from the New project dialog. Choose your Ren'Py game folder (the one that holds game/, or game/ itself), then choose where the new project should go. The import runs in the background, the project opens, and you get an import report.

If you prefer the command line:

orbit import-renpy path/to/my_renpy_game path/to/new_project

Your original game is never modified. The importer only reads it.


What gets converted

The goal was simple: the parts of a typical visual novel script should come through without any work from you.

Story structure

  • Every top-level label becomes a scene in OrbitEngine, with start first. Nested and local labels (label .foo) become labels inside their scene.
  • jump, call and return work as you'd expect. If one label falls through into the next, that becomes a jump.
  • menu: becomes a choice node, and conditions on individual choices are kept.
  • if / elif / else and while become conditions and labels.

Characters and dialogue

  • define e = Character("Eileen", color="#c8ffc8") becomes a character, name color included.
  • All the usual dialogue forms work: e "Hello", narration, "Name" "Hello", and extend.
  • e happy "Hello" changes Eileen's expression and then says the line.
  • Text tags {b}, {i}, {color}, {w} and {p} keep the same markup.

Staging

  • scene bg room with fade, show eileen happy at left with dissolve and hide are all supported.
  • Transitions map to their closest OrbitEngine equivalent: dissolve, fade, Dissolve(t), Fade(…) and pixellate. vpunch and hpunch become a camera shake.
  • pause 1.0 becomes a wait.

Audio

  • play music … fadein, stop music fadeout, play sound, play voice and voice.

Variables

  • default / define with simple values (booleans, numbers, text, lists, dicts) become project variables.
  • $ x = …, +=, -= and $ inventory.append(…) become variable changes.
  • persistent.x becomes a persistent variable.

Assets

  • PNG and JPG images, plus OGG and WAV audio, are copied into the project library.
  • Ren'Py's naming conventions are understood. images/eileen happy.png becomes the happy expression of the eileen character, and image bg room = "room.jpg" becomes a background.

Translations

  • Your tl/<language>/ folders (dialogue blocks and strings) become the project's translations, so a localized game stays localized.

What doesn't convert (yet), and how we handle it

I'd rather be honest here than oversell it. Ren'Py is huge, and part of it is plain Python, so some things can't be translated one to one:

  • Screens, styles, ATL transforms, layered images and the gui/ folder are skipped. In OrbitEngine, the interface is built in the visual UI editor instead.
  • init python blocks and complex Python are kept as commented script nodes, so nothing is silently lost. You can rewrite them in Rhai, OrbitEngine's scripting language.
  • Conditions too complex for the visual editor become script conditions. Give those a quick check.
  • NVL mode uses the normal text box for now. Sound loops and custom audio channels aren't handled.
  • WebP, GIF, MP3, FLAC and similar formats are listed as skipped. Convert them to PNG or OGG, then drop them into the Library.

The key rule: an import never fails because of one strange line. Anything the importer doesn't understand is reported, not fatal. A final repair pass replaces any node that wouldn't validate with a commented script, so the project always opens and always validates. From there, you fix things at your own pace.


The import report

Every import writes an import-report.md into the new project, and the editor shows it right away. It contains:

  • counts of everything converted: scenes, labels, nodes, characters, variables, strings, images, audio, and translations per language
  • every statement that wasn't converted, with its file and line number in your original .rpy files
  • every approximation the importer made

After that, open the Problems panel. Its warnings take you straight to the spots worth a second look.


Why bother?

Once your story is in OrbitEngine, you get:

  • a no-code visual editor with a scene graph and a live preview that hot-reloads as you edit
  • one-click exports to Windows, Linux and the Web. The web player is a lazily loaded WebGL2 build that we keep under a strict size budget (about 1.4 MB brotli for the engine itself).
  • rollback, skip/auto, history, multi-slot saves, persistent variables and translations, all built in
  • an AI assistant wired into the editor that can read and edit your project live, if you want it

On the technical side, the importer is its own module (orbit_renpy). It ships only with the editor and the CLI, never with your exported game. It adds zero bytes to what your players download.


What's next

  • Better coverage of common Python patterns, so fewer lines end up as commented scripts
  • NVL mode support
  • Automatic conversion of more image and audio formats

If you have a Ren'Py project, try importing it and send me the report. Every unconverted line in a real game tells me what to support next. Drop it in the comments or on the Discord.

Thanks for following along! 🚀

Get OrbitEngine

Leave a comment

Log in with itch.io to leave a comment.