Using Renpy's Persistent Storage for Cross‑Session Game Data
Learn how Renpy’s built‑in persistent object automatically pickles game data, handles version‑tolerant loads, and where to call save_persistent() for reliable cross‑session storage.
21 Jun 2026, 19:05 UTC

Problem: Keeping Data Between Playthroughs
When a visual novel or game built with Renpy closes, any Python variables that live only in memory are lost. Developers often need to retain information such as the player’s highest score, unlocked achievements, or persistent settings across sessions. Manually writing files, handling serialization, and worrying about version changes adds boilerplate and risk of corrupt saves.
How Renpy Persistent Storage Works
Renpy provides a built‑in renpy.persistent object that automatically pickles Python data to disk. Changes made during a playthrough are not written immediately; they are flushed to disk only when the game calls renpy.save_persistent() or exits cleanly. This session‑aware behavior reduces I/O overhead and avoids partial writes if the game crashes.
The persistent file lives in the platform‑specific save directory (e.g., %APPDATA%/Renpy//persistent on Windows). Because it is a pickle, the file is binary and not intended for direct editing.
Defining a Custom Persistent Class
For structured data you can subclass renpy.persistent.Persistent and register it with renpy.register_persistent. The class can contain any pickle‑compatible attributes (ints, strings, lists, dicts, etc.). When a game update adds or removes fields, Renpy’s loader ignores missing fields and assigns default values to new ones, giving forward‑compatible saves.
# Example: a custom persistent class for unlockable content class Unlockables(renpy.persistent.Persistent): def __init__(self): self.characters_unlocked = [] # list of character IDs self.secret_scene_seen = False renpy.register_persistent('unlockables', Unlockables)Worked Example: Tracking a High Score
- Declare the variable (if it does not yet exist). Place this in an
init pythonblock so it runs once at startup.init python: if not hasattr(renpy.persistent, 'high_score'): renpy.persistent.high_score = 0- Update the value during gameplay. For instance, after a level ends you might compare the current score.
label after_level: $ current_score = renpy.store.score # assume score is stored elsewhere if current_score > renpy.persistent.high_score: $ renpy.persistent.high_score = current_score "You set a new high score!" jump main_menu- Flush changes to disk. Call
renpy.save_persistent()at a safe point, such as when returning to the main menu or before exiting.label main_menu: "Main Menu" menu: "Start Game": jump start "Quit": $ renpy.save_persistent() return- Verify persistence. Close the game, relaunch it, and check the value. You can display it on the title screen:
If the number matches the last saved high score, the persistent system worked.label title_screen: "High Score: [renpy.persistent.high_score]" pauseTrade‑offs and Limitations
- Binary pickle format. The file is not human‑readable and can be tampered with. Storing sensitive data (passwords, tokens) without additional encryption is unsafe.
- Python version dependence. Pickles created with a different major Python version may raise
UnpicklingErrorwhen loaded. When upgrading the Renpy SDK, test existing saves on the target Python version. - Implicit save timing. Because writes happen only on explicit
save_persistent()or clean exit, a crash before the call loses unsaved changes. Developers should call the save function frequently enough for their use case.
Actionable Closing
To start using Renpy’s persistent storage:
- Add any needed persistent variables in an
init pythonblock, initializing them with sensible defaults. - Modify those variables as your game progresses.
- Call
renpy.save_persistent()at logical breakpoints (menu return, chapter end, or before quit). - After building, test the flow: play, change a value, quit, relaunch, and confirm the value persists.
- If you must store secret data, encrypt it before assigning to the persistent attribute and decrypt after loading.
By following these steps you gain automatic, version‑tolerant saving without managing files yourself, letting you focus on game design rather than I/O plumbing.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.