Packaging and Distributing Your Game
You’ve built your game. It runs fine when you double-click main.lua in your project folder. Now you want to share it with the world. This tutorial walks you through every step: creating a .love package, building platform-specific executables, handling save files correctly, and getting your game onto itch.io.
Creating a .love File
A .love file is LÖVE2D’s native package format. Under the hood, it’s just a ZIP archive with a different extension. Every file your game needs—Lua source, images, audio, fonts—gets bundled into this single archive.
The one rule that trips up most people: main.lua must sit at the root of the ZIP, not inside a subfolder. LÖVE looks for main.lua at the top level and refuses to run if it finds it nested.
Here’s how to create the archive from the command line:
zip -r game.love . -x "*.love"
The -x "*.love" prevents you from accidentally bundling any existing .love files in your project directory. If you’re on Windows and don’t have zip installed, 7-Zip, WinRAR, and PowerShell’s Compress-Archive all work fine. Just make sure main.lua ends up at the root level.
Test the package by running:
love game.love
If your game starts normally, the .love file is valid.
Configuring conf.lua
Before LÖVE initializes the game window and subsystems, it runs conf.lua if one exists in your project. This is where you set the window title, icon, dimensions, and—critically—the identity string that determines the save directory name.
A typical conf.lua for a released game looks like this:
function love.conf(t)
t.window.title = "My Awesome Game"
t.window.icon = "assets/icon.png"
t.window.width = 800
t.window.height = 600
t.window.vsync = 1
-- Unique identifier for the save directory
t.identity = "my_awesome_game"
-- Append version to identity to avoid save conflicts between releases
t.appendidentity = true
end
The t.identity value becomes the folder name inside the OS’s game save directory. Without it, LÖVE uses the filename of the .love file or executable. If two games share a name, their save files bleed into each other—bad news.
Not all platforms respect the t.window.icon setting. macOS in particular often ignores it. For reliable icon display, set the icon programmatically in love.load() instead:
function love.load()
local icon_path = "assets/icon.png"
local success, icon_data = pcall(love.image.newImageData, icon_path)
if success then
love.window.setIcon(icon_data)
end
end
The pcall wrapper is good practice here. If the icon file is missing in a packaged build, newImageData throws an error, and pcall lets you handle that gracefully instead of crashing.
Keep your icon small. 32×32 or 64×64 pixels in PNG format is the safest bet across all platforms.
Building a Windows Executable
The most common approach on Windows is fusing the .love file directly into the LÖVE2D executable. The result is a single .exe file players can double-click to launch.
You’ll need the official LÖVE2D Windows executable from https://love2d.org. Name it love.exe in the same folder as your .love file, then run:
copy /b love.exe + game.love game.exe
This concatenates the two files. The LÖVE executable detects the appended .love archive at startup and runs it as if you’d called love game.love.
You can automate this with a small batch script called build.bat:
@echo off
copy /b love.exe + %1 game.exe
echo Built: game.exe
Run it with:
build.bat game.love
The output is game.exe. Players need to extract the whole folder—not just the .exe—because some platforms look for DLLs and other resources alongside the executable. A cleaner approach is to distribute a ZIP containing the game executable and any required assets.
For more complex builds, tools like makelove or love-build automate AppImage creation and icon embedding automatically.
Building for macOS
macOS applications are distributed as .app bundles, which are folders with a specific internal structure. The LÖVE macOS download is already an .app bundle. To bundle your game into it, you place your .love file inside the app’s Resources folder.
The structure you need:
MyGame.app/
└── Contents/
├── MacOS/
│ └── love (the LÖVE binary from the official .app)
└── Resources/
└── game.love (your game)
To attach your .love file to a downloaded LÖVE .app:
cp game.love /path/to/LÖVE.app/Contents/Resources/game.love
mv LÖVE.app MyGame.app
zip -r MyGame.zip MyGame.app
The resulting MyGame.zip is what you distribute. Players unzip it, move MyGame.app to their Applications folder, and run it.
On macOS, icons must be in ICNS format, not PNG. If you need to set a custom icon, use the iconutil command on a macOS machine to convert a folder of PNG files into an .icns bundle.
To run a .love file directly without bundling (useful during development):
/Applications/love.app/Contents/MacOS/love /path/to/game.love
Building for Linux
Linux offers the most straightforward distribution path for .love files, since most major distributions include LÖVE2D in their package managers. You can distribute the .love file and let users install LÖVE themselves:
zip -r game.love .
# Users run: sudo apt install love (Debian/Ubuntu)
# Then: love game.love
For a more polished distribution, build an AppImage. An AppImage is a single executable file that bundles the LÖVE runtime and your game, running on most Linux distributions without installation.
Tools like makelove and love-build automate AppImage creation. From a Linux machine, you can even cross-compile for Windows using Wine in combination with these tools.
Whatever format you choose, remember to chmod +x the final executable before distributing.
Handling Save Files
Save file paths behave differently depending on whether the game runs from a .love file, a fused executable, or an installed application. The key function to know is love.filesystem.getSaveDirectory(), which returns the platform-specific path where your game should store persistent data.
A pattern that works in every distribution scenario:
function love.load()
-- Set the identity to match conf.lua
love.filesystem.setIdentity("my_awesome_game")
-- Load existing highscore, default to 0
local save_file = "highscore.txt"
if love.filesystem.exists(save_file) then
local data = love.filesystem.read(save_file)
highscore = tonumber(data) or 0
else
highscore = 0
end
end
function saveHighscore()
local save_file = "highscore.txt"
local file = love.filesystem.newFile(save_file)
file:open("w")
file:write(tostring(highscore))
file:close()
end
love.filesystem.setIdentity() mirrors the t.identity value from conf.lua. Call it in love.load() to guarantee the correct save directory is active, even when running from different distribution formats.
For detecting the platform at runtime:
local os_name = love.system.getOS()
-- Returns: "Windows", "OS X", or "Linux"
This is useful for platform-specific logic, such as adjusting default save directory paths or handling different input devices.
Common Pitfalls
Here are the issues that come up most often when packaging LÖVE2D games:
main.lua not at the root. If your ZIP structure puts main.lua inside a subfolder, LÖVE refuses to run. Always verify the archive contents before distributing.
Icon size and format. Large PNG icons cause problems on some platforms. Stick to 32×32 or 64×64. For macOS distribution, convert to ICNS format.
Case-sensitive file paths. Linux and macOS use case-sensitive filesystems. assets/Player.png and assets/player.png are two different files. Always match the casing in your code exactly to the filenames on disk.
Save directory conflicts. If you change the game title between releases, the identity string might collide with an older game’s saves. Use t.appendidentity = true in conf.lua to append version information to the save directory name.
Missing assets in fused builds. When you fuse a game on Windows, love.filesystem.getSourceBaseDirectory() points to the executable’s folder. Any assets referenced by absolute path may not resolve correctly. Always use paths relative to the .love file root, or ensure assets are bundled inside the archive.
Distributing on itch.io
itch.io has built-in support for LÖVE2D games, which makes distribution remarkably simple. Create your .love file, upload it to your itch.io project page, and set the “Upload type” to LÖVE. The platform handles the rest—no build scripts or special tooling required.
For projects where you need a proper executable instead of a .love file, upload the Windows .exe, macOS .app, or Linux AppImage instead.
See Also
- Playing Music and Sound Effects — add audio to your packaged game
- Collision Detection Techniques — handle collisions in your game
- Working with Tilemaps and STI — organize game levels
Summary
Getting a LÖVE2D game into players’ hands involves three main steps: packaging the game as a .love file, building a platform-specific executable using fusing or bundling tools, and verifying that save paths work correctly across all formats.
For most projects, the minimal path is: create the .love → fuse it into a Windows .exe → upload to itch.io. As your game grows and your audience expands to macOS and Linux players, add .app bundling and AppImage builds to your workflow.
With those tools in place, you’re ready to ship.