5 Getting Started
bryanthaboi edited this page 2026-10-21 17:34:48 -04:00

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

  1. Save your edited files, then close and reopen the app so it discovers them.
  2. In the launcher, open MODS. Use Show for to select Red if needed.
  3. Find My First Mod, enable it for Red, and launch Red.
  4. 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.
  5. 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.