unreadable.gg

readability

Presets and switches change how the code reads, never what it does. Some of them cost something you might need, like matching line numbers.

which preset

Use balanced if you don't want to think about it.

Use faithful if you want source that compiles to the same bytes, or stack traces that point into the right function. Just know it can be quite verbose.

Use readable if you want the cleanest result. It turns every switch on, so names, types and line numbers can differ from the original.

The site, the executor and the Discord bot start on balanced. The API and the MCP server start on faithful.

presets

A preset sets every switch at once.

faithfulexact bytesbalancedeasy to readreadableeverything on
Readable layoutoffonon
Function line numbersoffonon
Inferred typesoffonon
Fold constantsoffoffon
Model namesoffonon
Drop dead codeoffoffon
Upvalue notesoffonon
Upvalue notes on own lineoffonon
Compound assignmentoffonon
Positive conditionsoffonon
Early returnsoffonon
Paragraph breaksoffonon
Inline small callsoffoffon
Roll up loopsoffonon
Math constantsoffoffon
Const localsoffonon
Field functionsoffonon
Mutable globalsoffonon

Want readable without inferred types? Pick readable and turn that one switch off. On the site, it's under options. Over the API, send {"preset": "readable", "inferredTypes": false}.

what a switch costs

costwhat changes
bytesThe source no longer compiles to the same bytecode.
linesCode moves off its original lines, so line numbers in errors no longer match.
namesValues get names the file didn't have, or lose names it did.
typesType annotations appear that the file didn't have.
framesA call to a small helper becomes the helper's body, so stack traces have one frame fewer.
versionThe source uses syntax only newer Luau understands, like const, so older tools reject it.

The API lists what a result's switches cost in setting.spent. faithful costs nothing.

faithful layout

Faithful keeps every function on its original line, so you'll see runs of blank lines. Turn on Readable layout (freeLines) to get rid of them.

switches

Every switch is off in faithful. Each example shows the same bytecode decompiled without the switch, then with it.

Readable layoutfreeLineson in balancedreadable

Lays functions out as reads best, not padded or packed onto their original lines. costs: bytes, lines

without
print(1) print(2)
function done()
	print(3)
end
done()
with
print(1)
print(2)

function done()
	print(3)
end
done()
Function line numberslineNoteson in balancedreadable

Under the readable layout, ends each function's first line in the line it was defined on.

without
print(1)
print(2)

function done()
	print(3)
end
done()
with
print(1)
print(2)

function done() -- line 2
	print(3)
end
done()
Inferred typesinferredTypeson in balancedreadable

Adds the type annotations inference concludes, beside the input's own. costs: bytes, types

without
local function label(n)
	return "item " .. tostring(n)
end
return label(3)
with
local function label(n: number): string
	return "item " .. tostring(n)
end
return label(3)
Fold constantsfoldConstantson in readable

Writes a computation on literals as its value: 7 for math.max(4, 7). costs: bytes

without
local width = math.max(4, 7)
print(width)
with
local width = 7
print(width)
Model namesmodelNameson in balancedreadable

Names the values the input left unnamed, never with a name that could change the program. costs: names

without
local function v0(arg0)
	local v1 = 0
	for i = 1, #arg0 do
		v1 = v1 + arg0[i]
	end
	return v1
end
return v0({ 1, 2, 3 })
with
local function sum(values)
	local sum1 = 0
	for i = 1, #values do
		sum1 = sum1 + values[i]
	end
	return sum1
end
return sum({ 1, 2, 3 })
Drop dead codedropDeadCodeon in readable

Leaves out values nothing reads and branches that never run. costs: bytes, names

without
local unused = 21
print("ready")
with
print("ready")
Upvalue notesupvalueNoteson in balancedreadable

Says on each function's first line which outer variables it captures, and whether it sees their later changes.

without
local function counter(k)
	local n = 0
	return function()
		n += k
		return n
	end
end
print(counter(2)())
with
local function counter(k)
	local n = 0
	return function() -- upvalues: n (ref), k (copy)
		n += k
		return n
	end
end
print(counter(2)())
Upvalue notes on own lineupvalueNotesOwnLineon in balancedreadable

Puts the upvalue note on a line of its own under the function's first line. costs: lines

without
local function counter(k)
	local n = 0
	return function() -- upvalues: n (ref), k (copy)
		n += k
		return n
	end
end
print(counter(2)())
with
local function counter(k)
	local n = 0
	return function()
		-- upvalues: n (ref), k (copy)
		n += k
		return n
	end
end
print(counter(2)())
Compound assignmentcompoundEverywhereon in balancedreadable

Writes x op= e wherever it means the same thing. costs: bytes

without
local t = { k = 1 }
t.k = t.k + 2
print(t.k)
with
local t = { k = 1 }
t.k += 2
print(t.k)
Positive conditionspositiveBrancheson in balancedreadable

Tests an if without not, and puts a short exit arm first. costs: bytes, lines

without
local function check(ok)
	if not ok then
		print("no")
	else
		print("yes")
	end
end
check(true)
with
local function check(ok)
	if ok then
		print("yes")
	else
		print("no")
	end
end
check(true)
Early returnsearlyExitson in balancedreadable

A function that ends in one big if returns early instead. costs: bytes

without
local function save(s)
	if s then
		print(s)
		print(#s)
	end
end
save("x")
with
local function save(s)
	if not s then return end
	print(s)
	print(#s)
end

save("x")
Paragraph breaksparagraphBreakson in balancedreadable

Adds a blank line between the steps of the code. costs: bytes, lines

without
local squares = {}
for i = 1, 3 do
	squares[i] = i * i
end
return squares
with
local squares = {}
for i = 1, 3 do
	squares[i] = i * i
end

return squares
Inline small callsinlineCallson in readable

Writes a call of a small local helper as the helper's body. costs: bytes, names, frames

without
local function key(name, n)
	return name .. "_" .. n
end
local k = key("hp", 3)
print(k)
with


local k = "hp" .. "_" .. 3
print(k)
Roll up loopsrerollLoopson in balancedreadable

Writes lines repeated with 1, 2, 3 in them as the for loop that runs them. costs: bytes

without
local t = { 5, 6, 7 }
print(t[1])
print(t[2])
print(t[3])
with
local t = { 5, 6, 7 }
for i = 1, 3 do
	print(t[i])
end
Math constantsunfoldMathon in readable

Writes a number the math library computes as that expression: math.pi / 2 for 1.5707963267948966. costs: bytes

without
local quarter = 1.5707963267948966
print(math.sin(quarter))
with
local quarter = math.pi / 2
print(math.sin(quarter))
Const localsconstLocalson in balancedreadable

Declares a local nothing assigns again as const. costs: version

without
local limit = tonumber("8")
local count = 0
count = count + (limit :: any)
print(count)
with
const limit = tonumber("8")
local count = 0
count = count + (limit :: any)
print(count)
Field functionsfieldFunctionson in balancedreadable

Writes a table of keyed functions as local t = {} and a function t.k() per function. costs: bytes, names

without
return { ["clamp"] = function(arg0)
	return math.max(0, math.min(arg0, 1))
end,
["twice"] = function(arg0)
	return arg0 * 2
end }
with
local module = {}
function module.clamp(arg0)
	return math.max(0, math.min(arg0, 1))
end
function module.twice(arg0)
	return arg0 * 2
end
return module
Mutable globalsmutableGlobalson in balancedreadable

Writes game.Players for (game).Players, as scripts compiled with those globals mutable read them. costs: bytes

without
local camera = (workspace).CurrentCamera
print(camera)
with
local camera = workspace.CurrentCamera
print(camera)