if/else chains and nested loops, the script becomes a maze of indirect jumps, dispatcher loops, and meaningless branches. This page covers the most effective control flow techniques for Lua and Luau, with full code examples you can adapt.
Control Flow Flattening
The classic flattening technique replaces structured control flow with a singlewhile true do loop and a numeric state variable. Each basic block becomes a case in a large if/elseif chain inside the loop.
Before: Simple Sequential Code
After: Flattened Dispatcher
Assigning State Numbers
You can assign state numbers in different ways to confuse analysis:- Sequential:
1, 2, 3, 4— simplest, still effective - Random:
47, 12, 89, 3— harder to guess execution order - Shuffled: assign states so the textual order does not match execution order
Multi-Level Flattening
For deeply nested functions, use nested dispatchers. The outer dispatcher handles top-level blocks, and inner functions or blocks can themselves be flattened.Goto Spaghetti (Lua 5.2+)
Lua 5.2 introducedgoto and ::label:: syntax. You can abuse this to create spaghetti code that jumps around unpredictably.
Converting a Simple Function to Goto Spaghetti
Interspersing Dead Labels
Add labels that are never jumped to. They appear as valid targets but are actually unreachable.Opaque Predicates
An opaque predicate is a condition that always evaluates to true or false, but looks complex enough that a static analyzer cannot determine this.Trivially True Predicates
Trivially False Predicates
Algebraic Identity Predicates
Inserting Opaque Predicates
Dead Branch Injection
Inject branches that are always taken or never taken, filling the unused path with plausible-looking code.Always-False Branches with Decoy Code
Always-True Branches
Loop Transformation
Transform recognizable loops into unusual forms.Numeric For to While Loop
Numeric For to Generic For with Custom Iterator
Wrapping in Repeat/Until
Break with Opaque Predicates
Expression Splitting and Reordering
Splitting Compound Expressions
Reversing Condition Logic
Reordering Independent Statements
Goto-based spaghetti is highly effective against decompilers that expect structured control flow. Many Lua decompilers simply fail or produce nonsense when faced with heavy goto usage.
Does control flow obfuscation affect performance?
Does control flow obfuscation affect performance?
Yes. Flattening adds a dispatch overhead per block: every block transition requires a loop iteration, a state comparison, and a state variable update. For hot code paths called thousands of times per frame, this overhead is measurable. Benchmark critical sections and consider leaving performance-sensitive inner loops unflattened, or apply flattening only to initialization and configuration code.
