Getting Started with LOVE 2D

· 9 min read · Updated March 19, 2026 · beginner
love2d gamedev installation beginner first-game

LOVE (also written as LÖVE or LOVE2D) is a free, open-source framework for building 2D games using Lua. It’s a fantastic starting point if you want to create games without dealing with complex engine setups. Think of it like a toy box full of building blocks specifically designed for making games.

What Is LOVE 2D?

LOVE is a game engine. An engine handles the boring but necessary stuff: opening a window, drawing graphics, detecting key presses, playing sounds. Instead of building these from scratch, you tell LOVE what you want and it handles the rest.

Here’s why people like LOVE:

  • Lightweight — The entire engine is just a few megabytes
  • Cross-platform — Your games run on Windows, macOS, and Linux
  • Lua-based — You write games in Lua, which is one of the easiest programming languages to learn
  • Free and open — No cost, no license worries, and you can see how it works under the hood

Installing LOVE 2D

The installation process depends on your operating system.

Windows

  1. Go to love2d.org and download the latest installer
  2. Run the installer (choose 32-bit or 64-bit — if you’re unsure, 64-bit is probably right for modern computers)
  3. Optional: Add LOVE to your system PATH so you can run it from any command prompt

To check if it worked, open Command Prompt and type:

love

If you see a window pop up with the LOVE launcher, you’re all set.

macOS

You have two options:

Option 1: DMG file

  1. Download the .dmg file from love2d.org
  2. Drag the LOVE app into your Applications folder

Option 2: Homebrew If you use Homebrew, run:

brew install love

To verify, open Terminal and type love. You should see the launcher window.

Linux

The exact command depends on your distribution:

Debian or Ubuntu:

sudo apt install love

Fedora:

sudo dnf install love

Arch Linux:

sudo pacman -S love

If you’re on a different distribution, check your package manager for the love package. Some distributions only package older versions, so you might need to download the AppImage from love2d.org instead.

Run love in your terminal to verify the installation.

Your First LOVE 2D Game

Now let’s write some game code.

File Structure

LOVE expects your main game file to be called main.lua. That’s it. No complex folder structure, no configuration files needed. Just one file to start.

Create a new folder for your project, then create a file inside called main.lua:

mygame/
└── main.lua

The Game Loop

Every LOVE game has three main functions that form what we call the “game loop” — the heartbeat of your game that runs continuously while the game is running.

Think of these functions like a play in a theater:

  • love.load() — The preparation before the play starts (runs once)
  • love.update() — What happens on stage during each moment of the play (every frame)
  • love.draw() — Taking a photograph of the stage (every frame)

Here’s what each one does:

-- main.lua

-- love.load runs once when your game starts
-- Use it to set up your initial game state
function love.load()
    -- x and y store the position of our square
    x = 400
    y = 300
end

-- love.update runs every frame (typically 60 times per second)
-- This is where you update game logic like movement
-- dt stands for "delta time" - the seconds since the last frame
function love.update(dt)
    -- Move the square 100 pixels to the right every second
    -- Multiplying by dt ensures consistent speed regardless of frame rate
    x = x + 100 * dt
end

-- love.draw runs every frame after update
-- This is where you put all your rendering code
function love.draw()
    -- Draw a red filled rectangle
    -- Arguments: mode, x, y, width, height
    -- Note: x and y are the TOP-LEFT corner of the rectangle
    -- We're offsetting by 25 to center the 50x50 square on our position
    love.graphics.setColor(1, 0, 0)  -- RGB values from 0 to 1
    love.graphics.rectangle("fill", x - 25, y - 25, 50, 50)
end

Why does dt matter? Imagine two computers: one is slow and runs your game at 30 frames per second, another is fast and runs it at 60 frames per second. Without dt, the fast computer would move objects twice as fast. By multiplying movement by dt, both computers move objects at the same speed — the slow one just takes more frames to get there.

The Coordinate System

Before we go further, it’s important to understand LOVE’s coordinate system. The origin point (0, 0) is at the top-left corner of the window. Positive X extends to the right, and positive Y extends downward. This is different from some other engines where Y goes upward.

When you draw shapes, the x and y coordinates you provide determine where they’re positioned based on this system.

Creating a Window

By default, LOVE creates a window that’s 800 pixels wide and 600 pixels tall. You might want something different, so let’s see how to customize it.

The recommended way is to create a separate file called conf.lua:

-- conf.lua

function love.conf(t)
    -- t is a table that holds all configuration options
    t.window.title = "My First Game"  -- Window title
    t.window.width = 1024               -- Width in pixels
    t.window.height = 768              -- Height in pixels
    t.window.resizable = true          -- Let user resize the window
    t.window.fullscreen = false        -- Start in windowed mode
    t.window.vsync = true              -- Sync with monitor refresh rate
end

You can also set the window size in code using love.window.setMode:

function love.load()
    -- Arguments: width, height, and optional settings table
    love.window.setMode(1024, 768, {
        fullscreen = false,
        resizable = true,
        vsync = true
    })
end

