Documentation

Modes, options, macros and performance advice for getting the most out of LuaFlux.

Getting started

LuaFlux turns a readable script into one that only the interpreter can follow. It supports standard Lua and Luau, including Roblox scripts. Most builds finish in a few seconds.

  1. Paste your script into the editor on the home page.
  2. Pick a mode. If you are unsure, start with Ares.
  3. Press Obfuscate, then copy or download the result.
  4. Test the output in the same environment you plan to ship to.

Keep your original source. Obfuscated output cannot be turned back into your code.

Modes

The mode is the main choice you make for every build. Stronger modes produce heavier output, lighter modes run faster and stay smaller.

ModeBest forOutput
onyxRoblox scripts that must not be tampered with.Heaviest
atlasCode that depends on its environment, such as anti cheat and server validation.Heaviest
aresEveryday protected scripts. A solid default.Heavy
heavyLarger scripts that still need strong protection.Medium
normalLarger scripts where speed starts to matter more.Light
weakBig scripts with a tight performance budget.Lighter
rename, minimifyA light pass over names and formatting when you only need a smaller file.Lightest

Options

Extra settings you can pass along with a build. Defaults suit most scripts.

OptionEffect
--seed NMakes a build reproducible. The same script, mode and seed give the same output.
--multilineSpreads the output over several lines instead of one.
--asciiLimits the output to plain ASCII characters.
--wset NTrades memory use against speed. Each mode has a sensible default, so most people never change it.

Macros

Macros are special calls you place in your script. LuaFlux replaces them while it builds, so they never appear in the output. Use them to protect specific values or to control how a part of your script is handled.

Add shims so your script still runs

Outside of LuaFlux, a macro is just an undefined function. Declare a shim for each macro you use. A shim is a global function that returns its arguments, wrapped in a guard that is removed during the build.

if not FLUX_OBFUSCATED then
    function FLUX_ENCSTR(...) return ... end
    function FLUX_ASSUME(...) return ... end
    function FLUX_OPAQUE(...) return ... end
end

Every name starting with FLUX is reserved. Declaring one as a local, or calling a macro that does not exist, stops the build with an error.

Values

MacroResult
FLUX_OBFUSCATEDtrue in the protected build. Use it in guards like the one above.
FLUX_LINEThe line number of the call in your source.

Protection

MacroWhat it does
FLUX_ENCSTR(s)
FLUX_ENCSTR(s, key, check)
Encrypts a string. With three arguments the string is also tied to a runtime value, and only reads correctly when check matches.
FLUX_ENCBUF(s)
FLUX_ENCBUF(s, key, check)
Same as FLUX_ENCSTR, but returns a Luau buffer. Luau only.
FLUX_ENCNUM(n)
FLUX_ENCNUM(n, bindings)
Encrypts a number. bindings is a table such as { [expr] = expected } with up to 8 entries.
FLUX_ASSUME(expr, expected)Locks the build to a constant. If expr does not equal expected at runtime, the script stops.
FLUX_PRECHECK(fn, keys)Runs fn and stops the script if its result is not one of the expected keys. Accepts one key or a table of keys.
FLUX_REWRITE(expr)Rewrites an expression into a harder to read form that returns the same result.
FLUX_OPAQUE(bool)Returns the boolean you pass in, in a way that is hard to predict. Adds noise to control flow at no risk.
FLUX_CRASH()An expression that raises an error that cannot be recovered from.

Older spellings still work: FLUX_STRENC, FLUX_BUFENC and FLUX_NUMENC.

Performance

MacroWhat it does
FLUX_NO_VIRTUALIZE(fn)Keeps a function protected but tuned for speed. Use it on code that runs often.

Example

local token = FLUX_ENCSTR("secret", 0x1234, userId)
local level = FLUX_ENCNUM(42, { [playerLevel] = 5, [gameId] = 9 })

if FLUX_ASSUME(game.PlaceId, 123456789) then
    print("ok")
end

local magic = FLUX_REWRITE(0xDEADBEEF)

Performance

Protection always costs something. The goal is to spend it where it matters and keep everything else fast.

Choose the lightest mode that fits

Heavier modes are for code you cannot afford to lose. Everything else runs better on Heavy, Normal or Weak.

Leave non sensitive code out

Interface code, rendering and physics rarely need protection. Keep them in a separate file and obfuscate only the logic that matters.

Wrap hot functions

FLUX_NO_VIRTUALIZE keeps a function protected while making it much cheaper to run.

local hot = FLUX_NO_VIRTUALIZE(function(a, b)
    return a + b * 2
end)

Tidy your source first

Repeated lookups get expensive once protected. Store them in a local before you build.

Before

a.b.c.d.e.f.g.x = 1
a.b.c.d.e.f.g.y = 2
a.b.c.d.e.f.g.z = 3

After

local t = a.b.c.d.e.f.g
t.x = 1
t.y = 2
t.z = 3

Watch what runs every frame

Overhead shows up where code runs most. If it is not sensitive, keep it out, or wrap it in FLUX_NO_VIRTUALIZE.

  • RenderStepped, Heartbeat and Stepped connections
  • UserInputService handlers such as InputBegan and InputChanged
  • Metamethod hooks like __index, __namecall and __newindex
  • Tight loops with short wait() or task.wait() intervals
  • Events that fire many times a second

Roblox

Instance attributes work as usual. SetAttribute, GetAttribute and GetPropertyChangedSignal need no changes in your code.

local part = workspace.Part

part:SetAttribute("Health", 100)
local hp = part:GetAttribute("Health")

part:GetPropertyChangedSignal("Health"):Connect(function()
    print("health changed:", part:GetAttribute("Health"))
end)

Write attribute names as plain string literals so they are protected with the rest of your strings. Names built at runtime are left as they are. When a value must depend on who is running the script, wrap the name in FLUX_ENCSTR.

local key = FLUX_ENCSTR("SessionToken", 0x99, userId)
part:SetAttribute(key, tokenValue)

Changelog

v1.4 Latest

  • ImprovedMore variation between builds of the same script.
  • ImprovedLuau compatibility.
  • ImprovedCleaner error output.

v1.3

  • AddedThe --wset option.
  • AddedLuau parsing for casts, variadic types and function types in return position.
  • ImprovedMemory use and general stability.

v1.2

  • ImprovedHeavy, Normal and Weak now offer stronger protection while staying light.
  • AddedThe --multiline and --seed options.
  • AddedLine numbers in build errors.

v1.1

  • ImprovedReworked core for better speed and protection.
  • ImprovedOnyx received its full protection set.
  • FixedStricter handling of numeric edge cases.

FAQ

My script errors when I run it without obfuscating

Macros need shims in your source. Declare each macro you use as a global function that returns its arguments, inside an if not FLUX_OBFUSCATED then block. See the Macros section for the full pattern.

Why is my name reported as reserved?

Anything starting with FLUX belongs to LuaFlux. A call such as FLUX_DO_SOMETHING() fails because it is not a real macro, and local FLUX_NAME = 1 fails because the prefix is reserved. Rename your variable and stick to the documented macros.

My output is too slow

Try a lighter mode, then follow the tips in the Performance section. Moving non sensitive code out of the build and wrapping hot functions in FLUX_NO_VIRTUALIZE fixes most cases.

Are my scripts stored?

No. Scripts are processed and discarded as soon as the output is returned.

Can I use FLUX_STACKALLOC?

Not at the moment. Use a plain table, or wrap the function in FLUX_NO_VIRTUALIZE.

Still stuck?

Our team answers setup questions on Discord.

Join Discord