Codédex | Make a Baldur's Gate 3 Mod with Lua
Make a Baldur's Gate 3 Mod with Lua
·
45 min read
·
Sep 2, 2025
53
2
Prerequisites
Lua basics
Versions
Windows 11, Baldur's Gate 3 Patch 8, BG3 Modder's Multitool v0.13.4, BG3 Script Extender 20240430
Introduction
Baldur’s Gate 3 (BG3) is a 2023 RPG by Larian Studios, adapted from Dungeons & Dragons. It's earned over 10 million players due to its expansive worldbuilding, fleshed out characters, and stunning visuals.
Anyone who's played knows it features some truly epic battles, with animal allies who fight alongside you...
Like our best boy, Scratch!
The game is incredibly well designed, but have you ever wanted to make Baldur’s Gate 3 a little sillier?
Well, remember the Addled Frog you met in Auntie Ethel’s swamp? It’s normally not a recruitable companion, but what if you could summon it to help you win your battles? 🐸
We're going to do just that by making our own mod!
Mods (short for modifications) are a way of writing code to alter features, graphics, or gameplay of an existing game, even for huge titles like Baldur's Gate 3.
We’ll create a mod that lets your character cast a spell to spawn their frog friend anywhere. That frog friend is a playable character who can help out during battles!
Since BG3 Lua modding is only supported on Windows, this project tutorial is aimed at Windows users. If you're a Mac user, scroll down to our Resources section for some links!
By the end of this tutorial, you’ll know how to:
- Write a BG3 mod that lets you summon a custom companion with Lua
- Customize the companion’s stats
- Test your mod in-game
Note: Special thanks to Jon Hinkerton for creating the original mod that inspired this tutorial.
Setting Up
Make sure you have Baldur's Gate 3 installed on your machine! If not, start installing it in the background while you create your mod.
If you don't already have a code editor installed, download Visual Studio Code. Otherwise, you can use another code editor of choice.
BG3 Script Extender
BG3 Script Extender (BG3SE) is a .dll (Dynamic Link Library) file created by a developer named @Norbyte that adds Lua scripting and console support to Baldur's Gate 3. Download it from GitHub.
Right click on the downloaded .zip file, select [Extract All], and extract the files to a new folder in your Documents directory, and name it something like ModdingTools.
Next to your ModdingTools folder, create a new folder called Mods.
We'll be keeping:
- Our modding apps in the ModdingTools folder.
- Our working files in the Mods folder.
Here's the structure you'll be working with inside the Mods folder (you'll learn how to create the meta.lsx files and both .lua files in a bit!)
Starter files
Here’s a .zip folder containing the starter files for this project!
It contains the basic file structure you’ll be working with:
Frog
├── Localization/
│ └── English/
│ ├── Frog.xml
│
├── Mods/
│ └── Frog/
│ ├── meta.lsx
│ └── ScriptExtender/
│ ├── Config.json
│ └── Lua/
│ └── BootstrapServer.lua/
│ └── Server/
│ └── Frog.lua
└── Public/
└── Frog/
├── RootTemplates/
│ └── _merged.lsf.lsx
└── Stats/
└── Generated/
├── Data/
│ ├── Character.txt
│ ├── Object.txt
│ └── Spell_Target.txt
└── TreasureTable.txt
Unzip the folder and look around! You'll notice some of the files are empty, while others have been pre-filled with data our mod will pull from (like metadata and localization files).
Set up the .lsx file
An .lsx (XML) file tells Baldur’s Gate 3 about your mod, including its name, author, folder location, version, and unique ID, so the game can recognize and load it correctly.
The meta.lsx file tells the game info about your mod, including what the mod is called, who made it, where its files live, and how to tell it apart from other mods. We’ve provided you with a boilerplate meta.lsx file inside the project folder.
It contains the following components:
UUID: A unique ID number for your mod.Folder: The name of the folder that holds your mod’s files (inside the Mods/ directory).Name: The display name of the mod. This is what shows up in BG3’s Mod Manager or in-game.Author: The creator’s name (you!).Description: A short blurb about what your mod does.Version: A number you can bump up when you release updates so the game (and users) know it’s a new version.
Without this file, the game doesn’t know your mod exists.
A UUID (Universally Unique Identifier) is a 128-bit value used to uniquely identify information across systems or databases without significant risk of duplication.
It looks something like: 30b78323-c06f-4a66-9767-6241f5ee4656.
It'll make sure your mod is unique and doesn't interfere with another mod installed on the same system. We’ve left it blank, so generate your own by using this online UUID generator, and paste it in here:
<attribute id="UUID" type="FixedString" value="your UUID goes here" />
Setting up .txt files
In Baldur’s Gate 3, most of the game’s data, like characters, spells, and items, is stored inside .pak files. These are packed files that the game reads directly, so you can’t edit them in a normal text editor.
To make modding easier, the BG3 modding community uses tools to export, convert, and edit these files in a readable .txt format before putting them back into the game.
Inside the Data folder, you should see three text files:
- Character.txt is empty right now but it will define the stats for your frog, including Strength, Dexterity, Constitution, and other abilities. It will also include special boosts, resistances, and spells that the frog can use.
- Object.txt contains information about the object that interacts with the frog, like a teapot or magical item that summons it. This includes the item’s name, rarity, and any effects it grants (for example, unlocking the summon spell when equipped or used).
- Spell_Target.txt defines the spells themselves. It includes the summon spell you can cast to bring the frog into the game, as well as the frog’s own spells or attacks it can use in battle to help you out!
We’ve pre-populated Object.txt and Spell_Target.txt with example entries so you can see how the structure works (and because it’s a ton of metadata to sort through!)
We left Character.txt blank, so open it up, and let’s fill it out together!
We’ll write the header first.
new entry "Frog_Summon"
type "Character"
using "_Critter"
new entryis the internal name of your creature.type "Character"is always "Character" for creatures, NPCs, or summons.using "_Critter"pulls in the default critter template so you don’t have to define every single property from scratch.
Next let’s set up our frog’s stats!
data "Strength" "2"
data "Dexterity" "15"
data "Constitution" "8"
data "Intelligence" "2"
data "Wisdom" "8"
data "Charisma" "4"
These are all standard DnD stats. Feel free to change these numbers around!
data "Vitality" "2"
data "Weight" "1"
data "StepsType" "Clawed"
Vitalityis the base number of hit points (hp).Weightaffects physics and interactions.StepsTypedetermines footstep sound/animation (Clawed, Hoofed, etc.).
Next, let’s set up how the frog acts in combat and what abilities it has
data "SpellCastingAbility" "Dexterity"
data "ActionResources" "ActionPoint:1;BonusActionPoint:1;Movement:9;ReactionActionPoint:1"
data "Passives" "AttackOfOpportunity;ShortResting"
data "DefaultBoosts" "Skill(Stealth,6);IncreaseMaxHP(Owner.Level*2);UnlockSpell(Target_Claws_Bufo);ProficiencyBonus(SavingThrow,Dexterity);ProficiencyBonusOverride(Owner.LevelMapValue(StandardProficiencyBonusScale));RollBonus(Attack,max(Owner.DexterityModifier,Owner.SpellCastingAbilityModifier));IF(ConditionResult(GetSummoner(context.Source).Intelligence > 17) or ConditionResult(GetSummoner(context.Source).Wisdom > 17) or ConditionResult(GetSummoner(context.Source).Charisma > 17)):SpellSaveDC(2);UnlockSpell(Target_HypnoticPattern_Bufo);ScaleMultiplier(2);WeightCategory(+4);ObjectSize(+4);Ability(Strength,+8);Ability(Constitution,+8);Ability(Dexterity,-8);AC(-4)"
data "AcidResistance" "Resistant"
data "DifficultyStatuses" "STATUS_EASY:PLAYER_BONUSES_EASYMODE"
data "UnarmedRangedAttackAbility" "Dexterity"
data "SpellCastingAbility"determines which stat the creature uses for spell attacks and spell save DCs. In this example, the frog’s spells are powered by Dexterity, so attack rolls and DCs use the Dexterity modifier.data "ActionResources"defines what the creature can do on its turn.data "Passives"lists abilities that are always active.data "DefaultBoosts"are automatic bonuses or effects applied to the creature.data "AcidResistance"sets resistances. Options include None, Resistant, or Immune. In this case, the frog resists acid damage, since it can make acid attacks.data "DifficultyStatuses"adjusts stats or abilities based on game difficulty.data "UnarmedRangedAttackAbility"determines which stat the creature uses for unarmed ranged attacks, in this case it’ll use Dexterity.
Phew, our frog friend sure is powerful!
Write your Lua Script
The core logic: Summon our Frog friend when you cast the spell, and make sure only one Frog can be spawned at a time.
As you saw in your file folder structure, you'll create two Lua scripts:
BootstrapServer.lua
The first script is BootstrapServer.lua. Write the following code inside:
Ext.Require("Server/Frog.lua")
print("BootstrapServer.lua loaded")
Here, we are telling Script Extender to load our frog.lua file which contains the game logic, and once it's finished loading, we tell the game to run the code inside (in this case, a print message that says the mod has loaded!).
Frog.lua
The second script is Frog.lua.
By default, the Target_Summon_Frog spell just spawns a new frog every time it’s cast. If you cast it twice, you’d have two frogs. If you cast it ten times, you’d have a swarm of frogs!
This script fixes that by ensuring that any existing frog is removed before a new one is created. That way, you’ll always have exactly one frog active.
Write the following code:
local function KillFrog()
local summons = Osi.DB_PlayerSummons:Get(nil)
for _, summon in pairs(summons) do
if string.match(summon[1], "Frog") ~= nil then
Die(summon[1])
end
end
end
Osi.DB_PlayerSummons:Get(nil) queries the game for all currently active summons belonging to the player. The script checks them one by one.
string.match(summon[1], "Frog") says that if the summon’s identifier (its internal name) contains "Frog", we’ve found a frog.
Die(summon[1]) kills that frog immediately. Don’t fret though! We aren’t literally killing the frog, we’re just making sure we don’t spawn a million frogs and accidentally crash the game.
Below that, write:
Ext.Osiris.RegisterListener("UsingSpell", 5, "before", function(caster, spell, targettingType, school, StoryActionID)
if spell == "Target_Summon_Frog" then
KillFrog()
end
end)
Ext.Osiris.RegisterListener("UsingSpell", 5, "before", ...) registers a listener for the UsingSpell event in the game’s scripting system.
UsingSpell fires whenever someone casts a spell, and it contains 5 parameters. before means the function runs before the spell’s normal effects are applied.
Inside the callback function, we check if the spell being cast is Target_Summon_Frog. If yes, the program runs KillFrog() to clean up any old frogs.
Export your Mod to a .pak
Before your mod shows up in Baldur’s Gate 3, you’ll usually need to package it into a .pak file. This isn’t done in BG3 itself, you’ll use external tools made by the modding community.
A .pak file is a type of “package” file, primarily used in video games, that bundles multiple game data files like graphics, textures, sounds, and other assets into a single file for easier management and distribution.
BG3 Modders Multitool
Download BG3 Modder’s Multitool, which is a beginner-friendly open source tool that lets you unpack BG3’s files, browse models, and export your own mod into .pak format.
Extract the contents of the .zip folder into the ModdingTools folder so you can run the tool separately from your mod files.
- Open BG3 Modder’s Multitool (installed outside the game).
- Select your project folder ( Mods).
- Click Build Mod, which generates a .pak file.
Set Your Mod to Active
Download BG3 Mod Manager, and extract the contents of the .zip folder to the ModdingTools folder you put the BG3 Modder’s Multitool in.
Open BG3 Mod Manager. It should display the .pak file you generated before in the Inactive Mods section. Drag your .pak mod from Inactive Mods → Active Mods.
Click [Save Load Order] and [Export].
Load and Test Your Mod
Now you're ready to test your mod! Launch Baldur's Gate 3 and start a new game.
In the bottom left corner of the main menu of the game, you should see a message that says something like Script Extender v18 loaded, built on [date] [time].
The Script Extender console should also load automatically, but if you don’t see it, make sure you’re running your game in Windowed mode, and press the ~ key to check for errors.
You’ll be able to find the teapot containing the frog in the Tutorial Chest at the very beginning of the game, on the Tutorial Level (the Nautiloid ship). Walk past Shadowheart and open the chest in the next room.
Bonus Challenges
Try changing the frog’s stats! For example, you can scale the frog into a GIANT frog by altering the number inside ScaleMultiplier() in the Character.txt file.
Fun fact: that's Jackie’s BG3 character on the right.
You can also try summoning other creatures! The best way to understand Mod files is to download existing ones and play around.
Download one of these mods that use similar logic and let you summon a Ghost Cat, a Tressym, or even a Pet Rock.
Conclusion
Congrats! You just made your first Baldur’s Gate 3 mod! 🎉
We created a frog companion that can fight by your side in battle!
May you win every battle, and have fun breaking Faerûn!
More Resources
Here are some more resources to explore:
- BG3 Modding Wiki
- Norbyte's BG3 Search Engine
- Manual Modding in BG3 for MacOS Users
- Jon Hinkerton’s other mods
Scratch told us he wants you to share your projects with the team @codedex_io and @baldursgate3! Let us know what you come up with!