Lua
Tiny, fast and made to be embedded.
Lua is a small, fast scripting language designed to be embedded in other programs. It runs game logic in Roblox (as Luau), World of Warcraft add-ons and Neovim configuration, and the original Torch deep-learning framework, which inspired PyTorch, used Lua.
Why learn Lua?
Lua has only a handful of concepts, so you can learn it in an afternoon, and it is the scripting language inside many games and tools.
- Simple: one data structure (the table) and very few keywords.
- Games: Roblox, LÖVE, Defold and many engines use Lua.
- Embeddable: add scripting to C, C++ or Rust programs easily.
Set up and run your first program
Install Lua 5.4 (or LuaJIT) from your package manager, and LuaRocks for packages.
lua -v
lua hello.lualocal name = "Ada"
print("Hello, AI! Welcome to Lua, " .. name .. ".").. to join strings. Comments start with --.Variables and types
Variables are global unless declared local, so always use local. Lua has nil, booleans, numbers, strings, functions and tables.
local layers = 32
local lr = 3e-4
local model = "llama3.1"
local params = 8030000000
print(model, layers, lr)
print(string.format("FP16 size: %.1f GB", params * 2 / 1e9))
print(type(model), type(nil))Control flow
Blocks end with end. Numeric for loops include both ends. Only nil and false count as false; 0 is true.
for epoch = 1, 3 do
print("epoch " .. epoch)
end
local bits = 8
if bits == 16 then
print("FP16")
elseif bits == 8 then
print("FP8")
else
print("other")
end
local n = 3
while n > 0 do n = n - 1 endTables
The table is Lua's only data structure: it acts as an array (1-indexed), a dictionary, an object and a module. Loop with ipairs for arrays and pairs for keys.
local gpus = {"H100", "H200", "B200"} -- array
local vram = {H100 = 80, H200 = 141, B200 = 180} -- dictionary
for i, g in ipairs(gpus) do print(i, g) end
for name, gb in pairs(vram) do
if gb > 100 then print(name .. " has " .. gb .. " GB") end
end
print(#gpus) -- length: 3Functions and closures
Functions are values: store them in variables and tables, pass them around, and return several values at once.
local function params(layers, width)
width = width or 4096
return 12 * layers * width * width
end
local function stats(t)
local sum = 0
for _, v in ipairs(t) do sum = sum + v end
return sum, sum / #t -- two return values
end
print(params(32))
print(stats({80, 141, 180}))Objects with metatables
Lua builds objects from tables and metatables. The colon syntax obj:method() passes the object as self.
local Gpu = {}
Gpu.__index = Gpu
function Gpu.new(name, vram) return setmetatable({name = name, vram = vram}, Gpu) end
function Gpu:fits(gb) return gb <= self.vram end
function Gpu:describe() return self.name .. " (" .. self.vram .. " GB)" end
local g = Gpu.new("RTX 5090", 32)
print(g:describe(), g:fits(16))AI with Lua
Lua's AI role today is in games and embedded tools: scripting NPC behaviour, calling LLMs from game servers or Neovim plugins, and running small models through bindings. Torch7, written in Lua, was one of the first popular deep learning frameworks before PyTorch.
The essential libraries
Gradient descent from scratch
Learn y = 3x + 0.5 with tables and loops.
local n, xs, ys = 100, {}, {}
for i = 1, n do
xs[i] = -1 + 2 * (i - 1) / (n - 1)
ys[i] = 3 * xs[i] + 0.5 + (math.random() - 0.5) * 0.2
end
local w, b, lr = 0, 0, 0.1
for step = 1, 500 do
local gw, gb = 0, 0
for i = 1, n do
local err = w * xs[i] + b - ys[i]
gw = gw + 2 * err * xs[i] / n
gb = gb + 2 * err / n
end
w, b = w - lr * gw, b - lr * gb
end
print(string.format("w = %.2f, b = %.2f", w, b))Build an AI agent in Lua
With LuaSocket for HTTP and lua-cjson for JSON (install both with LuaRocks), Lua can run the tool-calling loop against a local model.
-- luarocks install luasocket lua-cjson
local http = require("socket.http")
local ltn12 = require("ltn12")
local json = require("cjson")
local function chat(body)
local payload, out = json.encode(body), {}
http.request{ url = "http://localhost:11434/v1/chat/completions", method = "POST",
headers = {["Content-Type"] = "application/json", ["Content-Length"] = #payload},
source = ltn12.source.string(payload), sink = ltn12.sink.table(out) }
return json.decode(table.concat(out))
end
local tools = {{type = "function", ["function"] = {name = "get_weather", description = "Weather for a city",
parameters = {type = "object", properties = {city = {type = "string"}}, required = {"city"}}}}}
local messages = {{role = "user", content = "Should I take an umbrella in Lahore today?"}}
for round = 1, 5 do
local msg = chat({model = "llama3.1", messages = messages, tools = tools}).choices[1].message
table.insert(messages, msg)
local calls = msg.tool_calls
if calls == nil or calls == json.null or #calls == 0 then print(msg.content) break end
for _, call in ipairs(calls) do
local args = json.decode(call["function"].arguments)
table.insert(messages, {role = "tool", tool_call_id = call.id, content = "It is 24°C and sunny in " .. args.city .. "."})
end
endfunction is a Lua keyword, so the field must be written as call["function"].
ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.Lua learning roadmap
Tick items off as you go. Your progress is saved in this browser.
Beginner
Intermediate
Advanced
Projects to build
Lua cheat sheet
| Task | Syntax |
|---|---|
| Local variable | local x = 1 |
| Join strings | "a" .. "b" |
| Table array | local t = {1, 2, 3} |
| Loop array | for i, v in ipairs(t) do end |
| Function | local function f(x) return x end |
| Method call | obj:method() |
Common mistakes
- Forgetting
localcreates global variables by accident. - Arrays start at 1, and
#tis unreliable if the table has holes (nil values). - Use
~=for not-equal, not!=.
Where to go next
- Programming in Lua — official book
- Lua manual — reference
- LÖVE — game framework
On TensorCore.si: C volume, Python volume.
Keyboard: [ previous language · ] next language · / search the library