Skip to content

Why LuneBlox

LuneBlox is Lune with the Luau underneath changed, so that code tested or measured here behaves like the code players run.

Lune 0.10.5 runs Luau 0.709. Roblox runs 0.740 (September 2026). On top of the version, Roblox runs Luau with its own set of fast flags: Luau ships new compiler and VM work behind flags that default to off, and Roblox turns them on remotely.

A benchmark or a test run on Lune therefore measures a VM that no game runs.

Lune 0.10.5 LuneBlox 0.11.3
Luau 0.709 0.740, the version Roblox runs
Luau’s fast flags Luau’s defaults set as the live Roblox client sets them
Luau’s C++ built with -Os -O3
_VERSION Lune 0.10.5+709 LuneBlox 0.11.3+740

LuneBlox builds against Luau 0.740. The version is part of _VERSION, after the +, so a script or a bug report can say which Luau it ran on:

--!strict
print(_VERSION) --> LuneBlox 0.11.3+740

Keeping Luau current describes how the version is moved.

At startup LuneBlox sets each Luau fast flag to the value the live Roblox client has for it. The table is generated from Roblox’s published client settings and limited to the flags that this Luau declares.

LUNE_ROBLOX_FFLAGS=0 runs with Luau’s own defaults instead, to measure what the flags change. See Fast flags.

Lune’s release profile optimizes for size (opt-level = "z"). Luau’s C++ is compiled by a build script that takes its optimization level from that profile, so the whole VM was built with -Os.

LuneBlox builds Luau’s C++ at -O3 and keeps the size profile for the Rust code. Measured on an M-series Mac with Luau 0.740 and Roblox’s flags, against the same build at -Os:

Operation Faster by
pcall 34%
A Luau-to-Luau metamethod call 29%
A called closure 24%

The cost is 370 KB of binary size.

Roblox’s utf8 library has graphemes, which open-source Luau does not. LuneBlox adds it. It walks the grapheme clusters of a string - a letter with its combining marks, a flag, an emoji joined from several - and gives the first and last byte of each:

--!strict
local s = "e\u{301}x" -- "e" with a combining acute accent, then "x"
for first, last in (utf8 :: any).graphemes(s) do
print(first, last) --> 1 3, then 4 4
end

i and j narrow it to part of the string, counted as in string.sub. luau-lsp’s standard platform does not declare utf8.graphemes, hence the cast.

Measured on an Apple M1, median of three runs, on one Luau networking library’s generated modules, LuneBlox against Lune 0.10.5:

  • Interpreted code - what most Roblox clients run - is 39 to 57% faster to encode and decode.
  • Modules that Lune’s Luau left interpreted even when natively compiled now compile.

LuneBlox builds Luau with its assertions compiled in. A code generation bug that a release build of Luau miscompiles silently stops this process instead.

That is how the ARM64 fix in 0.10.12 was found. Luau 0.740’s ARM64 register allocator could plan to reload a spilled value from a constant past K255, which no single ARM64 load reaches. With assertions on, loading a function with more than 256 constants as native code stopped the process; without them, the function would load the wrong value.

Fixes like this one are carried in a copy of Luau’s sources in the repository (vendor/luau0-src), each marked with a LuneBlox: comment, until a Luau release has them.

LuneBlox keeps Lune’s whole interface: the same @lune/* libraries, the same task scheduler, the same Roblox datatypes and place and model files. A script written for Lune runs unchanged.

Migrating from Lune lists the few things that differ.