If you reached this page through a search engine, you may not be viewing the documentation for the latest version of Ren'Py. Please go to the main Ren'Py page and choose "View the Ren'Py Reference Manual".

Writing Visual Novels with Ren'Py

The Ren'Py Tutorial

Last Updated: 2004-12-14 14:40

  1. A Simple Ren'Py Script
  2. The Structure of a Ren'Py Script
  3. Lexical Structure of Statements
  4. Grammar Rules
  5. Dialogue: The Say Statement
  6. Menus
  7. Displaying Images
  8. Image and Scene Functions
  9. Transitions
  10. Control Statements
  11. Python Statements
  12. Starting a Game
  13. Saving, Loading, and Rollback
  14. Persistent Data
  15. Interaction Functions
  16. Overlays
  17. Sound and Music Functions
  18. Configuration Variables
  19. Properties and Styles
  20. Obfuscating your Game
  21. Localizing Ren'Py
  22. Changing the Keymap
  23. UI Functions
  24. Function Index
  25. Variable Index
  26. Property Index
  27. Style Index
  28. Example Script

Ren'Py is a programming language and runtime, intended to ease the creation of visual-novel type games. It contains features that make it easy to display thoughts, dialogue, and menus; to display images to the user; to write game logic; and to support the saving and loading of games. Ren'Py tries to be like an executable script, allowing you to get a working game without much more effort than is required to type the game script into the computer.

Ren'Py is implemented on top of python, and that python heritage shows through in many places. Many Ren'Py statements allow python expressions to be used, and there are also Ren'Py statements that allow for the execution of arbitrary python code. Many of the less-used features of Ren'Py are exposed to the user by way of python. By only requiring use of the simplest features of python, it's hoped that Ren'Py will be usable by all game authors.

A Simple Ren'Py Script

The following is a simple but complete Ren'Py script. The colors are added to make it easier to read, and aren't part of the script proper.

    image whitehouse = Image("whitehouse.jpg")
    image eileen happy = Image("eileen_happy.png")
    image eileen upset = Image("eileen_upset.png")

label start:
    $ e = Character('Eileen')

    scene whitehouse
    show eileen happy

    e "I'm standing in front of the White House."

    show eileen upset

    e "I once wanted to go on a tour of the West Wing, but you have to 
       know somebody to get in."

    "For some reason, she really seems upset about this."      

    e "I considered sneaking in, but that probably isn't a good idea."

This example, shows many aspects of a Ren'Py script. The first four lines of the script serve to load in three images. After the label indicating the start of the game, a character is declared. The script then proceeds to display a picture of a character on top of a background image, and to have the character say two lines of dialogue, changing her picture in between. The POV character then thinks a line of dialogue, before the character says her final line.

We'll go into detail into what each of the statements here does over the course of this tutorial. For now, however let me just point out that the first 6 statements initialize the game, while the last 7 (starting with "scene") show images and display dialogue. As such, the bulk of a game is more like the last 7 then the first 6.

Of particular note is that a keyword isn't required to introduce dialogue. This allows visual novels consisting mostly of dialogue to be expressed in a concise form.

The Structure of a Ren'Py Script

The largest division of a Ren'Py script is into files. By default, Ren'Py reads the script from all files ending in .rpy found in the game underneath the directory in which Ren'Py is installed. These script files may be read in any order, and all of them together make up a Ren'Py script.

Each of these files is divided into a series of logical lines. The first logical line of a file begins at the start of a file, and another logical line begins after each logical line ends, until the end of the file is reached. By default, a logical line is terminated by the first newline encountered. However, a line will not terminate if any of the following are true:

These rules should be the same as for Python.

Ren'Py also supports comments. A comment begins with a hash mark that is not contained within a string, and continues to, but does not include, the next newline character. Some examples are:

# This line contains only a comment.
scene whitehouse  # This line contains a statement as well.

If, after eliminating comments, a logical line is empty, that logical line is ignored.

Logical lines are then combined into blocks. Two logical lines are in the same block if the lines have the same indentation preceding them, and no logical line with a lesser amount of indentation occurs between the two lines. In the following example:

line 1
    line a
    line b
line 2
    line c
    line d

There are three blocks. One block contains lines 1 and 2, another lines a and b, and the third contains lines c and d. This example can also serve to illustrate the concept of a block associated with a line. A block is associated with a line if the block starts on the next logical line following the line. For example, the block containing lines a and b is associated with line 1.

There are three kinds of blocks in an Ren'Py program. The most common is a block containing Ren'Py statements. Other blocks may contain menu entries or python code. The top-level block (the one that contains the first line of a file) is always a block of Ren'Py statements.

Lexical Structure of Statements

Before we can discuss statements, however, we must first discuss the tokens statements are built up out of. So here's a short list of all the tokens we use.

Keywords are words that appear in the source code. They're used to introduce a statement, or to delimit parts of a statement. You'll see keywords throughout the descriptions of statements. In grammar rules, keywords are in quotes. The keywords are:


A names consist of an alphabetic character or number, followed by zero or more alphabetic characters or underscores, so long as the string isn't a keyword.

An image_name is a list of one or more names, separated by a space.

A string begins with a " or a ', and continues until a matching unescaped " or ' is reached. Runs of whitespace inside a string are collapsed into a single space character, allowing strings to span multiple lines. The \ character is used inside the string to escape special characters, such as whitespace, quotes, and (as \n) to include a newline.

A simple_expression is a python expression that starts with a name, a string, or any python expression in parenthesis. This may be followed by any number of the following:

In general, simple expressions are strings, names, or method calls. They are not expected to contain operators.

A python_expression is an arbitrary python expression that may not include a colon. These expressions are generally used to express the conditions in the if and while statements.

Grammar Rules

We will be giving grammar rules for some of the statements. In these rules, a word in quotes means that that word is literally expected. Parenthesis are used to group things together, but they don't correspond to anything in the source code. Star, question mark, and plus are used to indicate that the token or group they are to the right of can occur zero or more, zero or one, or one or more times, respectively.

If we give a name for the rule, it will be separated from the body of the rule with a crude ascii-art arrow (->).

Dialogue: The Say Statement

As the bulk of the content of a visual novel is presented to the user in the form of dialogue or thoughts, it's important that the ability to display text to the user be as convenient as possible. In Ren'Py, both actions are done through the say statement. The say statement doesn't require a keyword to introduce it. Instead, it consists of either a single string, or a simple_expression followed by a string.

say_statement -> ( simple_expression )? string ( "with" simple_expression )?

We can distinguish two forms of the say statement, depending on if the simple_expression is provided. The single-argument form of say consists only of a single string. This form causes the string to be displayed to the user without any label as to who is saying it. Conventionally, this is used to indicate to the user thoughts or narration.

"I moved to my left, and she moved to her right."
"So we were still blocking each other's path."

"I then moved to my right, and at the same time she moved to her

"We could be at this all day."

The two-argument form of the say statement first evaluates the expression to see what its value is. If the expression returns a string, that string is used as a character name to indicate who is saying the dialogue. If it returns an object, that object is responsible for displaying the dialogue to the user.

The most common type of object used in a dialogue statement is a Character object. Character objects have associated with them a name and a color. When a character object is asked to display a line of dialogue, it labels it with the character name in the character's signature color. In general, strings are used to indicate the names of lesser characters or ones who we have not discovered the name of yet, while character objects are used to indicate important characters.

"Girl" "Hi, my name is Eileen."

e "Starting today, I'll be living here."

Finally, the string in a dialogue is subject to interpolation of variables. A string variable can be interpolated with %(name)s, while a number requires %(name)d. For example:

e "I know all about you."

e "I know that you're %(player_age)d years old, and your zodiac 
   sign is %(player_sign)s."

When the first object is a character object, that character object is given given complete control over how text is displated. As an example of this, the standard library includes a character object called "centered", which displays the text centered on the screen, without any background window.

centered "American Bishoujo presents..."
centered "The Ren'Py Demo Game"

The say statement also takes a with clause that is used to control the transition that is used to introduce the dialogue or thought. Please see the section on transitions for details on how to use the with clause.


Menus present a user with a list of choices that can be made. In a visual novel game, menus are the primary means by which the user can influence the game's story.

menu_statement -> "menu" ( name )? ":"

A menu statement consists simply of the word menu, an optional name, and a colon. If the name is supplied it's treated as a label for this menu statement, as if the menu statement was preceded by a label statement. (See the section on control flow for details about the label statement.)

The menu statement must have a block associated with it. This block must contain one or more menuitems in it. There are four kinds of menuitems that can be contained in a menu block.

caption_menuitem -> string

The first kind of menuitem is simply a string. This string is placed into a menu as a caption that cannot be selected. In general, captions are used to indicate what the menu is for, especially when it is not clear from the choices.

choice_menuitem -> string ( "if" python_expression )? ":"

The second kind of menuitem gives a choice the user can make. Each choice must have a block of Ren'Py statements associated with the choice. If the choice is selected by the user, then block of statements associated with the choice is executed. A choice may also have an optional if clause that includes a python expression. This clause gives a condition that must be satisfied for the choice to be presented to the user. The terminating colon is what indicates that this menuitem is a choice.

