LabHub

Blog

Modern Lua & Luau in 2026 — Lua 5.4 / LuaJIT / Roblox Luau / Neovim Lua / OpenResty / Defold / Love2D Deep Dive

한국어English日本語

Prologue — "Lua is dead." Who told you that?

In May 2026, if someone tells you "Lua is finished," that person has spent the last five years ignoring the game industry, the editor ecosystem, and the high-performance web server space.

The numbers.

All of this runs on top of a one-line language. The reference interpreter is under 25,000 lines of ANSI C 89 and embeds almost anywhere.

This article is a one-shot tour of the Lua and Luau ecosystem in 2026: Lua 5.4 features, LuaJIT performance, Roblox Luau type system, LazyVim config explosion, OpenResty NGINX embedding, Defold and Love2D indie engines, and desktop embeddings like Hammerspoon and Wireshark.


1. The 2026 Lua ecosystem map — four major axes

One picture first.

                          [ Lua core ]
                                 |
        +------------------------+------------------------+
        |                |                |               |
   Lua 5.4         LuaJIT 2.1       Luau (Roblox)    Variants
   (PUC-Rio)      (Mike Pall)       (typed fork)     (4 + extras)
   integer type    Lua 5.1 ABI       gradual types    MoonScript
   to-be-closed    interpreter       native codegen   Yuescript
   bitwise         FFI               parallel Luau    Teal (typed)
   generational    fastest dynamic                    Pluto
                                                      Fennel (Lisp)
        |                |                |               |
        +-------+--------+--------+-------+-------+-------+
                |                 |               |
        [ Neovim 0.12 ]   [ OpenResty ]    [ Games/Engines ]
         LazyVim           NGINX + Lua      Roblox
         AstroNvim         Kong API GW      WoW addons
         NvChad            Apache APISIX    Defold (King)
         LunarVim          Tarantool DB     Love2D
                                            Solar2D
                                            Garry's Mod
                                            Tabletop Sim
                                            Source 2 (CS2)
                                            |
                                       [ Desktop embedded ]
                                        Hammerspoon (Mac)
                                        Adobe Lightroom plug-ins
                                        Wireshark dissectors

Key points.


2. Lua 5.4 — the latest stable, deliberately conservative

PUC-Rio Lua 5.4 first shipped in June 2020 and is patched up to 5.4.7 by 2024. As of 2026, no 5.5 major release exists. In every interview, Roberto Ierusalimschy repeats: "stability comes first for a language."

What 5.4 brought.

Integer vs float — separate types

Introduced in 5.3, refined in 5.4. Previously, every number was a double.

