Codédex | Make a Baldur's Gate 3 Mod with Lua

Make a Baldur's Gate 3 Mod with Lua

Julien Kris

·

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:

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:

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:

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:

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"

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"

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"

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.

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:

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!