set_menuitem -> "set" simple_expression

The third kind of menuitem gives an expression that yields a set. There may only be one of this kind of menuitem per menu. If present, it's used to filter the list of choices shown to the user.

with_menuitem -> "with" simple_expression

The final kind of menuitem is a with clause. This is used to specify the transition that introduces this menu. Please see the section on transitions for a discussion of this.

When a menu is to be shown to the user, the first thing that happens is that a list of captions and choices is built up from the menuitems associated with the menu. Each of the choices that has an expression associated with it has that expression evaluated, and if it evaluates to false, that choice is removed from the list. Finally, if a set is present, it is checked to see if the text of a choice is in the set. If the text is found, the choice is removed from the list.

If no choices survive this process, the menu is not displayed and execution continues with the next statement. Otherwise, the menu is presented to the user. When the user makes a choice, the text of that choice is added to the set (if one is present), and execution continues with the block associated with the choice. When that block finishes, execution continues with the statement after the menu.

Here's a fairly complicated menu that uses all three kinds of menuitems. Most menus in actual games will not be this complicated.

menu what_to_do:

    # Ensure that we can only do a given thing once.
    set what_to_do_set
    "What should we do today?"

    "Go to the movies.":
        "We went to the movies."

    "Go shopping.":
        "We went shopping, and the girls bought swimsuits."
        $ have_swimsuits = True

    "Go to the beach." if have_swimsuits:
        "We went to the beach together. I got to see the girls in their
         new swimsuits."

This menu will only allow a given activity to be chosen once, and will allow the user to chose to go to the beach only if the user has chosen to go shopping.

Displaying Images

Without the ability to display images to the user, a visual novel would be a text adventure. Ren'Py controls image display by using a scene list, a list of things to be displayed to the user. Every time an interaction starts (that is, a line of dialogue or a menu is displayed), the things in the scene list are drawn to the screen, with the first being in the back and the last being in the front. A number of statements manipulate the scene list. Before we can explain them, however, we should first define a few terms.

An image_name is a space-separated list of names that's used to refer to an image. This list of names may not include keywords in it. The first element of the image name is known as the image tag, and is treated specially.

A Displayable is a python object implementing an interface that allows it to be displayed to the screen. A transform is a function that, when applied to a Displayable, returns a new Displayable. Transforms are used to change the way an image is displayed to the user. A transform_list is a comma-separated list of transforms. Display lists are applied from left to right.

image_spec -> image_name ( "at" transform_list )? ( "with" simple_expression )?

An image_spec is an image name, an optional at list of transformers, and an optional with clause giving a transition. The image name is used to specify an image that will be shown. The at clause is a list of transformations that are applied to that image, which can control things like the placement of the image on the screen. The with clause is used to specify the transition that the image is being shown or hidden with. It will be discussed more in the section on transitions.

image_statement -> "image" image_name "=" python_expression

The first display statement is the image statement, which does binds an image name with a displayable defining that image. As the list of name bindings, is never saved, the image statement can only appear inside of an init block. The most popular python expression to use here is Image, which takes as an argument an image filename to load. Another popular choice is Animation, which is defined elsewhere in this document.

An example of image in use is:

    image eileen happy = Image("eileen/happy.png")
    image eileen upset = Image("eileen/upset.png")
show_statement -> "show" image_spec

The next display statement is the show statement, which takes an image specifier and displays it on the screen. If an image with the same tag as the image given in the spec already exists on the scene list, it is replaced with the newly displayed Displayable. Otherwise, the new one is added to the end of the scene list (that is, closest to the user).

If an at list is present, the image is transformed with the at list before being added to the display list.

Automatically replacing an image with the same tag is a useful feature that allows characters to change expression without having to explicitly hide the old image.

scene_statement -> "scene" ( image_spec )?

The scene statement first clears the scene_list. If the optional image_spec is present, it is shown as if it was shown with the show statement. The best use for the image_spec on a scene command is show a background for the scene.

By default, no background is added to the screen. (See config.background to change this.) Without such a background, Ren'Py will produce odd results if there is not at least one image in the scene list that is the full size of the screen. So we strongly advise that the scene statement always be used with an image, and that image be one that takes up the full width and height of the screen.

We can put together the scene and show statements to get the following example:

scene living_room
show eileen happy at left

e "I'm feeling happy right now."

show eileen upset at left 

e "But sometimes, I can get upset for no good reason."
hide_statement -> "hide" image_spec

The hide statement is used to remove an image from the scene list. The image spec is parsed for a tag, and any image matching that tag is remove from the scene. The at list is ignored (but should be valid or omitted), and transitions associated with the image spec are run.

Hide is a rarely used display statement. The show statement automatically replaces an old image when a character changes emotion, and the scene image removes all images when the scene changes. Hide is generally only used for when a character leaves in the middle of a scene.

e "Well, I'll be going now."
hide eileen

"And with that, she left."

These four statements, along with the library of Displayables and transforms provided with Ren'Py, should be enough to render most scenes needed in a visual novel type game.

Parameterized Images

Show and scene statements may also take parameterized images. If a name of an image is a prefix of the name given in a scene or show statement, the rest of the name is considered to be parameters. Parameters may be names, but they may also be simple_expressions, which includes strings. Parameters are handed off to the object that the prefix is defined to, and that object is responsible for returning a displayable that can be shown to the user.

An example of this is the pre-defined text displayable, which displays text as if it was an image. For example, the following code:

show text "American Bishoujo Presents..."

Will display the given text as if it was an image. The game will not pause while the text is being displayed. Such an image can also be processed through an at clause, as in the following code which moves the text down from the top of the screen, while waiting 10 seconds or until the user makes some input.

show text "A PyTom Game" \
        at Move((0.5, 0.0), (0.5, 0.5), 4.0,
                xanchor='center', yanchor='bottom')

$ renpy.pause(10)

Please note that parameterized images are replaced in the same way that normal images are. (So you can only have one image created with text on the screen at a time, without creating a second ParameterizedText object.)

Image and Scene Functions

This section includes functions that are useful when defining images or composing scenes.

Image (filename, style='image_placement', **properties):

Returns a Displayable that is an image that is loaded from a file on disk.

filename - The filename that the image is loaded from. Many common file formats are supported.

The Image function is generally used in conjunction with the image statement. For example:

    image eileen happy = Image("9a_happy.png")
    image eileen vhappy = Image("9a_vhappy.png")
    image eileen concerned = Image("9a_concerned.png")
Solid (color):

Returns a Displayable that is solid, and filled with a single color. A Solid expands to fill all the space allocated to it, making it suitable for use as a background.

color - An RGBA tuple, giving the color that the display will be filled with.

Frame (filename, xborder, yborder):

Returns a Displayable that is a frame, based on the supplied image filename. A frame is an image that is automatically rescaled to the size allocated to it. The image has borders that are only scaled in one axis. The region within xborder pixels of the left and right borders is only scaled in the y direction, while the region within yborder pixels of the top and bottom axis is scaled only in the x direction. The corners are not scaled at all, while the center of the image is scaled in both x and y directions.

filename - The file that the original image will be read from.

xborder - The number of pixels in the x direction to use as a border.

yborder - The number of pixels in the y direction to use as a border.

For better performance, have the image file share a dimension length in common with the size the frame will be rendered at. We detect this and avoid scaling if possible.

    style.window.background = Frame("frame.png", 125, 25)
Animation (*args):

A Displayable that draws an animation, which is a series of images that are displayed with time delays between them.

Odd (first, third, fifth, etc.) arguments to Animation are interpreted as image filenames, while even arguments are the time to delay between each image. If the number of arguments is odd, the animation will stop with the last image (well, actually delay for a year before looping). Otherwise, the animation will restart after the final delay time.

    image animation = Animation("frame_1.png", 0.25,
                                "frame_2.png", 0.25,
                                "frame_3.png", 0.25)

Frames are normally used in conjunction with styles, to provide the displayable that is the background for a window containing a menu or dialogue.

Position (**properties):

