Learning

Lua + LÖVE: Make a Game · Lesson 01 · 25 min

Launch your first LÖVE game

Install LÖVE, run a game folder, and understand the three callbacks at the heart of a 2D game.

Learn

How Lua and LÖVE divide the work.

Build

A window that draws your first game object.

Prove it

Move that object by changing two numbers.

You are going to build Orbit Runner, a small arcade game about collecting gold signals while dodging meteors. It uses only colored shapes and generated sound, so you can learn the engine without hunting for assets.

By the end of the course, you will be able to:

No programming background is assumed. Type the code rather than pasting it whenever you can—the small mistakes you fix are part of learning the language.

The two-layer mental model

Lua is the programming language. It gives you values, variables, functions, conditions, loops, and tables.

LÖVE (often written Love2D) is the game framework hosting that Lua code. It opens the window, reads the keyboard, draws shapes and images, plays audio, and calls your functions at the right times.

Your game is not a script that runs top to bottom and exits. It stays alive because LÖVE repeatedly calls functions you define:

function love.load()
  -- Run once, at startup.
end

function love.update(dt)
  -- Change the game state every frame.
end

function love.draw()
  -- Draw the current state every frame.
end

These specially named functions are callbacks. You define them; LÖVE decides when to call them.

The most useful habit in this course is to keep state, update, and draw distinct. A position is state. Changing the position belongs in update. Showing it belongs in draw.

Install the stable version

This course targets LÖVE 11.5, the current stable release. LÖVE 12.0 is still in development as this course is written. Download 11.5 from the official LÖVE site, install it, and verify the command if your installer adds it to your path:

love --version

You should see a version beginning with LOVE 11.5. If your terminal cannot find love, the installation may still be fine:

You need a plain-text code editor. Any editor with Lua syntax highlighting is enough.

Make the game folder

Create this exact structure somewhere convenient:

orbit-runner/ └── main.lua

The filename matters. A LÖVE game must have main.lua at the top level of its folder or archive.

Put this in main.lua:

local playerX = 480
local playerY = 270

function love.load()
  love.window.setMode(960, 540)
  love.window.setTitle("Orbit Runner")
  love.graphics.setBackgroundColor(0.04, 0.03, 0.08)
end

function love.draw()
  love.graphics.setColor(0.33, 0.91, 0.84)
  love.graphics.circle("fill", playerX, playerY, 18)
end

Run the folder, not the file:

love orbit-runner

If your terminal is already inside orbit-runner, use:

love .

You should see a dark 960 × 540 window with a cyan circle in the center.

Read the program like the computer

The two local lines create state:

local playerX = 480
local playerY = 270

love.load runs once. It configures the window and background.

love.draw runs many times per second. Each time, it chooses a color and draws a filled circle. In LÖVE 11.x, color channels range from 0 to 1: 0 means none of that channel; 1 means full intensity.

The screen origin (0, 0) is at the top-left. x grows to the right and y grows downward:

(0, 0) ───────────────► +x

  │       (480, 270)


 +y

Function arguments go between parentheses. This call has four:

love.graphics.circle("fill", playerX, playerY, 18)
--                   mode    center x  center y radius

Make it yours

Change one thing at a time, save, then relaunch the game.

  1. Move the circle 100 pixels to the right.
  2. Move it near the upper-left without letting it leave the screen.
  3. Change it to gold: try love.graphics.setColor(1, 0.83, 0.47).
  4. Change "fill" to "line".

Checkpoint

You are done when you can predict where the circle will appear before running the game, and you can explain why love.load and love.draw do different jobs.

When it does not run

Read LÖVE’s error screen from the top. It normally gives a filename, line number, and message. Check these first:

The error screen is not a verdict. It is the engine telling you where its understanding stopped.

Next, you will learn enough Lua to control what happens inside these callbacks.