Use conf.lua when you want configuration that’s set before the game starts. Use love.window.setMode() when you need to change settings dynamically during gameplay.

Drawing Shapes

Let’s draw some shapes. LOVE provides several functions in the love.graphics module:

function love.draw()
    -- Draw a filled rectangle
    -- "fill" means solid, "line" would be just the outline
    love.graphics.rectangle("fill", 100, 100, 200, 150)
    
    -- Draw an outlined rectangle
    love.graphics.rectangle("line", 100, 100, 200, 150)
    
    -- Draw a filled circle
    -- Arguments: x, y, radius, segments (higher = smoother circle)
    love.graphics.circle("fill", 400, 300, 50, 32)
    
    -- Draw an outlined circle
    love.graphics.circle("line", 400, 300, 50, 32)
    
    -- Draw a line between two points
    love.graphics.line(0, 0, 800, 600)
    
    -- Draw a triangle (polygon with 3 sides)
    -- Pass a table of x,y coordinates for each vertex
    love.graphics.polygon("fill", {
        500, 200,   -- vertex 1 (x, y)
        600, 350,   -- vertex 2 (x, y)
        500, 350    -- vertex 3 (x, y)
    })
    
    -- Set color before drawing (RGB + optional Alpha)
    -- All values range from 0 to 1
    love.graphics.setColor(0.2, 0.5, 0.8, 1)  -- light blue
end

The color function uses values from 0 to 1 instead of the more common 0-255. Think of it like this: 0 means “none of this color” and 1 means “maximum of this color”. So (1, 0, 0) is pure red, (0, 1, 0) is pure green, (0, 0, 1) is pure blue, and (1, 1, 1) is white.

Handling Keyboard Input

Now let’s make your game interactive. LOVE provides several ways to detect key presses.

The simplest is love.keyboard.isDown(), which checks if a key is currently held down:

function love.update(dt)
    local speed = 200 * dt
    
    -- Check if arrow keys are held down
    if love.keyboard.isDown("left") then
        x = x - speed
    end
    if love.keyboard.isDown("right") then
        x = x + speed
    end
    if love.keyboard.isDown("up") then
        y = y - speed
    end
    if love.keyboard.isDown("down") then
        y = y + speed
    end
end

For detecting individual key presses (like firing once when a key is pressed), use the love.keypressed callback:

function love.keypressed(key)
    if key == "escape" then
        -- Quit the game when escape is pressed
        love.event.quit()
    end
    if key == "space" then
        -- Do something when space is pressed
        print("Space pressed!")
    end
end

Loading Images

Games need graphics beyond just shapes. Here’s how you load and display images:

function love.load()
    -- Load an image from a file
    -- Make sure you have a "player.png" in your game folder
    playerImage = love.graphics.newImage("player.png")
    
    x = 400
    y = 300
end

function love.draw()
    -- Draw the image at position x, y
    -- The third argument is rotation in radians
    love.graphics.draw(playerImage, x, y, 0)
    
    -- You can also specify the origin point (center of the image)
    -- love.graphics.draw(playerImage, x, y, 0, 1, 1, playerImage:getWidth() / 2, playerImage:getHeight() / 2)
end

Place your image files in the same folder as your main.lua. LOVE will automatically find them.

Running Your Game

Run your game:

love /path/to/your/game/folder

For example, if your game is in a folder called “mygame” on your desktop on Windows:

love C:\Users\YourName\Desktop\mygame

You can also run LOVE on a .zip file containing your game files — LOVE will automatically find and run the main.lua inside.

Common Setup Problems

Let’s cover some issues that often trip up beginners.

Wrong Filename

Problem: Nothing happens when you run LOVE
Solution: Check that your entry point is exactly main.lua (lowercase, with the .lua extension). LOVE won’t find files named Main.lua, main.LUA, or anything else.

Working Directory Issues

Problem: You get errors about missing files, or assets don’t load
Solution: Always run LOVE from your game folder, not from somewhere else. The simplest way is to navigate to your game folder first, then run love . (the dot means “current folder”).

Frame-Rate Dependent Movement

Problem: Your game runs faster on faster computers
Solution: Always multiply movement by dt in your love.update function:

-- This runs at different speeds on different computers
x = x + 5

-- This runs at the same speed everywhere
x = x + 200 * dt

Drawing Outside love.draw

Problem: Graphics don’t appear, or they flicker wildly
Solution: LOVE only draws what you put inside the love.draw() function. If you try to draw elsewhere, it either won’t show up or will behave unpredictably. Keep all rendering code in love.draw().

Version Information

The current stable version of LOVE is 11.x (nicknamed “Mysterious Mysteries”). It supports Lua versions 5.1, 5.2, 5.3, and 5.4, depending on which version of LOVE you use.

You can check what version you’re running in code:

function love.load()
    local v = love.getVersion()
    print(string.format("LOVE version: %d.%d.%d", v.major, v.minor, v.revision))
    -- Prints something like "LOVE version: 11.5.0"
end

Where to Go Next

Now that you’ve got LOVE running, here are some great next steps:

See Also