Position, when given position properties as arguments, returns a callable that can be passed to the "at" clause of a show or scene statement to display the image at the given location. See the section below on position properties to get a full explanation of how they are used to lay things out, but hopefully this example will show how Position can be used:

    left = Position(xpos=0.0, xanchor='left')
    center = Position(xpos=0.5, xanchor='center')
    right = Position(xpos=1.0, xanchor='right')
    top = Position(xpos=0.5, xanchor='center', ypos=0.0,

show eileen happy at left
renpy.ParameterizedText (style='default', **properties):

This can be used as an image. When used, this image is expected to have a single parameter, a string which is rendered as the image.

This is used to impelement the text image. The user may also want to instatiate their own ParameterizedText object (in an init block) if they want to have more than one bit of text on the screen at once, or if they want to change the style (and therefore the position) of that text.

    image text1 = renpy.ParameterizedText(ypos=0.25)

show text "centered."
show text1 "1/4 of the way down the screen."
Pan (startpos, endpos, time):

Pan, when given the appropriate arguments, gives an object that can be passed to the at clause of that image to cause the image to be panned on the screen. The parameters startpos and endpos are tuples, containing the x and y coordinates of the upper-left hand corner of the screen relative to the image. Time is the time it will take this position to move from startpos to endpos.

As the current implementation of Ren'Py is quite limited, there are quite a few restrictions that we put on pan. The big one is that there always must be a screen's worth of pixels to the right and below the start and end positions. Failure to ensure this may lead to inconsistent rendering.

Hopefully, an example will demonstrate how Pan is used. For this example, assume that the screen is 800 x 600, and that the image marspan is 2400 x 600 pixels in size. We want to take 10 seconds to pan from left to right on the image.

scene marspan at Pan((0, 0), (1600, 0), 10.0)

Please note that the pan will be immediately displayed, and that Ren'Py will not wait for it to complete before moving on to the next statement. This may lead to the pan being overlayed with text or dialogue. You may want to use a call to renpy.pause to delay for the time it will take to complete the pan.

Finally, also note that when a pan is completed, the image locks into the ending position.

Move (startpos, endpos, time, **properties):

Move is similar to Pan, insofar as it involves moving things. But where Pan moves the screen through an image, Move moves an image on the screen. Specifially, move changes the position style of an image with time.

Move takes as parameters a starting position, an ending position, the amount of time it takes to move from the starting position to the ending position, and extra position properties. The positions are given as tuples containing xpos and ypos properties. The positions may be integer or floating point, but it's not permissable to mix the two.

The following example moves a ball from the upper-left to the lower-right of the screen, taking 10 seconds to do so.

show ball at Move((0.0, 0.0), (1.0, 1.0), 10.0,
                  xanchor="center", yanchor="center")

In general, one wants to use Pan when an image is bigger than the screen, and Move when it is smaller.


By default, Ren'Py displays each scene list by replacing the old scene list with a new one. This is appropriate in general (such as for emotion changes), but it may be boring for large scene changes, such as a change in scene or a character entering or leaving the scene. Ren'Py supports transitions that control how changes to the scene lists are exposed to the user.

Transitions occur between the last scene list that was shown to the user, and the current scene list that has been updated using the scene, show, or hide statements. A transition takes both lists as input, and is responsible for displaying the transition between them to the user. Each transition runs for a given amount of time, but may be dismissed early by the user. Once a transition is shown, the scene list is considered shown for the purposes of future transitions.

with_statement -> "with" simple_expression

Transitions are introduced with the with statement. The with statement takes an expression that is suitable for use with the with statement (that is, a callable that takes as input two scene lists), and runs that transition. Alternatively, if the expression is None, then the with statement has the effect of showing the scene to the user, and returning instantly. This is useful in conjunction with a future with statement, so that only some changes to the scene list will be transitioned in.

An example is in order. First, let us define a few objects that can be passed in as the argument to a with statement:

    # Fades to black, then to the new scene.
    fade = Fade(0.5, 0, 0.5)

    # Dissolves between old and new scenes.
    dissolve = Dissolve(0.5)

A simple use of with would be to place it after a series of show and hide statements of the program. As an example:

scene whitehouse
show eileen happy
with fade

This series of statements will cause the old scene (displayed before these statements started) to fade to black, and be replaced with the new scene all at once. This is a useful behavior, for example, when we are replacing a scene with a new one, such as when the story changes locations.

scene whitehouse
with None
show eileen happy
with dissolve

The "with None" statement is useful to break changes to the scene list into parts, as in the example above. When run, the background will be instantly shown, and then the character image will be dissolved in over the background.

Another use of the "with None" statement is to remove transient elements before a transition begins. By default, the scene list includes transient elements like dialogue, thoughts, and menus. "with None" always executes without these elements, and so gets rid of them.

The "show", "hide", and "scene" statements all take a with clause. One of these statement with a with clause associated with it is actually converted into three statements: A "with None" statement, the original statement sans the with clause, and the with clause as a with statement. For example:

scene whitehouse with fade
show eileen happy at left with dissolve
show lucy happy at right with dissolve


with None
scene whitehouse
with fade
with None
show eileen happy at left
with dissolve
with None
show lucy happy at right
with dissolve

This has the effect of fading out the old scene and fading in the new background, then dissolving in the characters one after the other.

We also allow with clauses to be supplied for say and menu statements. When a with clause is supplied on one of these statements, the transition is used to introduce the say or menu element. For example,

e "How are you doing?" with dissolve

Will dissolve in a line of dialogue. The line of dialogue will be dismissed immediately, unless it is followed by a with statement or clause that causes it to transition to something else.

There is one variable that controls transitions:

default_transition = None
If not none, specifies a default transition that is applied to all say and menu statements that are not provided a with clause. This is only considered if the transitions preference is set to "All".

Functions that return things that are useful as arguments to with statements or clauses are:

Fade (in_time, hold_time, out_time, color=(0, 0, 0)):

This returns an object that can be used as an argument to a with statement to fade the old scene into a solid color, waits for a given amount of time, and then fades from the solid color into the new scene.

in_time - The amount of time that will be spent fading from the old scene to the solid color. A float, given as seconds.

hold_time - The amount of time that will be spent displaying the solid color. A float, given as seconds.

out_time - The amount of time that will be spent fading from the solid color to the new scene. A float, given as seconds.

color - The solid color that will be faded to. This is an RGB triple, where each element is in the range 0 to 255. This defaults to black.

Dissolve (delay):

This dissolves from the old scene to the new scene, by overlaying the new scene on top of the old scene and varying its alpha from 0 to 255.

delay - The amount of time the dissolve will take.

Control Statements

Control statements change the order in which statements in a Ren'Py script execute. These statements allow for control transfers, conditional execution, and procedure calls.

label_statement -> "label" name ":"

The label statements assigns a name to a point in the program, allowing control to be transfered to this point by the jump or call statements. The label statement may have a block associated with it. If it does, the statement executed after the label is the first statement in the block. Otherwise, the next statement to be executed is the first statement after the label.

jump statement -> "jump" name

The jump statement unconditionally transfers control to the statement with the given name. If the name does not exist, an error is raised.

label loop_start:

e "Oh no! It looks like we're trapped in an infinite loop."

jump loop_start
call_statement -> "call" name ( "from" name )?

The call statement transfers control to the location given. It also pushes the name of the return site onto the return stack, allowing the return statement to return to the statement after the call site.

If the optional from clause is present, it has the effect of including a label statement with the given name as the statement immediately following the call statement. An explicit label is required here to ensure that saved games with return stacks can return to the proper place when loaded on a changed script. On the other hand, from clauses may be distracting when a game is still under development. We provide with Ren'Py a program, called "add_from", that adds from clauses to all bare calls in the game directory. This program should be run before a final release of your game is made. Be sure to make a backup of your game directory before running add_from.

return_statement -> "return"

If the return stack is not empty, the return statement pops the top return site off of it and transfers control there. Otherwise, it terminates execution without raising an error.

e "First, we will call a subroutine."

call subroutine from _call_site_1

e "Finally, we will exit the program."


label subroutine:
    e "Next, we will return from the subroutine."
if_statement -> "if" python_expression ":"
elif_clause -> "elif" python_expression ":"
else_clause -> "else" ":"

The if statement is used to conditionally execute a block of statements. It is the only statement that consists of more than one logical line in the same block. The initial if statement may be followed by zero or more elif clauses, concluded with an optional else clause. The expression is evaluated for each clause in turn, and if it evaluates to a true value, then the block associated with that clause is executed. If no expression evaluates to true, then the block associated with the else clause is executed. (If else clause exists, execution immediately continues with the next statement.) In any case, at the end of the block, control is transferred to the statement following the if statement.

if points >= 10:

   e "Congratulations! You're getting the best ending!"

elif points >= 5:

   e "It's the good ending for you."


   e "Sorry, you're about to get the bad ending."
while_statement -> "while" python_expression ":"

The while statement executes its block while the expression is true. Specifically, each time the while statement executes, it evaluates the expression. If the expression is true, control is transferred to the first statement of the block associated with the while loop. If it is false, then control is transferred to the next statement. The while statement is the statement that normally executes after the last statement in the block, causing the condition to be evaluated again and the loop to repeat.

This definition of a while loop means that it would be hard to implement statements like python's "continue" or "break". These statements can be easily faked with labels in the right places and jumps to those labels. This definition also means that it's possible to jump into the middle of the block associated with a while loop and, if at the end of the block the condition is true, have the while loop repeat the block.

while not endgame:

   "It's now morning. Time to get up and seize the day."

    call morning
    call afternoon
    call evening
    "Well, time to call it a night."

"Now it's time to wake up and face the endgame."
pass_statement -> "pass"

The pass statement can be used where a block is required, but there's no statement that can be placed in that block. When executed, pass has no effect.

For example, pass can be used in a menu if we don't want to take any action when a choice is selected.

    "Should I go to the movies?"

        call go_see_movie


"Now it's getting close to dinner time, and I'm starving."
init_statement -> "init" ":"

The init statement is used to introduce a block of code that should be run when the game first starts. When the game is first loaded, the script is scanned for init blocks, and code in init blocks is run in an arbitrary order. An init statement encountered during execution, however, is treated as a pass statement, and the block is not executed.

Python Statements

python_statement -> "$" python_code

There are two Ren'Py statements that allow python statements to be mixed with Ren'Py code. Any statement beginning with a dollar-sign ('$') will be interpreted as python code extending to the end of the logical line. This form can only include a python statement containing a single logical line. (So python control constructs cannot be used.)

python_block_statement -> "python" ( "hide" )? ":"

The other way to introduce python statements is with a python block statement. The block associated with this statement, along with any block inside those blocks, is interpreted as python code that is passed to the python interpreter. The block nesting structure is reflected in the python code that is interpreted, so that python control structures will work as advertised.

If the optional hide keyword is added to a python block statements, local variables created in the block will not be added to the store. The variables in the store can be accessed as attributes of the store, however.

# Toggle fullscreen mode.
$ config.fullscreen = not config.fullscreen

# Pointless python that uses a loop.
    for i in ('e', 'l'):
        globals()[i].points = 0

In general, if a Ren'Py construct exists that does what you want (like while or if), it should be used in preference to a python block, unless a large amount of code is to be executed with no user interaction.

Starting a Game

When Ren'Py is first invoked, it first tries to parse all the .rpy files in the game directory. If at least one .rpy file exists, it is loaded, and the script is then written out in a serialized form. If no .rpy files exist, but the serialized script does exist, the serialized script is read back in from disk.

Once the script is loaded, the first thing that occurs is that it is scanned for init blocks. These init blocks are then run immediately, in no particular order. The init blocks should do things like loading images and changing Ren'Py configuration. On no account should an init block try to display an image or interact with the user, as the display system is not yet initialized, and so such interaction will not work.

After the last init block has finished running, the display is initialized, and the actual game can begin. If the library is present it looks for a label named "splashscreen", and calls it if it exists and this is not a reset of Ren'Py. It then looks for the label "main_menu". If it exists, then that label is jumped to as the main menu. Otherwise, the library main menu is displayed. While library.main_menu allows this menu to be customized, the default main menu calles the label "start" when the user chooses "Start Game". The library main menu can also be manually accessed by jumping to _library_main_menu, which is useful if you want to display an intro before the menu is shown.

Saving, Loading, and Rollback

In Ren'Py, saving, loading, and rollback are three actions that share quite a bit of infrastructure. So, we'll discuss them together in this section. We will pay special attention as to what it takes to support loading of a saved game when the script changes between saving in loading, while running on the same Ren'Py version. Apart from designated bug-fix releases of Ren'Py, we do not plan to support loading a save created on an older version of Ren'Py in a newer release of Ren'Py. Once a game has been released once, it's expected that future releases of that game will use the same release of Ren'Py.

The state of the game in Ren'Py consists of two basic parts. The interpreter state consists of state that the user never manipulates directly, while the user state consists of variables that have been changed by the user, and objects reachable from those variables. Currently, the interpreter state that is saved consists of:

These items are automatically saved, restored, or rolled-back when the appropriate commands are invoked by the user. You do not need do anything for this state to be handled, and there is no way to prevent these things from being saved.

There are some important portions of interpreter state that are not saved, and therefore should only be changed in init: blocks. These are:

To deal with this, one should ensure that all images and config variables are set up in init blocks, and then left alone for the life of the game. In addition, as a scene list in a saved game may contain references to image names, once an image name is defined in a released version of the game, the image name should remain defined in all future release of the game.

The other kind of state that Ren'Py can save and restore is user state. User state consists of the values of all variables that are changed after the end of the init phase, as well as all data reachable from those variables.

It's important to clarify what it means for a variable to be changed. In Python and Ren'Py, variable names are bound to objects. The variable changes when a new object is assigned to it. It does not change when the object that is assigned to it changes.

As an example, in the following code:

      $ a = 0
      $ state = object()

$ a = 1
$ b = [ ]
$ state.love_love_points = a + 1

The variables a and l are updated after the end of the init: block, while state is not updated. The variable a is assigned a different integer object, while b is assigned a new empty list. While a field in the Character object is changed by the last statement, state still points to the same object, and therefore it is not considered to have changed. (And therefore, the object isn't considered to be part of user state.)

User state is gathered by first finding all variables changed since the end of the init phase. We then find all objects reachable from one of those variables through some combination of field access, iteration, or iteration over items (as in a dictionary). This combination of variable bindings and object values comprises the user state.

It's important that everything that's kept in the user state can be pickled (serialized). Thankfully, most python constructs can be, including booleans, integers, floating-point numbers, strings, lists, tuples, dictionaries, and most objects. You can also refer to your own classes and functions, provided that they are defined in a python block (not a python hide block) inside an init block, and always exist with the same name in later versions of the script. Things that can't be pickled include strange objects like iterators and files. Usually, you don't have to worry much about this.

As the game is played, Ren'Py logs all changes to user and interpreter state. When the game is saved, it writes this log out to disk, alongside the current state.

When the game is loaded back in, the variables are reset to what the were when the init code in the current version of the script finished running. The saved user state is then merged with this, with saved user state overriding any variable that was also assigned in the init code. Finally, a rollback is triggered.

The rollback that is triggered on load ends when it can find a statement that has the same name as it had when it was encountered in the log. When the script hasn't changed, all statement have the same name, so the effect of the rollback is to bring control back to the start of the statement that was executing when the user saved. When the script has changed, however, the only statements that retain their names are statements that have an explicit name specified. (Such as a label, a menu with a name, or a call with a from clause.) The game will rollback to the start of one of these statements. To ensure that rollback works correctly when the script changes, a label that exists in a released version of the game should continue to exist in all future version of the game.

When a rollback occurs, both user and interpreter state are restored to what they were when the statement that is being rolled-back to began executing. The statement is then executed again, and play continues normally.

Please note that we can only roll back the currently-executing statement, and not the return sites listed on the return stack. If the name of a return site changes, we will not be able to return from a procedure call, and the script will crash. If a return site has an explicit name, however, that name is returned to even if the script change. Because of this, it's important that every call site in a released game have a from clause associated with it.

Finally, if allowed, rollback can be invoked explicitly by user input. When such a rollback occurs, we first look for a previous statement that is a checkpoint (checkpoints are say and menu statements, as well as python blocks that called renpy.checkpoint()). Once a checkpoint is found, we look for a statement which has a name that exists in the current script (this is normally the same statement). We then rollback to that statement and begin executing again.

The upshot of this is that when a user rollback occurs, the game is reverted to the start of the say or menu statement that executed before the currently executing one.

There is one variable that controls the behavior of loading and saving:

save_name = ''
This is a name that will be associated with save files. It's expected that the game will update this on a regular basis with the name of the section of the script that is currently executing. When a save file is shown to the user, this name will be shown with it. Alternatively, never change this an no name will be shown.

While these rules may seem to be complex, it's hoped that in practice they can be reduced to a simple heuristic: Any variable changed outside of an init block, and any object reachable from such a variable, will be saved, loaded, and rolled-back properly.

Persistent Data

Persistent data is data that is saved that is not associated with a single game. One possible use of it is to store information about things that have been unlocked, such as an image gallery that is only unlocked when an ending has been reached.

Persistent data is stored as fields on the "persistent" object. This object is special, as uninitialized fields are forced to take the value None. A change to this object is visible in every game that the player undertakes.

Take as an example an unlockable image gallery. The code to display the gallery look like:

label gallery:

    if not persistent.gallery_unlocked:
        show background
        centered "You haven't unlocked this gallery yet."
        $ renpy.full_restart()

    # Actually show the gallery here.

Then, to unlock the gallery, run the following code somewhere in your program.

$ persistent.gallery_unlocked = True

Interaction Functions

The following functions either implement new game behavior that didn't merit its own statement, or complement the behavior of statements.

    e = Character("Eileen", color=(200, 255, 200, 255))

e "My name is shown in full, and in green."

It's probably best to define Character objects in an init block, and not attempt to set attributes on them.

Character (name, who_style='say_label', what_style='say_dialogue', window_style='say_window', **properties):

The character object contains information about a character. When passed as the first argument to a say statement, it can control the name that is displayed to the user, and the style of the label showing the name, the text of the dialogue, and the window containing both the label and the dialogue.

name - The name of the character, as shown to the user.

who_style - The name of the style that is applied to the characters name when it is shown to the user.

what_style - The name of the style that is applied to the body of the character's dialogue, when it is shown to the user.

window_style - The name of the style of the window containing all the dialogue.

properties - Additional style properties, that are applied to the label containing the character's name.

In addition to the parameters given above, there are also a few other keyword parameters:

who_prefix - A prefix that is prepended to the name.

who_suffix - A suffix that is appended to the name.

what_prefix - A prefix that is prepended to the text body.

what_suffix - A suffix that is appended to the text body.

DynamicCharacter (name_expr, who_style='say_label', what_style='say_dialogue', window_style='say_window', **properties):

A DynamicCharacter is similar to a Character, except that instead of having a fixed name, it has an expression that is evaluated to produce a name before each line of dialogue is displayed. This allows one to have a character with a name that is read from the user, as may be the case for the POV character.

name_expr - An expression that, when evaluated, should yield the name of the character, as a string.

All other parameters are as for Character.

renpy.pause (delay=None, music=None):

When called, this pauses and waits for the user to click before advancing the script.

delay - The number of seconds to delay.

music - If supplied, this gives the number of seconds into the background music that we will delay until. If music is playing, this takes precedence, otherwise the delay parameter take precedence.

Returns True if the pause was interrupted by the user hitting a key or clicking a mouse, or False if the pause was ended by the appointed time being reached.

renpy.display_say (who, what, who_style='say_label', what_style='say_dialogue', window_style='say_window', who_prefix='', who_suffix=': ', what_prefix='', what_suffix='', **properties):

who - Who is saying the dialogue, or None if it's not being said by anyone.

what - What is being said.

For documentation of the various prefixes, suffixes, and styles, please read the documentation for Character.

renpy.display_menu (items, window_style='menu_window'):

Displays a menu containing the given items, returning the value of the item the user selects.

items - A list of tuples that are the items to be added to this menu. The first element of a tuple is a string that is used for this menuitem. The second element is the value to be returned if this item is selected, or None if this item is a non-selectable caption.

renpy.input (prompt, default='', length=None):

This pops up a window requesting that the user enter in some text. It returns the entered text.

prompt - A prompt that is used to ask the user for the text.

default - A default for the text that this input can return.

length - If given, a limit to the amount of text that this function will return.

$ name = renpy.input("What is your name?", "Joe User", length=20)

e "Pleased to meet you, %(name)s."
renpy.imagemap (ground, selected, hotspots, unselected=None, overlays=False, style='imagemap', **properties):

Displays an imagemap. An image map consists of two images and a list of hotspots that are defined on that image. When the user clicks on a hotspot, the value associated with that hotspot is returned.

ground - The name of the file containing the ground image. The ground image is displayed for areas that are not part of any hotspots.

selected - The name of the file containing the selected image. This image is displayed in hotspots when the mouse is over them.

hotspots - A list of tuples defining the hotspots in this image map. Each tuple has the format (x0, y0, x1, y1, result). (x0, y0) gives the coordinates of the upper-left corner of the hotspot, (x1, y1) gives the lower-right corner, and result gives the value returned from this function if the mouse is clicked in the hotspot.

unselected - If provided, then it is the name of a file containing the image that's used to fill in hotspots that are not selected as part of any image. If not provided, the ground image is used instead.

overlays - If True, overlays are displayed when this imagemap is active. If False, the overlays are suppressed.

    $ result = renpy.imagemap("ground.png", "selected.png", [
        (100, 100, 300, 400, "eileen"),
        (500, 100, 700, 400, "lucy")

    if result == "eileen":
        e "You picked me!"

    elif result == "lucy":
        e "It looks like you picked Lucy."   
renpy.transition (trans):

Sets the transition that will be used for the next interaction. This is useful when the next interaction doesn't take a with clause, as is the case with pause, input, and imagemap.

renpy.full_restart ():

This causes a full restart of Ren'Py. This clears the store and configuration, and re-runs init before branching off to start. It's very close to what happens when we quit out and re-run the interpreter, save for some caches not being cleared.

renpy.quit ():

This causes Ren'Py to exit entirely.

renpy.watch (expression, style='default', **properties):

This watches the given python expression, by displaying it in the upper-right corner of the screen (although position properties can change that). The expression should always be defined, never throwing an exception.

A watch will not persist through a save or restart.

renpy.windows ():

Returns true if we're running on Windows. This is generally used as a test when setting styles.

Finally, there exists an object named renpy.random. This object is a random number generator that implements the Python random number generation interface. However, unlike the standard random number generator, this object cooperates with rollback, generating the same numbers regardless of how many times we rollback. It should be used instead of the standard Python random module.


Overlays are used to display information above the scene currently displayed. The overlay is regenerated each time an interaction with the user begins, making it suitable for displaying to the user things like statistics or dates. The overlay is generally displayed whenever transient things (like dialogue, thoughts and menus) are.

Overlays are set up by adding to the config.overlay_functions list a python function which, when called, returns a list of Displayables that are added to the overlay. These functions are called for each interaction, which allows the overlay to change to reflect the status of game variables.

As an example, take the following code fragement. When added to a program, this displays a date image in the upper-right corner of the screen (as is done in Kanon). The image shown is based on the variable date. If date is None, then no date is shown. Otherwise, a png file beginning with the value of date is shown.

    $ date = "mar25"

    python hide:
        def date_overlay():
            if date:
                return [ Image(date + ".png", 
                               xpos=1.0, xanchor="right",
                               ypos=0.0, yanchor="top") ]
                return [ ]


Like all config variables, config.overlay_functions should only be changed in an init block. If you need to toggle an overlay on and off, then the overlay function should be conditioned on some normal variable, returning an empty list if it is false. This is done in the example above when date is None.

Sound and Music Functions


Ren'py supports playing sounds in the background, using the renpy.play function. These sounds must be in wav files, but may live in an archive file.

renpy.play (fn, loops=0):

This plays the given sound. The sound must be in a wav file, and expected to have a sample rate 44100hz (changable with config.sound_sample_rate), 16 bit, stereo. These expectations may be violated, but that may lead to conversion delays.

Once a sound has been started, there's no way to stop it.

fn - The name of the file that the sound is read from. This file may be contained in a game directory or an archive.

loops - The number of extra times the sound will be played. (The default, 0, will play the sound once.)

$ renpy.play("quindar.wav")
e "Ground control to Major Tom."

Sounds can also be associated with buttons, menu choices, and imagemaps becoming hovered and activated. See the section on sound properties in styles for how to used this.


Ren'Py supports playing music in the background of your game. Theoretically, Ren'Py should support any format SDL_mixer supports (mp3, ogg, midi, mod, and more), but we've only tested mp3 and ogg support. The music that is currently playing is kept as part of the interpreter state, and is restored when a player loads a game or rolls back the game state, automatically.

Music must exist in real files (not archive files), named with paths starting at the game directory. The filename that the music is stored in is saved with the game state. If the filename changes between game releases, the game will still proceed, but music that was playing when the game was saved may be absent when the game is loaded.

The playing of music is controlled by a pair of Python function calls:

renpy.music_start (filename, loops=-1, startpos=0.0):

This starts music playing. If a music track is already playing, stops that track in favor of this one.

filename - The file that the music will be played from. This is relative to the game directory, and must be a real file (so it cannot be stored in an archive.)

loops - The number of times the music will loop after it finishes playing. If negative, the music will loop indefinitely. Please note that even once the song has finished, rollback or load may cause it to start playing again. So it may not be safe to have this set to a non-negative value.

startpos - The number of seconds into the music to start playing.

renpy.music_stop ():

Stops the currently playing music track.

Here's an example of a script fragment that comes with musical accompaniment.

e "Lemma kept asking for support for playing music."

# Loads game/music/taps.mp3
$ renpy.music_start("music/taps.mp3")

show eileen flaming mad

e "Well, we'll show him."

$ renpy.music_stop()

show eileen wink

e "Just kidding."

The config.fade_music variable is used to control the fading out of old music when a new track is started.

Configuration Variables

Much of the the configuration of Ren'Py is done using configuration variable. These variable, when assigned in a python block, change the behavior of the interpreter. As configuration variables aren't saved, and many need to be set before the GUI initializes, it makes sense to set all configuration variable inside init blocks. An example setting of variables is:

    $ config.screen_width = 640
    $ config.screen_width = 480
config.screen_width = 800
This sets the width of the screen.
config.screen_height = 600
This sets the height of the screen.
config.background = None
This sets the default background that is shown in areas of the screen where there are no images. If the screen will always be filled with an image, it makes sense to set this to None, which will improve performance by not drawing a default background. Otherwise, this is interpreted as an RGBA tuple, which will be drawn before any image is.
config.debug = False
Enables some minor debugging functionality (mostly by turning some missing files into errors.) This should always be turned off in a release.
config.debug_sound = False
Enables debugging of sound functionality. This disables the supression of errors when generating sound. However, if a sound card is missing or flawed, then such errors are normal, and enabling this may prevent Ren'Py from functioning normally. This should always be False in a released game.
config.rollback_enabled = True
Should the user be allowed to rollback the game? If set to False, the user cannot interactively rollback. (The feature still works for loading savegames when the script changes.)
config.rollback_length = 128
When there are more than this many statements in the rollback log, Ren'Py will consider trimming the log.
config.hard_rollback_limit = 10
This is the number of steps that Ren'Py will let the user interactively rollback. Set this to 0 to disable rollback entirely, although we don't recommend that, as rollback is useful to let the user see text he skipped by mistake.
config.profile = False
If set to True, some profiling information will be output to stdout (wherever that may go to).
config.image_cache_size = 10
This is the number of images that can be in the image cache at once. This is mostly a suggestion, as if Ren'Py ever needs to display more than this number of images at once, it will cache them all. But it will stop predictively loading images when the cache is filled beyond this, and it will try to drop images from the cache until it shrinks below this size. If this is too small, there will be more disk access as images are loaded and re-loaded. If this is too large, memory usage will be increased as images are kept in memory.
config.predict_statements = 10
This is the number of statements, including the current one, to consider when doing predictive image loading. A breadth-first search from the current statement is performed until this many statements is considered, and any image referenced in those statements is potentially predictively loaded. Setting this to 0 will disable predictive loading of images.
config.debug_image_cache = False
If True, Ren'Py will print the contents of the image cache to standard output (wherever that goes) whenever the contents of the image cache change.
config.skip_delay = 100
The amount of time that dialogue will be shown for, when skipping statements using ctrl, in milliseconds.
config.archives = [ ]
A list of archive files that will be searched for images.
config.mouse = None
If this variable contains a string, that string is taken as an image file that a color mouse cursor is loaded from. It's probably best given as a PNG file, in which case the mouse can have transparency and translucency. If this is None, a black-and-white hardware mouse is used, and the speed of Ren'Py is slightly increased.
config.keymouse_distance = 5
This gives the number of pixels that the mouse is moved every 1/20th of a second when the keymouse is being used.
config.sound_sample_rate = 44100
The sample rate that the sound card will be run at. If all of your wav files are of a lower rate, changing this to that rate may make things more efficent.
config.overlay_functions = [ ]
A list of functions. When called, each function is expected to return a list of displayables, which are added to the overlay list. See the section on overlays for more.
config.annoying_text_cps = None
If not None, dialogue and thoughts will be "typed" onto the screen a few characters at a time. The value of this is the number of characters that will be typed onto the screen in a second. I strongly reccomend keeping this None. Not only does this interact poorly with the rest of Ren'Py, it's annoying.
config.fade_music = 0.0
This is the amount of time in seconds to spend fading the old track out before a new music track starts. This should probably be fairly short, so the wrong music doesn't play for too long.
config.keymap = dict(...)
This variable contains a keymap giving the keys and mouse buttons assigned to each possible operation. Please see the section on keymaps for more information.
config.sticky_positions = False
Sticky positions causes the at clause of a show or scene statement to be remembered for as long as that character is on the screen. Future show statements without at clauses will be given the remembered at clause. This lets us easily change the character's expression without changing their position on the screen.
library.file_page_length = 10
This is the number of save slots that are shown in the picker that's used by the load and save portions of the game menu.
library.thumbnail_width = 100
The width of the thumbnails that are taken when the game is saved. These thumbnails are shown when the game is loaded. Please note that the thumbnail is shown at the size it was taken at, rather than the value of this setting when the thumbnail is shown to the user.
library.thumbnail_height = 75
The width of the thumbnails that are taken when the game is saved. These thumbnails are shown when the game is loaded. Please note that the thumbnail is shown at the size it was taken at, rather than the value of this setting when the thumbnail is shown to the user.
library.main_menu = [ ( "Start Game", "start" ), ("Continue Game", "_continue"), ("Quit Game", "_quit") ]
This is used to give the main menu that is shown to the user when the game first starts. It is a list of tuples, where the first element of each tuple is the title of the menu button, and the second element is a label that we jump to when that button is selected. Two useful labels to use here are "_continue", which brings up the game menu in load mode, and "_quit", which terminates Ren'Py.
library.has_music = True
If True, the preference for enabling and disabling music will be presented to the user. This should be set to False if the game does not play any music.
library.has_sound = True
If True, the preference for enabling and disabling sound effects is presented to the user. This should be set to False if the game does not have any sound effects.
library.has_transitions = True
If True, the preference for enabling and disabling transitions is presented to the user. This should be set to False if the game does not have any transitions (that is, never uses the with statement or clause).

Properties and Styles

Ren'Py includes a style system that allows the user to control the styles of text and windows (including things that inherit from window, like buttons). A style inheritance mechanism allows a single change to (for example) font size to affect the entire game, while at the same time allowing fine-grained control when it is desired.

There are two families of style properties. Text properties are used to control the font, size, and color of text, while window properties are used to control the layout, background, and padding of windows. If a widget displays text inside a window (as a textbutton does), then it respects both text and window properties. A widget is a Displayable that can accept properties.

Many style properties take as an argument RGBA tuples. These are python tuples containing four values, representing the red, green, blue, and alpha components of a color. Each of these values is a number between 0 and 255. Alpha is used to control how opaque the thing should be, with 0 being transparent, and 255 being fully opaque. It's probably easiest to show an example of RGBA tuples being used as part of styles.


    # Selected menu choices should be yellow and solid.
    $ style.menu_choice_selected.color = (255, 255, 0, 255)
    # Unselected menu choices should be cyan and translucent.
    $ style.menu_choice_unselected = (0, 255, 255, 128)

Text Properties

The following are properties that can be applied to Displayables that take text styles.

font --- This can be either the name of file containing a truetype font that is used to render the text, or a string containing font names, the first found of which will be used to render the text. Examples: "Vera.ttf" or "Arial,Helvetica,FreeSans"

size --- The size of the font that is used to display the text on the screen. Please note that the meaning of this can vary from font to font, and bears only a weak relationship with the number of pixels high that the font will be on the screen. Example: 22

color --- The color in which the text will be displayed on the screen, as an RGBA tuple. Example: (255, 255, 255, 255)

antialias --- If True, the text will be antialiased. Otherwise, it will be left jagged. Example: True

drop_shadow --- This is used to control the generation of a drop shadow on text. It's either a 2-element tuple, or None. If it's a tuple, then the 2 elements control the drop shadow offsets in X and Y, respectively. (Both numbers should be positive for best results.) If None, then no drop shadow is created for the text. Example: (2, 3)

drop_shadow_color --- An RGBA tuple that's used to give the color of the drop shadow. Example: (0, 0, 0, 128)

minwidth --- The minimum width in pixels of this text. If the rendered text is smaller than this, it is right-padded with whitespace until it is at least this many pixels long.

line_height_fudge --- On some platforms (Windows), the height of a line of text isn't reported correctly. This can lead to an overly-large spacing between lines. This is a number that is added to the reported line spacing. If negative, the lines will be pushed closer together. This is very platform-dependent, so you may want to condition it on the result of renpy.windows().

text_y_fudge --- This fudges the y position of a block of text by adding whitespace above the first line.

textalign --- This is used to control the horizontal alignment of the lines of text in the area allocated to the Text widget containing that text. It only really has any effect if the text is more than one line long. It's a number between 0 and 1, which gives the fraction of empty space that should be to the left of each line of text. (To center text, it should be 0.5.)

Window Properties

Window properties are used to control the display of windows, and other widgets that involve a rectangular area with a background being displayed on the screen.

background --- A Displayable that is used as the background for the window. This needs to be a Displayable that always draws exactly the size requested of it, which usually means either a Solid or a renpy.Frame. This can also be None, which means that there is no background on this window. (All the other window properties that refer to a background still work. Just think of them as if their background was transparent.) Example: Solid((0, 0, 128, 128))

xmargin --- The amount of transparent space that is left around this window in the x direction, in pixels.

xpadding --- The amount of space left between the edge of the background of the window, and the edge of the content contained within this window, in the x direction, in pixels.

xfill --- If True, the window will expand to fill all available space in the x direction. If False, it will shrink to fit its contents.

xminimum --- The minimum size of this window in the x direction, including margins and padding. If the window would be smaller than this, it is grown to be at least this size.

ymargin --- The amount of transparent space that is left around this window in the y direction, in pixels.

ypadding --- The amount of space left between the edge of the background of the window, and the edge of the content contained within this window, in the y direction, in pixels.

yfill --- If True, the window will expand to fill all available space in the y direction. If False, it will shrink to fit its contents.

yminimum --- The minimum size of this window in the y direction, including margins and padding. If the window would be smaller than this, it is grown to be at least this size.

Position Properties

Position properties are applied to widgets that are smaller than the space allocated to them. They control the placement of the widget in the space. For example, position properties can be used to control the placement of a dialogue window on the screen.

Position properties work best when a small widget is placed into empty space. This is the case for say windows, and the menus that are displayed as part of the main menu. Position properties work on many updates, but the vagaries of how space is allocated by widgets may make some of the results counterintuitive.

Positioning is done by specifying the placement of an anchor that is part of a widget within the space allocated to that widget. The position is either specified as an absolute number of pixels or a fraction of the available space, while the position of the anchor within the widget is done by selecting one of three settings for each dimension.

xpos --- Controls the positioning of the widget anchor in the x dimension. If an integer, this is taken as the number of pixels to the left of the anchor. If a float, this is taken as a fraction of the available space in the x direction.

xanchor --- Controls the placement of the anchor within the widget, in the x dimension. This can be one of 'left', 'center', or 'right'.

ypos --- Controls the positioning of the widget anchor in the y dimension. If an integer, this is taken as the number of pixels to the left of the anchor. If a float, this is taken as a fraction of the available space in the y direction.

yanchor --- Controls the placement of the anchor within the widget, in the y dimension. This can be one of 'top', 'center', or 'bottom'.

Sound Properties

Some widgets can take sound style properties. These widgets can cause a sound to play when they become hovered (see below), and also when they are selected. Currently, the styles that can take sound properties belong to buttons, imagemaps, and menu choices. Sound style properties are either the name of a wav file which is played with renpy.play when appropriate event occurs, or None to disable sound playback.

hover_sound --- The sound to play when this widget becomes hovered.

activate_sound --- The sound to play when the widget is activated, by clicking on or otherwise selecting it.


In Ren'Py, buttons and their labels support the idea of hovering: using different sets of properties when the mouse is over or not over the widget. If the mouse is over the widget, then that widget is hovered, else it is idle. On these widgets, Ren'Py will look up a property prefixed with "hover_" or "idle_" (as appropriate) in a style, before looking up an unprefixed property. If neither form is found on a style, the process is repeated again on the parent style. (The search pattern for background on a hovered button goes: button.hover_background, button.background, default.hover_background, default.background.)

As an example, take assigning a background to buttons that changes when the button is hovered over.

    style.button.hover_background = Solid((255, 255, 255, 255))
    style.button.idle_background = Solid((0, 0, 0, 255))

enable_hover --- This property must be set to True (the default) on the style of a hoverable widget (like a button) for hovering to be enabled. Setting it to False speeds up Ren'Py, at the cost of making the widget not respond to hovering.

Using Properties and Styles

For properties to be useful, there needs to be a way to change the properties associated with a given widget. Ren'Py provides several ways to do this.

The most common way to do this is to change the definition of one of the standard styles. Widgets that use this style, or one of the styles that inherits from it, will pick up the change and change how they are displayed.

The following example code ensures that the label of a button is colored light green and aligned to the left. It also changes the background images that are used when the button is idled and hovered.

    $ style.button.color = (128, 255, 128, 255)
    $ style.button.xpos = 0
    $ style.button_idled.background = \
                      xborder=10, yborder=10)
    $ style.button_hover.background = \
                      xborder=10, yborder=10)

This is the only way to change the look of widgets that are automatically generated, such as those used to implement the interface of the say and menu statements, except for say statements that are routed through an object, like a character object.

The second way to change the look of a widget is to change the name of the style associated with that widgets. All widgets have one or more parameters that take styles that the widget take on. One can define a new style using style.new_style, and modify that style, and tell widgets to use that style when appropriate.

While normally new styles can only be assigned to widgets constructed by the user, there are some exceptions to this rule. The Character object takes as optional arguments styles that are given to the say window, character name, and dialogue text.

The final way of assigning properties is to list them as keyword arguments on a constructor that takes properties. (Indicated by having **properties in its argument list.) Properties that are given in this way take precedence over all other properties. The Character argument also takes properties that are applied to the window and the label.

Standard Styles

In this section find a list of the standard styles that come with Ren'Py, either built-in or in the standard library. Before each in parentheses, we list the types of properties that are used on the style, and "hover" if the default use of the style supports hovering.

The default style that all styles inherit from.
(inherits from default)
(window, placement) The base style for the windows that contain dialogue, thoughts, and menus.
(inherits from default)
This style is used to control the default placement of images on the screen.
(inherits from default)
(text) The style that is used by default for the label of dialogue. The label is used to indicate who is saying something.
(inherits from default)
(text) The style that is used by default for the text of dialogue.
(inherits from default)
(text) The label that is used by default for the text of thoughts or narration, when no speaker is given.
(inherits from window)
(window, position) The default style for windows containing dialogue and thoughts.
(inherits from default)
(text) The style that is used to render a menu caption.
(inherits from default)
(text, hover, sound) The style that is used to render a menu choice.
(inherits from window)
(window, position) The default style for windows containing a menu.
(inherits from default)
(text) The style used for the text of an input box.
(inherits from default)
(text) The style used for the prompt of an input box.
(inherits from window)
(window, position) The style used for the window of an input box.
(inherits from default)
(window) The style that is used for a "window" containing centered text.
(inherits from default)
(text) The style used for centered text.
(inherits from image_placement)
(sound, position) The style that is used for imagemaps.
(inherits from default)
(window, sound, hover) The default style used for image buttons.
(inherits from default)
The default style used for images inside image buttons.
(inherits from default)
(window, sound, hover) The default style used for buttons in the main and game menus.
(inherits from default)
(text, hover) The default style used for the label of a button.
(inherits from button)
(window, hover) The style that is used for a selected button (for example, the active screen or a chosen preference).
(inherits from button_text)
(text, hover) The style that is used for the label of a selected button.
(inherits from button)
(window, hover) The style that is used for a disabled button.
(inherits from button_text)
(text, hover) The style that is used for the label of a disabled button.
(inherits from default)
(window) The style used for the root window of the main menu. This is primarily used to set a background for the main menu.
(inherits from default)
(window, position) A window that contains the choices in the main menu. Change this to change the placement of these choices on the main menu screen.
(inherits from button)
(window, hover) The style that is used on buttons that are part of the main menu.
(inherits from button_text)
(text, hover) The style that is used for the labels of buttons that are part of the main menu.
(inherits from default)
(window) The style used for the root window of the game menu. This is primarily used to change the background of the game menu.
(inherits from default)
(window, position) The style used by a window containing buttons that allow the user to navigate through the different screens of the game menu.
(inherits from button)
(window, hover) The style of an unselected game menu navigation button.
(inherits from button_text)
(text, hover) The style of the text of an unselected game menu navigation button.
(inherits from selected_button)
(window, hover) The style of a selected game menu navigation button.
(inherits from selected_button_text)
(text, hover) The style of the text of a selected game menu navigation button.
(inherits from default)
(window, position) A window containing the file picker that is used to choose slots for loading and saving.
(inherits from default)
(inherits from button)
(window, hover) The style that is used for each of the slots in the file picker.
(inherits from default)
(text) A base style for all text that is displayed in the file picker.
(inherits from file_picker_text)
(text) The style that is applied to the new indicator in the file picker.
(inherits from file_picker_text)
(text) The style that is applied to the old indicator in the file pciker.
(inherits from file_picker_text)
(text) The style that is applied to extra info in the file picker. The extra info is the save time, and the save_name if one exists.
(inherits from file_picker_text)
(text) The style that is used for the new slot indicator in the file picker.
(inherits from default)
(text, position) The style used for the prompt in a yes/no dialog.
(inherits from button)
(position) The position of the yes button on the screen.
(inherits from button)
(position) The position of the no button on the screen.
(inherits from default)
(text, position) The style that is applied to the label of a block of preferences.
(inherits from default)
(position) The position of the box containing an individual preference.
(inherits from default)
(position) The position of the left column of preferences.
(inherits from default)
(position) The position of the right column of preferences.
(inherits from button)
(window, hover) The style of an unselected preferences button.
(inherits from button_text)
(text, hover) The style of the text of an unselected preferences button.
(inherits from selected_button)
(window, hover) The style of a selected preferences button.
(inherits from selected_button_text)
(text, hover) The style of the text of a selected preferences button.

Obfuscating your Game

For some reason, many people seem to want to distribute their games in an obfuscated form, making it difficult to read the script, or to view the images associated with a game. Ren'Py supports this, to a point, by compiling scripts and allowing images to be archived. While a moderately skilled programmer could easily crack this obfuscation, it should be enough to protect a game from casual browsing.

Obfuscating the script is practically automatic. Every time the game runs, any .rpy file in the game directory is written out as an .rpyc file. These .rpyc files are enough to run the game, so simply running the game once (to create the .rpyc files) and then deleting (or more appropriately, moving away) the .rpy files will leave you with a runnable game with an obfuscated script. As a bonus, the .rpyc files are already parsed, improving game load time. (If a directory contains .rpy and .rpyc files with the same stem, the newer one of them is chosen, meaning all games get this performance improvement.)

Images can be archived using the archive.py or archive.exe programs that ship as part of Ren'Py. To use this, one can change into the game directory and run a command like:

../archive.exe images *.png *.jpg

This will create the files images.rpa and images.rpi in the game directory. One can then add to the script a line like:

    $ config.archives = [ 'images' ]

Then, delete the .jpg and .png files from the game directory. Ren'Py will look in the images archive to find images that need to be loaded.

Localizing Ren'Py

While Ren'Py is by default set up to operate in an English speaking environment, it is not limited to such settings. Assuming a proper font is loaded, Ren'Py scripts can contain any language expressible in Unicode.

There are two things in the Ren'Py library that may need to be translated into a user's language. The first is the main menu. There is no explicit support for doing this, but as the library.main_menu variable supports changing the text of the main menu, it also supports translating said text.

The second thing that needs to be translated is the game menu. The library.translations dictionary is used to translate text in the game menu into your language. If a key in this map corresponds to the English text that would be displayed, the value corresponding to that key is displayed again. For example:

    $ library.translations = {
        "Yes" : u"HIja'",
        "No" : u"ghobe'",
        # etc.

The u characters prefixed to the strings on the right, while not strictly necessary in this case, are used to tell Python that the string is in Unicode rather than ASCII. This is useful if your language uses non-ascii characters.

Changing the Keymap

The variable config.keymap contains a map from functions that can be performed by the various user-interface elements of Ren'Py to a list of keysyms that actually perform those functions. Modifying the contents of the keymap can change the keys and mouse buttons that cause things to happen.

While this functionality has been added to Ren'Py at a user's request (and because it simplifies the Ren'Py code), it's not altogether clear that it should be used. Having a common set of keybindings makes games easier to play, by reducing the learning curve of users. It's probably better to build consensus around a change in keybindings, rather than unilaterally making something different from everyone else.

Anyway, in Ren'Py keysyms are strings. The first kind of keysym is of the form 'mouse_#', for a number between 1 and 5. These keysyms are generated by mouse button presses or turns of the mouse wheel. For example, "mouse_1" is generally the left mouse button, while "mouse_4" is a turn of the the mouse wheel to the top.

A second kind of keysym is a string containing a character that is generated when a key is pressed. This is useful for binding alphabetic keys and numbers. Examples of these keysyms include "a", "A", and "7".

The final kind of keysym is the symbolic name for the key. This can be any of the K_ constants taken from pygame.constants This type of keysym looks like "K_BACKSPACE", "K_RETURN", and "K_TAB".

To change a binding, update the appropriate list in config.keymap. The following code adds the 't' key to the list of keys that dismiss a say statement.

    $ config.keymap['dismiss'].append('t')

The default keymap is contained inside the python code implementing Ren'Py, and as of version 4.3 is as follows:

keymap = dict(
    # Bindings present almost everywhere, unless explicitly
    # disabled.
    rollback = [ 'K_PAGEUP', 'mouse_4' ],
    screenshot = [ 's' ],
    toggle_fullscreen = [ 'f' ],
    toggle_music = [ 'm' ],
    game_menu = [ 'K_ESCAPE', 'mouse_3' ],
    hide_windows = [ 'mouse_2' ],

    # Say.
    rollforward = [ 'mouse_5', 'K_PAGEDOWN' ],
    dismiss = [ 'mouse_1', 'K_RETURN', 'K_SPACE', 'K_KP_ENTER' ],
    skip = [ 'K_LCTRL', 'K_RCTRL' ],

    # Keymouse.
    keymouse_left = [ 'K_LEFT' ],
    keymouse_right = [ 'K_RIGHT' ],
    keymouse_up = [ 'K_UP' ],
    keymouse_down = [ 'K_DOWN' ],
    # Menu.
    menu_mouseselect = [ 'mouse_1' ],
    menu_keyselect = ['K_RETURN', 'K_KP_ENTER' ],
    menu_keyup = [ 'K_UP' ],
    menu_keydown = [ 'K_DOWN' ],
    # Button.
    button_select = [ 'mouse_1', 'K_RETURN', 'K_KP_ENTER' ],

    # Input.
    input_backspace = [ 'K_BACKSPACE' ],
    input_enter = [ 'K_RETURN', 'K_KP_ENTER' ],

    # Imagemap.
    imagemap_select = [ 'K_RETURN', 'K_KP_ENTER', 'mouse_1' ],

    # This isn't a binding, but instead a list of keys that should
    # repeat when held down.
    repeating = [ 'K_LCTRL', 'K_RCTRL' ],

UI Functions

While it's hoped that Ren'Py say and menu statements will be appropriate for much of the user interaction that goes on in a visual novel, we recognize that not all games can be fit in such a mold. To allow other, similar, games to be built, we've exposed the functions that we use to build the Ren'Py user interface. These functions are used both for normal gameplay, and to build the main and game menus. While these functions won't be enough for all games (Ren'Py will never be a RPG engine), hopefully these will allow the creation of, for example, schedule-management dating-simulation games.

To start, let's give a script snippet that uses the UI functions. The following displays a window containing three buttons, arraigned horizontally. The string assigned to choice varies depending on which of the three is picked.

$ ui.window()
$ ui.hbox()

$ ui.textbutton("A", clicked=ui.returns("A"))
$ ui.textbutton("B", clicked=ui.returns("B"))
$ ui.textbutton("C", clicked=ui.returns("C"))

$ ui.close()
$ choice = renpy.interact(suppress_overlay=True)

To understand the ui functions, you must first understand the three kinds of widgets that Ren'Py supports. The first kind of widget (of which window is one), takes a single child widget. When this kind of widget is open, the next widget to be created is added to it, and it then closes automatically.

A second kind of widget can take an arbitrary number of child widgets. This kind of widget is exemplified by the layout widgets: vbox, hbox, and fixed. New widgets are added to this kind of widget until a matching call to ui.close() occurs, unless they are instead added to an open child of this widget.

The final kind of widget cannot take any children. This kind of widget is exemplified by textbutton.

Nothing is displayed on the screen until ui.interact() is called. Once it has been called, Ren'Py displays the screen to the user. It then waits for a widget to return a value. In the example given above, when a textbutton is clicked, the function returned by ui.returns is called, which in turn causes ui.interact to return the same value. When ui.interact returns, the screed is cleared of all transient ui elements, and control proceeds with the next statement. The ui functions must be called again if we want to show the same user interface to the user again.

The following are the functions available in the ui module, which is automatically present in the game namespace.

Single-Child Widgets

ui.window (**properties):

A window contains a single widget. It draws that window atop a background and with appropriate amounts of margin and padding, taken from the window properties supplied to this call. The next widget created is added to this window.

ui.button (clicked=None, **properties):

This creates a button that can be clicked by the user. When this button is clicked or otherwise selected, the function supplied as the clicked argument is called. If it returns a value, that value is returned from renpy.interact().

Buttons created with this function contain another widget, specifically the next widget to be added. As a convenience, one can use ui.textbutton to create a button with a text label.

clicked - A function that is called when this button is clicked.

Multiple-Child Widgets

ui.vbox (padding=0, **properties):

This creates a layout that places widgets next to each other, from top to bottom. New widgets are added to this vbox until ui.close() is called.

padding - The number of pixels to leave between widgets.

ui.hbox (padding=0, **properties):

This creates a layout that places widgets next to each other, from left to right. New widgets are added to this hbox until ui.close() is called.

padding - The number of pixels to leave between widgets.

ui.fixed (**properties):

This creates a layout that places widgets at fixed locations relative to the origin of the enclosing widget. New widgets are added to this widget.

No-Child Widgets and Psuedo-Widgets

ui.text (label, **properties):

This creates a widget displaying a text label.

label - The text that will be displayed on the screen.

It uses font properties.

ui.image (filename, **properties):

This loads an image from the given file, and displays it as a widget.

ui.textbutton (text, clicked=None, text_style='button_text', **properties):

This creates a button that is labelled with some text. When the button is clicked or otherwise selected, the function supplied as the clicked argument is called. If it returns a value, that value is returned from renpy.interact().

text - The text of this button.

clicked - A function that is called when this button is clicked.

text_style - The style that is used for button text.

ui.imagebutton (idle_image, hover_image, clicked=None, image_style='image_button_image', **properties):

This creates a button that contains two images. The first is the idle image, which is used when the mouse is not over the image, while the second is the hover image, which is used when the mouse is over the image. If the button is clicked or otherwise selected, then the clicked argument is called. If it returns a value, that value is returned from renpy.interact().

idle_image - The file name of the image used when this button is idle.

hover_image - The file name of the image used when this button is hovered.

clicked - The function that is called when this button is clicked.

image_style - The style that is applied to the images that are used as part of the imagebutton.

ui.imagemap (ground, selected, hotspots, unselected=None, **properties):

This is the widget that implements imagemaps. Parameters are roughtly the same as renpy.imagemap. The value of the hotspot is returned when renpy.interact() returns.

ui.menu (menuitems, **properties):

This creates a new menu widget. Unlike the menu statement or renpy.menu function, this menu widget is not enclosed in any sort of window. You'd have to do that yourself, if it is desired.

menuitems - A list of tuples that are the items to be added to this menu. The first element of a tuple is a string that is used for this menuitem. The second element is the value to be returned from renpy.interact() if this item is selected, or None if this item is a non-selectable caption.

ui.saybehavior ():

This is a psuedo-widget that adds the say behavior to the screen. The say behavior is to return True if the left mouse is clicked or enter is pressed. It also returns true in various other cases if the current statement has already been seen. This widget should not be added to any other widget, but should instead be only added to the screen itself.

ui.keymousebehavior ():

This is a psuedo-widget that adds the keymouse behavior to the screen. The keymouse behavior allows the mouse to be controlled by the keyboard. This widget should not be added to any other widget, but should instead be only added to the screen itself.


ui.interact (**kwargs):

Displays the current scene to the user, waits for a widget to indicate a return value, and returns that value to the user.

Some useful keyword arguments are:

show_mouse - Should the mouse be shown during this interaction? Only advisory, as this doesn't work reliably.

suppress_overlay - This suppresses the display of the overlay during this interaction.

ui.close ():

Closes the current widget. This means that we will no longer add things to the current widget, but will instead start adding things to the parent of that widget, or the screen if the widget has no parent. Calling this when there is no current widget will lead to an exception being thrown.

ui.returns (value):

This function returns a function that, when called, returns the supplied value. It's best used to supply the clicked argument to the various button widgets.

ui.jumps (label):

This function returns a function that, when called, jumps the game to the given label, ending the current interaction in the process. It's best used to supply the clicked argument to the various button widgets. (Most of the time, you want to use ui.returns and a series of if statements to jump or call the appropriate label, rather than relying on this function.)

Function Index

Variable Index

Property Index

Style Index

Example Script

For your browsing pleasure, we present a syntax-highlighted version of the demo that ships with Ren'Py. The start of this script has been commented to make it easier to understand.