-- Lua 5.4
print(math.type(1))     -- integer
print(math.type(1.0))   -- float
print(math.type(1/2))   -- float (division is always float)
print(math.type(1//2))  -- integer (floor division)

-- Bitwise ops work on integers only
print(0xFF & 0x0F)      -- 15
print(1 << 4)           -- 16

LuaJIT is still based on the Lua 5.1 ABI and does not support this integer type as standard. This is the biggest fork point between the two camps.

to-be-closed variables — Lua's RAII

Similar to C++ RAII or Python's with for resource management.

-- Lua 5.4
do
  local file <close> = io.open("data.txt", "r")
  -- When the block exits, __close is automatically called.
  -- Safe even on exceptions — no try/finally needed.
  for line in file:lines() do
    print(line)
  end
end
-- file is already closed here

Variables tagged <close> invoke __close on the metatable when leaving scope. Extremely useful for catching resource leak patterns.

Generational GC

Lua 5.4 adds a generational mode to the GC as a first-class option, toggleable with incremental.

-- Switch to generational GC
collectgarbage("generational")

-- Workloads with many short-lived objects (parsing, render loops)
-- can be 30–60 percent faster.

New syntax — goto labels, const, close

local PI <const> = 3.14159  -- Assigning to PI again raises a compile error

for i = 1, 10 do
  for j = 1, 10 do
    if i * j > 50 then goto done end
  end
end
::done::
print("escaped")

<const> is a small but valuable safety win: catches unintended reassignments at compile time.


3. LuaJIT — Mike Pall, performance king, no successor in sight

You cannot talk about Lua without LuaJIT. Mike Pall almost single-handedly built and maintained this project, and in 2026 it is still one of the gold standards for dynamic-language runtimes.

Why it is fast

-- LuaJIT FFI example
local ffi = require("ffi")

ffi.cdef[[
typedef struct { double x, y, z; } Vec3;
double sqrt(double);
]]

local v = ffi.new("Vec3", 1, 2, 3)
local len = ffi.C.sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
print(len)  -- 3.7416...

This code runs at near C speed. That is why game engines and middleware love LuaJIT.

The truth about no 5.3-plus support

LuaJIT is pinned to the Lua 5.1 ABI. Mike Pall paused work on LuaJIT 2.1 beta around 2020, and 2.1 still carries the "beta" tag while being used in production worldwide. Integer types, to-be-closed, and 64-bit bitwise ops are partially supported or absent.

So in 2026 the Lua ecosystem is split into two camps.

LuaJIT's future


4. Luau — Roblox's typed Lua fork

Luau (pronounced "loo-OW") is a Lua 5.1-based language Roblox forked around 2019. Open-sourced in 2021, it is the fastest-evolving Lua variant in 2026.

What is different

-- Luau type system
local function add(a: number, b: number): number
  return a + b
end

type Vec3 = { x: number, y: number, z: number }

local function length(v: Vec3): number
  return math.sqrt(v.x^2 + v.y^2 + v.z^2)
end

-- Pick strict / nonstrict / nocheck per file
--!strict

Can you use Luau outside Roblox

Since late 2025, Luau has become serious about its standalone runtime. The luau command-line tool is available on npm and Homebrew, and projects like Lune (a Luau runtime) make it possible to write Discord bots or CLI tools in Luau.

# Install standalone Luau on macOS
brew install luau

# Run
echo 'print(string.format("hello %s", "luau"))' | luau -

2026 roadmap

Roblox publishes Luau RFCs (formal proposals) every quarter. Highlights for Q1 2026:


5. Roblox development — the massive Luau market

Half the reason you cannot ignore Lua and Luau is Roblox.

How to get started

1. Download Roblox Studio (Windows / Mac)
2. New Place → Baseplate template
3. In Explorer, right-click ServerScriptService → Insert Object → Script
4. Write Luau

-- Example: a part that changes color when touched
local part = workspace.Part
part.Touched:Connect(function(hit)
  local character = hit.Parent
  if character:FindFirstChild("Humanoid") then
    part.Color = Color3.fromRGB(
      math.random(0, 255),
      math.random(0, 255),
      math.random(0, 255)
    )
  end
end)

Roblox developer economics

Korean and Japanese Roblox development


6. The Neovim Lua config explosion — LazyVim, AstroNvim, NvChad, LunarVim

The biggest change from Vim to Neovim is the migration from VimScript to Lua. The Lua API stabilized in 0.5 (2021), and through 0.10, 0.11, 0.12, VimScript has effectively entered legacy mode.

In 2026 the Neovim config ecosystem is dominated by four distributions that take more than 90 percent of share.

LazyVim — Folke's standout distro

Built on top of folke/lazy.nvim, the lazy-loading plugin manager. Since its 2024 debut, it has become the de facto default.

-- ~/.config/nvim/lua/plugins/example.lua
return {
  {
    "nvim-treesitter/nvim-treesitter",
    build = ":TSUpdate",
    config = function()
      require("nvim-treesitter.configs").setup({
        ensure_installed = { "lua", "python", "typescript", "rust" },
        highlight = { enable = true },
      })
    end,
  },
  {
    "neovim/nvim-lspconfig",
    dependencies = { "williamboman/mason.nvim" },
    config = function()
      require("lspconfig").lua_ls.setup({})
    end,
  },
}

Pros

Cons

AstroNvim — polished UI

Dashboard, status line, and icons come pre-styled. Aims for an "IDE-like Neovim you can use immediately."

-- AstroNvim v4 structure
return {
  colorscheme = "catppuccin",
  features = {
    large_buf = { size = 1024 * 256, lines = 10000 },
    autopairs = true,
    cmp = true,
    diagnostics_mode = 3,
  },
  diagnostics = { virtual_text = true, underline = true },
}

Who likes it — developers new to Vim, coming from IDEs.

NvChad — lightweight, clean UI

A distro by Indian developer siduck. Targets under-30ms startup. Also popular in Korea and Japan.

-- NvChad v2.5+
require("nvchad.configs.lspconfig").defaults()
local servers = { "html", "cssls", "tsserver", "lua_ls", "rust_analyzer" }
vim.lsp.enable(servers)

LunarVim — comprehensive IDE focus

A classic "make Vim a real IDE" distro. Peak popularity in 2022–2023, still has a stable user base.

Which distro should you pick

SituationRecommendation
First-time Neovim userLazyVim or AstroNvim
Fast boot, lightweight UINvChad
Steady learning curve, full IDE feelLunarVim
No distro, from scratchkickstart.nvim then DIY

The from-scratch revival — through 2025–2026, modular plugins like nvim-lspconfig, blink.cmp, and snacks.nvim have matured to the point where "writing your own without a distro is cleaner" is a real trend again. kickstart.nvim is the standard starting point.


7. OpenResty (NGINX + Lua) — the hidden engine of high-performance web

A platform that embeds LuaJIT into NGINX's core so you can write request handlers in Lua. Created in 2011 by Yichun Zhang (agentzh) and now maintained by OpenResty Inc.

Why it is fast

# nginx.conf
location /api/users {
    content_by_lua_block {
        local user_id = ngx.var.arg_id
        local redis = require "resty.redis"
        local red = redis:new()
        red:connect("127.0.0.1", 6379)
        local user_json = red:get("user:" .. user_id)
        ngx.header.content_type = "application/json"
        ngx.say(user_json)
    }
}

Who uses it (as of 2026)

Module ecosystem

Install through opm (OpenResty Package Manager) or LuaRocks.

# Common libraries
opm install openresty/lua-resty-redis
opm install openresty/lua-resty-mysql
opm install openresty/lua-resty-http
opm install bungle/lua-resty-prometheus

OpenResty in Korea


8. Tarantool — in-memory DB plus Lua application server

A project that started at Mail.Ru in Russia and is now an independent project, Tarantool is the unusual combination of in-memory DB plus a Lua app server.

-- Run inside Tarantool
box.cfg{ listen = 3301 }
box.schema.space.create('users', { if_not_exists = true })
box.space.users:format({
  { name = 'id', type = 'unsigned' },
  { name = 'name', type = 'string' },
  { name = 'email', type = 'string' },
})
box.space.users:create_index('primary', { parts = { 'id' } })

-- Business logic lives in the same process
function add_user(id, name, email)
  return box.space.users:insert{ id, name, email }
end

function find_user(id)
  return box.space.users:get(id)
end

Where it is used: large Russian internet companies, Eastern European game studios, and increasingly fintech.


9. Games — from WoW addons to Source 2

World of Warcraft addons

Since launch in 2004, WoW has shipped a Lua-based addon system. Twenty-two years later, unchanged.

-- The simplest WoW addon (MyAddon.toc + MyAddon.lua)
local frame = CreateFrame("Frame")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:SetScript("OnEvent", function(self, event, ...)
  print("|cFFFF0000Hello|r from MyAddon!")
end)

Tabletop Simulator

Released in 2014, still going strong. All mods (game rules) are Lua. Tens of thousands of mods on Steam Workshop.

Garry's Mod and Source 2 (CS2)

Every Garry's Mod mod is Lua. The Source 2 engine (CS2 since 2023 and beyond) supports partial Lua scripting — map makers and server plugins are written in Lua.

Defold — King's Lua engine

A 2D and 3D engine acquired and open-sourced by King (Candy Crush). Logic is Lua, the engine itself is C-plus-plus.

-- script.script
function init(self)
  self.speed = 100
end

function update(self, dt)
  local pos = go.get_position()
  pos.x = pos.x + self.speed * dt
  go.set_position(pos)
end

function on_input(self, action_id, action)
  if action_id == hash("touch") and action.pressed then
    self.speed = self.speed * -1
  end
end

Solar2D (formerly Corona SDK)

After Corona Labs disappeared, the community took it over and rebranded as Solar2D. Mobile 2D game engine. Lua is the main language. Still used in Korea and Japan by training schools and indie developers.

Love2D — the standard for tiny 2D games

A 100 KB-class SDK. With just three callbacks — love.load, love.update, love.draw — a game runs.

function love.load()
  player = { x = 100, y = 100, speed = 200 }
end

function love.update(dt)
  if love.keyboard.isDown("right") then
    player.x = player.x + player.speed * dt
  end
  if love.keyboard.isDown("left") then
    player.x = player.x - player.speed * dt
  end
end

function love.draw()
  love.graphics.rectangle("fill", player.x, player.y, 50, 50)
end

10. Variants — MoonScript, Yuescript, Teal, Pluto, Fennel

Lua is small and simple, but some users felt "this is just a bit short of what I want" and built variant languages.

MoonScript — Lua's CoffeeScript

Indentation-based syntax, classes, list comprehensions.

-- MoonScript
class Animal
  new: (@name) =>
  speak: => print "#{@name} makes a sound"

class Dog extends Animal
  speak: => print "#{@name} barks"

d = Dog "Rex"
d\speak!

Popular in the early-to-mid 2010s, now in maintenance mode. Still, parts of the OpenResty world remain written in MoonScript.

Yuescript — actively developed MoonScript fork

A MoonScript fork by Taiwanese developer Li Jin. Lua 5.4-compatible, actively developed.

Teal — typed Lua, the most serious contender

-- Teal (.tl file)
local record Point
  x: number
  y: number
end

local function distance(a: Point, b: Point): number
  return math.sqrt((a.x - b.x)^2 + (a.y - b.y)^2)
end

Pluto — Lua 5.4 plus modern features

A superset that adds continue, switch, the ?? null-coalescing operator, classes, async or await, and more on top of Lua 5.4.

-- Pluto
class Player
  function __construct(self, name)
    self.name = name
    self.hp = 100
  end
  
  function takeDamage(self, amount)
    self.hp -= amount  -- compound assignment
    if self.hp <= 0 then
      print(self.name .. " died")
    end
  end
end

local p = new Player("Alice")
p:takeDamage(50)

local config = json.decode(http.get("https://example.com/cfg")) ?? {}

Actively developed and popular in game-modding communities (Stand and others).

Fennel — Lisp on the Lua VM

Clojure-style S-expression syntax, runs on the Lua VM.

;; Fennel
(fn greet [name]
  (print (.. "Hello, " name "!")))

(greet "World")

(local people [{:name "Alice" :age 30}
               {:name "Bob" :age 25}])

(each [_ person (ipairs people)]
  (print (.. person.name " is " person.age " years old")))

11. Hammerspoon (Mac) and Wireshark Lua dissectors

Hammerspoon — macOS automation

-- ~/.hammerspoon/init.lua
-- Cmd-Alt-R snaps the window to the right half
hs.hotkey.bind({"cmd", "alt"}, "R", function()
  local win = hs.window.focusedWindow()
  local f = win:frame()
  local screen = win:screen():frame()
  f.x = screen.x + screen.w / 2
  f.y = screen.y
  f.w = screen.w / 2
  f.h = screen.h
  win:setFrame(f)
end)

-- Clipboard history
local clipboardHistory = {}
hs.pasteboard.watcher.new(function()
  local item = hs.pasteboard.readString()
  if item then table.insert(clipboardHistory, 1, item) end
end):start()

Wireshark Lua dissectors

In Wireshark, when you analyze network packets you can define a custom protocol in Lua.

-- Wireshark plugins/my_proto.lua
local my_proto = Proto("MyProto", "My Custom Protocol")

local fields = my_proto.fields
fields.id = ProtoField.uint16("myproto.id", "ID", base.DEC)
fields.payload = ProtoField.bytes("myproto.payload", "Payload")

function my_proto.dissector(buffer, pinfo, tree)
  pinfo.cols.protocol = my_proto.name
  local subtree = tree:add(my_proto, buffer())
  subtree:add(fields.id, buffer(0, 2))
  subtree:add(fields.payload, buffer(2))
end

DissectorTable.get("tcp.port"):add(8080, my_proto)

Huge help for game companies and IoT companies debugging in-house protocols. What would take a C dissector now takes 100 lines of Lua.


12. Adobe Lightroom Lua plug-ins

A barely known fact: Adobe Lightroom's plug-in system is Lua-based. Since 2007, unchanged.

-- Lightroom SDK plug-in example (Info.lua + Main.lua)
return {
  LrSdkVersion = 12.0,
  LrToolkitIdentifier = "com.example.exporter",
  LrPluginName = "My Exporter",
  LrExportServiceProvider = {
    title = "My Cloud",
    file = "ExportServiceProvider.lua",
  },
}

13. Lua in Korea and Japan

Korea

Japan


14. Who should learn Lua and Luau

Roblox game developers

Luau is mandatory, obviously. Outside Roblox, the standalone Luau runtime (Lune) is growing, so the reach is widening.

Neovim users

Even just to configure it, you need to read Lua at minimum. The migration from VimScript to Lua is essentially complete. init.lua, lazy.nvim configs, plugin authoring — all Lua.

Embedded developers

A small-memory-footprint interpreter equals Lua. NodeMCU, which embeds Lua on ESP32 for IoT, is still alive, and Lua is the standard for embedding in game engines and middleware.

NGINX and web infrastructure engineers

If you work with OpenResty, Kong, or APISIX, Lua is essentially required. You may also encounter it in Cloudflare Workers or Envoy (Lua extension).

Game modders

If you want to create mods, plug-ins, or extensions for WoW, Tabletop Simulator, Garry's Mod, or CS2 (map makers), Lua is your entry point.

macOS power users

If you want to automate with Hammerspoon, you need about 100 lines of Lua. The way out of AppleScript pain.

Photographers and Lightroom users

If you want a custom exporter or file-naming automation tailored to your workflow, Lua plus the Lightroom SDK is the most efficient path.


15. Learning path — from zero to modern Lua

Week 0 — one hour of core

Programming in Lua (Roberto Ierusalimschy), 4th edition, chapters 1–5. Cover basic syntax, tables, functions, and metatables.

Week 1 — pick a real environment

Week 2 — metatables and OOP patterns

OOP is not built into Lua. You have to write the class emulation with setmetatable at least once.

local Animal = {}
Animal.__index = Animal

function Animal.new(name)
  local self = setmetatable({}, Animal)
  self.name = name
  return self
end

function Animal:speak()
  print(self.name .. " makes a sound")
end

local Dog = setmetatable({}, { __index = Animal })
Dog.__index = Dog

function Dog.new(name)
  local self = Animal.new(name)
  return setmetatable(self, Dog)
end

function Dog:speak()
  print(self.name .. " barks")
end

local d = Dog.new("Rex")
d:speak()  -- Rex barks

Week 3 — coroutines, LPeg, request handling

Week 4 and beyond — go deep in one area


Epilogue — the long shadow of a small language

Lua has been consistent since its 1993 birth. Simple syntax, eight data types, a small interpreter, powerful metatables. Roberto Ierusalimschy keeps saying "Lua is a language for being embedded," and 32 years later in 2026 that statement is still exactly correct.

The Lua and Luau ecosystem in 2026 is more diverse than ever. On Roblox, 400 million people play games made in Luau every day. Hundreds of thousands of Neovim users see Lua daily through LazyVim. At Cloudflare's edge, LuaJIT handles a slice of Earth's traffic. WoW addons have been alive for 22 years. A Lightroom photographer automates a workflow in a Lua plug-in.

Lua never dies. It is just quietly everywhere.

The next fashion might be Rust, or Zig, or something else. But when a game engine needs to pick a scripting language, when an editor needs to pick a configuration language, when a web server needs to pick an embedded-handler language, Lua has been on the shortlist for 32 years. And will be for a while longer.


References

Comments

No comments yet.

Sign in to leave a comment