Getting started with making mods
For: new mod authors. Result: a mod folder the game can load. Start with a working imported game from Start Playing; the examples here use Red. If you only want to install somebody else's mod, use that page's install steps.
You can make the first mod with the packaged app and a plain-text editor. The command-line tools are an optional second route for people who have the engine source checkout.
Choose your setup
| Your setup | Follow | Where you create files |
|---|---|---|
| Packaged desktop app | Manual setup below | The mods folder in the app's save directory |
| Source checkout | Manual setup or scaffold | <engine folder>/mods/ |
A source checkout is a copy of the game's source code, containing
main.lua, src/, and tools/. The wiki folder is a separate repository;
it cannot run tools/modkit.py. See Modding Basics.
Run the game first
Open the app, choose Import ROM for Red, and select your supported ROM. Reach the title screen once before writing a mod. This creates the imported data that examples use. Source users should complete Developer Setup first.
Where mods live
For desktop manual setup, use the mods folder in the LÖVE save directory.
LÖVE is the runtime that runs the game. Create mods if it does not exist.
These are the default locations; ~ means your home folder:
| System | Default mod location |
|---|---|
| macOS, source/LÖVE run | ~/Library/Application Support/LOVE/pokemon-love2d/mods/ |
| macOS, packaged app | ~/Library/Application Support/pokemon-love2d/mods/ |
| Linux, source/LÖVE run | ~/.local/share/love/pokemon-love2d/mods/ |
| Linux, packaged AppImage | ~/.local/share/pokemon-love2d/mods/ |
| Linux, Flatpak | ~/.var/app/com.theboisclub.gen1recomp/data/love/pokemon-love2d/mods/ |
| Windows, source/LÖVE run | %APPDATA%\love\pokemon-love2d\mods\ |
Windows, packaged .exe |
%APPDATA%\pokemon-love2d\mods\ |
One mods folder serves every game. There is no per-generation copy; the
manifest's games field decides where a mod loads.
On macOS, use Finder > Go > Go to Folder and paste the relevant path. On Windows, paste the path into File Explorer's address bar. On Linux, use your file manager's location bar. If the game uses a custom identity or portable mode, its location can differ. The desktop launcher's game Manage panel has Open folder for the LÖVE save directory; portable game files may be beside the executable instead. See Save Editor.
A source checkout can also use <engine folder>/mods/. Pick one location
for each mod and avoid a second copy with the same ID. The loader combines
these locations into one view; duplicate copies make edits hard to track.
The minimal mod
Inside mods, create a folder named my_first_mod. Use a plain-text editor
and create these two files, with exactly these extensions:
mods/
└── my_first_mod/
├── manifest.json
└── main.lua
Complete file: manifest.json
{
"id": "my_first_mod",
"name": "My First Mod",
"version": "1.0.0",
"entry": "main.lua",
"api": 2,
"games": ["red"]
}
The manifest tells the game the mod's internal name (id), displayed name
(name), revision (version), code file (entry), API rules (api), and
intended game (games). Keep the punctuation and double quotes. API 2 checks
known field shapes and catches close misspellings; it does not turn every
unknown custom field into an error. More fields are listed in the
Manifest reference.
Complete file: main.lua
return function(mod)
mod.content.text:override("_PalletTownSignText", "HELLO FROM MY MOD!")
end
mod is the set of tools the game gives your mod. text selects the game's
text collection. override replaces the named text. The first quoted string
is the sign's internal ID; the second is what the player will read. Keep the
code between the first and last lines. Modding Basics
explains this structure in more detail.
Save both files. Check that they are not named manifest.json.txt or
main.lua.txt. Follow Your First Change
for the in-game check.
Scaffold in a source checkout
Scaffold means create starting files automatically. This route requires
Python 3 and LuaJIT as described in Developer Setup.
Open a terminal in the engine folder containing tools/, then run:
python3 tools/modkit.py scaffold my_first_mod
Expected result: created .../mods/my_first_mod (content profile, api 2).
It also writes README.md, .luarc.json and an assets/ folder. games
defaults to ["gen1"]; pass --games with gen1, gen2, gen3, frlg,
rse, all or a comma list of version ids (red,gold,firered) to change it.
The manifest is written as api 2 unless you pass --api 3 for a
portable mod. The generated main.lua contains a sample
Mew stat patch; replace that whole file with the sign example above if
you want the first exercise. Set games to ["red"] in the manifest for
that exercise. If the folder already exists, use the manual route; you do
not need to overwrite it with --force.
Then validate the mod from the same terminal:
python3 tools/modkit.py validate my_first_mod --base imported
A successful command exits with code 0. Read its summary: imported data is
needed to check real game IDs. --base fixture is useful without a ROM but
skips some real-data checks. Validation is separate from opening the game
and checking the sign; it does not exercise every callback or render asset.
For a mod outside the checkout, pass its quoted path instead of
my_first_mod. scaffold --dest DIR creates DIR/my_first_mod/, where
DIR is a parent folder you choose. See Modkit.
Enabling, disabling, and restarting
- Save your edited files, then close and reopen the app so it discovers them.
- In the launcher, open MODS. Use Show for to select Red if needed.
- Find My First Mod, enable it for Red, and launch Red.
- For the in-game manager, press F10 on Red, Blue or Yellow, or pick MODS from the START menu on the Gen 2 and Gen 3 games. Select a mod to see its options and errors. Follow APPLY & RESTART if changes are staged.
- After changing content files, restart the game before checking the result.
Enablement is saved per game. Most mods start enabled, but experimental mods and unsupported targets have different rules; look at the actual row. A new game does not reset these settings. Profiles save named sets of mod choices, which helps keep tutorial experiments separate.
Content changes are applied during startup. Authors can use F5 in developer
mode (Gen 1 only) for a faster iteration loop, but restarting is the beginner default.
Lifecycle explains the timing; Developer Setup
explains POKEPORT_DEV=1.
Other games and API 3
The same folder and files work for every game the launcher imports: Red,
Blue, Yellow, Gold, Silver, Crystal, FireRed, LeafGreen, Ruby, Sapphire and
Emerald. What decides where a mod loads is games:
games value |
Loads on |
|---|---|
| omitted | Red, Blue, Yellow only |
["gen1"], ["gen2"], ["gen3"] |
that generation |
["frlg"], ["rse"] |
FireRed/LeafGreen, or Ruby/Sapphire/Emerald |
["red", "gold"] |
exactly those versions |
["all"] |
every game |
A Gen 1 mod is not loaded on Gold or FireRed at all; the manager lists it as not for this game. Text IDs, map IDs and many records differ per generation, so the Pallet Town sign above is a Red/Blue/Yellow example.
"api": 3 is an opt-in for one entry file across generations: pokemon,
moves, items, trainers and encounters use the Gen 1 record shape on
every game, and mod:supports tells you what a game can do. api 1 and 2
mods keep working unchanged. See
Portable Mods across Gen 1, 2 and 3 and
Portable API 3. Check a mod
against another generation with python3 tools/modkit.py gen2check my_mod
or gen3check my_mod (Modkit).
When something breaks
If the mod is missing, check its folder and file extensions. If it is listed but failed, open its error details. If it loaded but the sign is unchanged, check the target game, enabled state, saved files, and restart. Use Troubleshooting for exact feedback locations.
The rule that has no exceptions
Keep original game data out of shared mods. Use your own art/audio or
asset transforms that run against the player's imported
cache. Do not copy files from assets/generated/ into a distributable mod.
Publishing explains packaging checks.
Where to next
| Goal | Page |
|---|---|
| See your first change and understand every step | Your First Change |
| Learn what the syntax means | Modding Basics |
| Replace a sprite and a dialogue line | Tutorial 01 |
| Understand when code runs | Lifecycle |
| Find a small task | Cookbook |
| Find a record or field | Choose a Registry |
| Run one mod on Gen 1, 2 and 3 | Portable Mods |
Technical sources: src/mods/Loader.lua, src/mods/LauncherMods.lua,
src/mods/ManagerState.lua, src/mods/ModTargets.lua, tools/modkit.py,
conf.lua.
Start here
Playing
Tutorials
- Choose a tutorial
- 01 Sprite and Text Tweak
- 02 Balance Patch
- 03 New Species
- 04 New Item and Ball
- 05 New Move
- 06 NPC and Dialogue
- 07 New Map
- 08 Quest
- 09 Custom Music
- 10 New Mechanic
- 11 Custom UI Screen
- 12 Mini Total Conversion
Creative guides
Concepts
Recipes and reference
Author and developer tools
Home · Start Playing · Glossary · Troubleshooting · Repository · Community
Game data is imported locally from a supported ROM supplied by the player. Shared mods contain original work or transform recipes, never extracted game content. Check each guide's target game before using its examples.