Mobile devices are limited in CPU/GPU and memory. Therefore, the script logic on Lua should be extremely light: minimum allocations, less work in each frame, reasonable work with rows and tables, careful attitude to the garbage collector (GC). Below is the "skeleton" of optimizations to start with.

1. We profile, we don't guess 🧭
Measure where frame time is lost: use your engine profiler (Defold, Solar2D/Corona, etc.) and internal timers. Determine hot functions and optimize them first.
local clock = os.clock
local function bench(fn, iters)
local t0 = clock()
for i=1,iters do fn() end
return (clock()-t0)*1000
end
print("ms:", bench(updateEnemies, 1000))2. Working with memory and GC 🧹
Reuse tables instead of frequently creating new ones.
Avoid allocations in
update()and collisions.Collect entity pooling for projectiles/particles.
-- Пул на таблицах
local pool = {}
local function acquire()
return table.remove(pool) or {x=0,y=0,vx=0,vy=0,alive=false}
end
local function release(obj)
obj.alive=false; obj.x=0; obj.y=0; obj.vx=0; obj.vy=0
pool[#pool+1]=obj
end3. Quick variables and modules ⚙️
locals are faster than globals: cache frequently used functions.
Save links to module functions in local variables.
-- Было (медленнее из-за глобальных поисков):
function tick(dt) math.sin(dt); math.cos(dt) end
-- Стало (быстрее благодаря локальным ссылкам):
local sin, cos = math.sin, math.cos
function tick(dt) sin(dt); cos(dt) end4. Strings and concatenation 🧵
Strings are immutable: each concatenation creates a new string. Accumulate parts in the table and use table.concat.
-- Плохо:
local s = ""
for i=1,1000 do s = s .. i end
-- Хорошо:
local t = {}
for i=1,1000 do t[i]=i end
local s = table.concat(t, ",")5. Cycles and iterators 🔁
Prefer numeric
forwhere possible.ipairsis convenient, but numericfor i=1,#tis often faster.pairsleave for associative tables and not in hot spots.
-- Быстро:
for i=1,# arr do local v = arr[i]; -- end processing6. Metatables and OOP 💼
Methods through metatables are convenient, but extra calls and __index in hot paths cost FPS. In critical places, refer to the fields directly or cache the methods to local ones.

TOP mistakes of beginner Lua developers
Error ❌ | Why is it bad 🐌 | How to fix ✅ |
|---|---|---|
Creating new tables for each frame | GC splices, micro-lags | Object pool, cleaning and reuse |
Concatenation of strings in loops | Many allocations |
|
Global instead of local | Access is slower | Cache in |
| Unpredictable order/costs | Numeric |
Heavy logic in | FPS drops | Take out the calculations, break down the tasks, cache |
Loading assets "on the fly" | Friezes at I/O | Preloading, atlases, audio streaming |
Before/after example
-- До: много мусора и глобальных обращений
function spawnBullets(n)
for i=1,n do
local b = {x=0,y=0,vx=0,vy=0,alive=true}
table.insert(bullets, b)
end
end
-- После: пулы + локальные ссылки
local insert = table.insert
function spawnBullets(n)
for i=1,n do
local b = acquire() -- из пула
b.alive=true; b.x=0; b.y=0; b.vx=0; b.vy=0
insert(bullets, b)
end
endOptimizing Lua on mobile is a discipline: we profile, eliminate unnecessary allocations, cache functions in local, carefully work with strings and loops, and use pools and atlases. Start with measurements — and purposefully "remove" bottlenecks.
Code helps to improve optimization skills: mini-lessons, practical tasks, checks and tips directly in the application.
And we also have an active Telegram channel, where we discuss cool ideas, share experiences and analyze tasks together — learning becomes not only useful, but also fun.
