mango.onl docs

Editor

mango.onl/editor is a Luau editor built for Roblox code. It's the same editor engine VS Code uses, with a Luau parser, linter, Roblox's real compiler and the full Roblox API bolted on. It's meant for reading and fixing scripts, including the huge one-line ones people hand each other, not for replacing Studio.

Everything runs in your browser. Nothing you open is uploaded unless you hit Share. No account is needed except for sharing and the sandbox.

The layout is three columns: your files on the left, the code in the middle, and the panels on the right. Everything the editor can do is in the menus along the top (File, Edit, Go, View, Tools, AI, Help), and Commands (F1) searches every one of them. Save, Beautify and Share have their own buttons.

The panels are Problems, UI preview, DataStore check, Syntax tree, Bytecode and Stats. Their tabs run down the right edge and never go away: click a tab to open it, and click the open one again to fold the panels away. Drag the panels' left edge to make them wider. Ctrl+B hides the file list. The editor remembers all of this, and which panel you had open. On a phone the editor is read-only; it's not built for typing on a touchscreen.

Opening and saving

There are a few ways to get code in:

  • Paste it. A blank file is open when you arrive. If you paste code wrapped in ```lua fences (the way AI assistants return it), the fences are stripped automatically. Ctrl+Z puts them back if you wanted them.
  • File → Open files takes one or more .lua, .luau or .txt files.
  • Drag and drop files, or a whole folder, anywhere on the page.
  • File → Open folder (Chrome and Edge only) opens a project folder with read and write access. It skips .git, node_modules, Packages and .vscode, so a Wally project doesn't drown you in dependencies.

Save (Ctrl+S) is always there. A file from an opened folder is written back in place. Anything else, like something you pasted, dropped or opened from a share, asks where to save it (Chrome and Edge), and from then on Ctrl+S writes straight to that file. Firefox and Safari don't let a page save files, so there Save downloads the file instead. Save as is Ctrl+Shift+S, Save all saves every changed file in an opened folder, and File → Download a copy always works.

Saving is always manual; the editor never writes to your disk on its own. If a file changes on disk while you have it open (Rojo syncing, another editor), you'll get a notice. If you have no unsaved edits it reloads; if you do, it asks you to decide.

Files you pasted or dropped are kept in your browser between visits (up to 20 files, each under 400 KB), so closing the tab doesn't lose them. Folder files aren't copied; the editor asks to reopen the folder instead. Right-click a file, or press F2 in the file list, to rename it. Opening a share link adds its files next to yours; it doesn't replace them.

Folders

Use the folder button at the top of the file list (or File → New folder) to make a folder, and drag files onto it to move them. Drag a file onto empty space to move it back to the top. Right-click a folder to add a file inside it, rename it or remove it. Removing a folder never deletes files; they move up a level. A folder opened from disk shows its real folders. Those files aren't moved here, because they mirror your disk: move them in your file manager instead.

Big and minified files

Obfuscated scripts are usually one line that's megabytes long, and that's what breaks most web editors. This one scales features back as files get bigger instead of freezing:

SizeWhat changes
Under 512 KBEverything on.
512 KB to 5 MBFolding, occurrence highlighting, type hints and semantic colours off. Go to definition and rename still work.
5 MB to 12 MBMinimap, word suggestions, go to definition and rename off too.
Over 12 MBView and edit only. No analysis.

The badge in the bottom right tells you which level a file is on. Any line over 10,000 characters is cut off visually so it can't lock the tab, and you'll be told the file looks minified. Hit Beautify (Ctrl+Shift+F) to reformat it into normal lines. Beautify is one undo step, so Ctrl+Z takes you straight back.

Navigating code

The editor knows which declaration every name refers to, following Luau's real scoping rules. That drives everything in the Go menu:

  • Go to definition (F12 or Ctrl+Click) jumps to where a local, parameter or function was declared, and to Module.field definitions. On a require(...), or on the name a module was required into, it opens that module's file, and Util.greet lands on greet inside it.
  • Find all references (Shift+F12) and peek (Alt+F12).
  • Rename symbol (F2 in the code) renames a local everywhere it's used, including inside backtick strings. It refuses a new name that would change what another variable refers to and tells you which line, so a rename can't quietly break the script. This is the quickest way to make an obfuscated script readable: rename _0x3f to what it is.
  • Symbols in this file (Ctrl+Shift+O) lists functions, methods, module tables, types and callbacks like Players.PlayerAdded:Connect(…). Symbols in all open files is Ctrl+Alt+O.
  • The bar above the code shows where the cursor is: the file (or its place in the Rojo game tree) and the functions around it. Click a part to jump there.

Folding follows the code too: functions, blocks, tables, runs of comments, and --#region / --#endregion pairs.

Type hints and colours

Inlay hints show what a local holds after its name, in grey: local plr = Players.LocalPlayer gets : Player, local pos = part.Position gets : Vector3. They come from the same Roblox API data as autocomplete, and appear only when the editor is sure. Hover a name for its type, where it was declared and how often it's read.

Semantic colours paint parameters, local functions and your own globals differently from ordinary locals, and fade anything that's never read. In obfuscated code where every name looks alike, that's often the fastest way to see what's what. Both can be switched off in View; the setting is remembered.

Roblox API autocomplete

The editor knows the whole Roblox API: every class, property, method, event and enum, with parameter and return types. It gets this from the same place Studio does. On load it asks Roblox which Studio build is live, then downloads that build's API-Dump.json from Roblox's CDN. If Roblox's CDN won't answer, it falls back to the copy MaximumADHD's Roblox Client Tracker publishes on GitHub.

The dump is about 7 MB. It's boiled down to under 1 MB and kept in your browser, so it's only downloaded again when Roblox ships a new Studio build. The badge in the status bar shows which build you have. Click it to check for a newer one.

What completes

  • player. lists properties and events, player: lists methods. Own members come first, inherited ones after, deprecated ones last and struck through.
  • Inside game:GetService(" you get every service name. Inside Instance.new(" you get only classes that can actually be created. IsA, FindFirstChildOfClass and friends complete class names too.
  • :GetPropertyChangedSignal(" lists the properties of whatever it's called on.
  • Enum. lists enums, Enum.KeyCode. lists its items with their values.
  • Typing local Players at the start of a line offers the whole = game:GetService("Players") line.

How it knows what a variable is

There's no full type checker, so the editor follows the patterns Roblox code actually uses:

  • game, workspace, script and plugin are known.
  • local x = game:GetService("Players") and Instance.new("Part") give x that class.
  • local cam: Camera = ... uses the annotation.
  • Chains follow the dump: Players.LocalPlayer.Character is a Model. FindFirstChildOfClass("Humanoid") returns a Humanoid, :Clone() keeps the type.
  • Something like workspace.Map.Door can't be known ahead of time, so it's treated as a plain Instance.

When it can't work a chain out, it shows nothing rather than a wrong list.

Hover, signatures and deprecation

Hover a member to see its signature, which class it comes from, whether it yields or is read-only, and a link to the Roblox docs. Typing ( after a method shows its parameters and highlights the one you're on.

Deprecated members get a strike-through and a warning, for example workspace:findFirstChild() or part:remove(). Where there's a direct replacement, Ctrl+. (or the light bulb) swaps it in. Deprecated classes in GetService/Instance.new and deprecated enum items are flagged the same way.

Problems and quick fixes

The Problems panel lists syntax errors and lint findings for the open file, and the same issues are underlined in the code. Click one to jump to it. The lint rules only flag things that cause real bugs or real lag in a Roblox game. There are no style rules.

RuleLevelWhat it catches
infinite-looperrorwhile true do with no task.wait() and no break. Freezes the client.
implicit-globalwarningAssigning to a name that was never declared, which makes a global. Usually a missing local.
redeclaredwarningDeclaring the same local twice in one scope.
deprecatedwarningwait, spawn, delay, old camelCase methods and similar.
heavy-in-frame-loopwarning:GetChildren(), :GetDescendants() and similar called inside RenderStepped/Heartbeat.
unused-localhintA local that's never read.
shadowhintA local that hides an outer one with the same name.
unknown-globalhintA name that isn't a local or a known global. Usually a typo. Executor globals like getgenv are known.
waitforchild-no-timeouthintWaitForChild without a timeout, which waits forever if the child never shows up.
unstored-connectionhint:Connect() whose connection is thrown away, so it can never be disconnected.
unprotected-callhintA DataStore, HTTP, teleport or similar call that throws when the request fails, not inside pcall.
concat-in-loophintBuilding a string with .. inside a loop. Slow on big loops; use table.concat.
heavy-in-loop, alloc-in-frame-loophintAllocating a new table every iteration or every frame.

The checkboxes at the top of the panel filter by level.

Quick fixes

Put the cursor on an underline and press Ctrl+. (or click the light bulb):

  • wait, spawn, delay → task.*, one at a time or every one in the file at once.
  • Add a timeout to WaitForChild.
  • Store an unstored connection in a local.
  • Wrap an unprotected DataStore or HTTP call in pcall, with a warn when it fails.
  • Prefix an unused local with _ to mark it deliberate.

Select some lines and Ctrl+. also offers Wrap selected lines in pcall.

UI preview

The UI tab draws the ScreenGui a script builds, without running it. It reads Instance.new calls, property assignments and .Parent in the order they're written, including the usual helper, create("Frame", {Size = ...}), and loops over a fixed count or a literal list. Pick a screen size to see scale versus offset at work, tick hidden to include Visible = false elements, and click any element to jump to the line that made it. The expand button opens a large view.

It lays out Size, Position, AnchorPoint, ZIndex, colours and transparency, borders, text (size, scaling, alignment, wrapping), UICorner, UIStroke, UIPadding, UIListLayout, UIGridLayout, UIGradient, UIScale, UIAspectRatioConstraint and UISizeConstraint. Images show as placeholders, since a web page can't fetch Roblox assets. It's an approximation: values that depend on the running game (a player's name, a tween) keep their defaults.

DataStore check

The DataStore tab checks a value before you save it. Paste a Luau table or JSON, or select one in the code and press Use selection (right-click → Check the selected table also works). You get the encoded size against the 4 MB limit, which fields take the most room, and the same errors Roblox gives: a Vector3 or Color3 can't be stored, mixed and sparse tables fail, and math.huge doesn't survive.

Below it, the request budget: set players per server and how often you autosave to see your writes a minute against the per-server budget, plus checks on key and store name length. The full simulation of DataStores, with versions and failures, is the sandbox.

Rojo projects

Open a folder (or drop one) that has a default.project.json and the file list switches to the game tree: services, folders and scripts the way Studio's Explorer shows them, following Rojo's rules (.server.luau is a Script, .client.luau a LocalScript, init.luau makes the folder the module). The Game / Files buttons switch between the tree and the folders on disk.

With the tree known, require(ReplicatedStorage.Shared.Util), require(script.Parent.Util) and string requires ("./Util", and "@alias/Util" from .luaurc) all resolve for Go to definition. Typing a module's name offers to add its require line at the top, through a service local if the file has one.

There's no live sync to Studio. That needs the Rojo program running on your computer (rojo serve), and a web page can't be that. Keep using Rojo for syncing; the editor reads the same project file.

Color3 and UDim2

Every Color3.fromRGB, Color3.new, Color3.fromHex and Color3.fromHSV call with plain numbers gets a colour swatch. Click the swatch or the call itself to open a colour picker. The picked colour is written back in the same form it was in, so an fromRGB stays an fromRGB. Click the header of the picker to switch to another form.

UDim2.new, UDim2.fromScale and UDim2.fromOffset calls have a dotted underline. Click one and you get a small grid standing in for the parent frame:

  • Drag to move the point. That changes the scale values.
  • Hold Alt while dragging to change the offsets instead.
  • Hold Shift to snap to 5% steps.
  • Pick a screen size in the corner to see what the offsets mean at 1080p, on a laptop, a phone or a tablet.
  • Position mode shows where the frame's corner lands. Size mode shows the box. It picks the right one from the line (.Size = vs .Position =) but you can switch.
  • Offset → scale folds the pixel offsets into scale at the chosen screen size, and Scale → offset does the reverse.

The code updates as you drag, and the whole session is a single Ctrl+Z. Calls with variables instead of numbers are left alone, since there's no value to show.

Constant unpacker

Tools → Unpack constant arrays. A lot of obfuscators pull every string and global into one table and then only ever refer to it by index. That turns print("hi") into K[12](K[4]). The unpacker puts the values back where they're used.

-- before
local K = {"\104\105", game, "Players", "print"}
local plr = K[2]:GetService(K[3]).LocalPlayer
getfenv()[K[4]](K[1] .. "!")

-- after
local plr = game:GetService("Players").LocalPlayer
getfenv()["print"]("hi!")

It handles:

  • Tables of literals and globals: local K = {"a", 2, game, x = "y"}.
  • Pools filled one by one before they're used: _G[1] = "print", _G[2] = game.
  • Index math: K[0x1F - 30], K["x"], K.x.
  • Wrapper functions like local function F(i) return K[i - 512] end.
  • The shuffle Prometheus does, where a loop reverses slices of the table before anything reads it. The shuffle is replayed first so the values come out right.
  • Escaped strings anywhere in the file ("\80\108\97\121" → "Play") and joining literal concatenations.

Tables that are no longer used afterwards are removed. It won't touch a table that's written to after it's created, reassigned, passed to table.insert/setmetatable, or shadowed by a local or parameter with the same name. Inlining a value that can change would give you readable code that does something different. The notice tells you which tables were skipped and why.

It runs in the background so big files don't freeze the page, and it's one undo step. It doesn't try to crack VM-based obfuscators (Luraph, Moonsec and so on); those don't use a constant table in this form. Beautify first if the file is one line, then unpack.

Templates

Templates (Ctrl+Shift+T) is a library of the boilerplate every game ends up writing: service headers, PlayerAdded with the "players already in game" case handled, leaderstats, a DataStore module with retries and BindToClose, RemoteEvents with server-side checks and rate limiting, UI built from code, tweens, draggable frames, raycasts, CollectionService tags, developer product receipts, a Signal class, a cleanup class and more.

Each one says whether it belongs in a Script, LocalScript or ModuleScript. Search by name or by anything in the code. Insert puts it at the cursor, or open it as a new file. Every template compiles with Roblox's own Luau compiler, and they use current APIs (task.wait, not wait).

Sandbox

Tools → DataStore sandbox, Retry lab or HttpService sandbox. You need to be signed in to use it. It's a simulation of DataStoreService and HttpService so you can check save logic and retry handling without a game server. It doesn't run your script. It's a mock service with a UI, plus an export that lets your real script run against the same mock in Studio (see below).

Everything you make in the sandbox is saved in your browser only.

DataStores

Make stores (with scopes, and ordered stores for leaderboards), or hit Scan script to create every store the open file uses in a GetDataStore("Name", "scope") call. Pick a key and run GetAsync, SetAsync, UpdateAsync, IncrementAsync or RemoveAsync on it. Values are JSON. Every write keeps a version, and you can see the history.

The mock enforces the same limits and error codes Roblox does:

CodeWhen
101Empty key name.
102Key, store name or scope over 50 characters.
103Non-integer value in an ordered store, or incrementing a non-number.
104Storing nil or something that can't be serialised.
105Value over 4,194,304 characters once serialised.
106, 107Bad page size or min/max for GetSortedAsync.
301 to 306Request budget used up (throttled).
403Writing with "Studio API access" turned off.
502Random failure, from the failure % setting.
511, 512Metadata over 300 characters, or more than 4 user IDs.

Budgets use Roblox's per-server numbers and scale with the player count you set: reads and writes get 60 + 40 per player each minute, ordered-store writes 30 + 5 per player, and listing or sorting 5 + 2 per player. Latency and failure rate are adjustable so you can see how your code behaves when things go wrong.

Retry lab

Set up a burst of traffic (how many saves, how far apart, player count, failure rate, latency) and a retry policy (attempts, base delay, multiplier, cap, jitter), then run it. It simulates the whole thing on a fake clock, so a minute of traffic takes a few milliseconds. You get how many saves got through, how many would have been lost, average attempts, how many attempts hit the budget, and the median and 95th percentile wait, plus a timeline of the first 60 requests. When a policy works, Insert this policy as Luau writes a matching retry function into your file.

HttpService

  • JSONEncode takes a Luau table literal and applies Roblox's rules. Mixed tables and gappy arrays fail with the same error Roblox gives, and math.huge comes out as inf, which most servers won't parse.
  • JSONDecode turns JSON into the Luau table you'd get back, and can insert it into your file.
  • GenerateGUID and UrlEncode work like the real ones.
  • RequestAsync mocks: add routes (method, URL with * wildcards, status, body) and test a request. You get the response table your script would see. The HttpEnabled toggle reproduces the "Http requests are not enabled" error. No real request is ever sent from here.

Testing your real script in Studio

Export mock module writes MockDataStoreService.luau: a ModuleScript that behaves like DataStoreService with your sandbox data preloaded and the same latency, failure rate and budgets. Put it in a ModuleScript and swap it in while testing:

local RunService = game:GetService("RunService")

local DataStoreService = if RunService:IsStudio()
	then require(script.MockDataStoreService)
	else game:GetService("DataStoreService")

AST and queries

The AST panel shows the parsed structure of the file as a tree. Click a node to select its code; move the cursor in the code and the tree follows. It handles files with hundreds of thousands of nodes because it only draws what's on screen.

The search box above it takes a small query language:

QueryFinds
CallEvery function call. Any node type shown in the tree works, and a prefix is enough.
Call[callee=loadstring]Calls to loadstring.
MethodCall[method=HttpGet]:HttpGet(...) calls.
Member[object=game]game.Something lookups.
String[value=discord]Strings containing "discord".
Local[name=/^_/]Locals whose name starts with an underscore. /.../ is a regex.
[name=remote]Any node with a name containing "remote".

Fields you can match on are name, callee, method, property, object, value and operator. Plain values match case-insensitively and anywhere in the text. This is the quick way to find every network call or loadstring in a 9,000-line script someone sent you.

Bytecode

The Bytecode panel compiles the file with Roblox's actual Luau compiler, built to WebAssembly. What you see is exactly what Roblox would produce, not a guess. The compiler (about 630 KB) is only downloaded the first time you open the tab.

Each source line is shown above the instructions it produced, so you can see what a line really costs. Click either to jump. Pick -O0, -O1 or -O2 (-O2 is what Roblox uses for live games), and leave compare on to see how much each level saves. The notes at the top point out things worth fixing, like lots of global lookups that should be locals or table allocations that might be in a loop.

The Stats tab next to it has line counts, comment ratio, the longest line, every function with its complexity, and the most used globals. Click a function to jump to it.

Sharing

Share turns your open files into a link that shows the code as a page. It needs a free account: type your email, get a 6-digit code, done. There's no password.

  • Up to 24 files per share, 1.0 MB per file, 2.0 MB in total.
  • Up to 10 shares and 25.0 MB per account.
  • Shares are deleted after 30 days. You can delete them any time from your account.
  • Links are unlisted and can't be guessed, but anyone with the link can read it. Don't share keys or tokens.
  • A share can only be viewed as a web page or downloaded as a file. It can't be pulled with game:HttpGet and loadstringed. That's deliberate: /share is for showing code, not hosting it.

If anything is over the limit, nothing is saved and the error tells you which file and by how much. Someone viewing a share can open it straight in the editor.

Your account page has Sign out for this browser and Sign out everywhere, which ends every session on every device at once, including a copy of your sign-in cookie that ended up somewhere it shouldn't.

AI menu

The editor doesn't talk to any AI service. The AI menu just copies things to your clipboard in a shape that works well when you paste them into one: your files with names, the current problems with line numbers and a request to fix them, a Roblox-specific review request, or the selected code with surrounding context. The status bar shows a rough token count so you know if it'll fit.

Shortcuts

KeysDoes
Ctrl+S / Ctrl+Shift+SSave / Save as (download outside Chrome and Edge)
Ctrl+Shift+FBeautify
Ctrl+Shift+TTemplates
Ctrl+Alt+SShare
Ctrl+Alt+CCopy for AI (selection, or all files)
F1Every command
Ctrl+BShow or hide the file list
Ctrl+Shift+OSymbols in this file
Ctrl+Alt+OSymbols in all open files
Ctrl+GGo to line
F12 / Ctrl+ClickGo to definition
Alt+F12Peek definition
Shift+F12Find all references
F2Rename symbol (in the code), rename file (in the file list)
Ctrl+.Quick fix
F8Next problem
Ctrl+SpaceShow suggestions
Ctrl+F / Ctrl+HFind / replace
Ctrl+DSelect next match (multi-cursor)
Alt+ClickAdd a cursor
Ctrl+/Toggle comment

Help → Keyboard shortcuts shows this list inside the editor.

Use Cmd instead of Ctrl on a Mac.

What stays local

Opening, editing, linting, navigating, renaming, beautifying, unpacking, compiling, the UI preview, the DataStore check and the sandbox all happen in your browser. Your code only leaves it when you create a share. The only outside requests the editor makes are to Roblox's CDN (and GitHub as a backup) for the API dump, which contain nothing about you or your code. Clearing this site's data in your browser removes saved tabs, the API cache and sandbox data. The privacy policy has the rest.

Tools

mango.onl/tools has small Roblox utilities that don't need the editor. All except the changelog run entirely in your browser.

API reference

Every class, property, method, event, callback and enum in the Studio build Roblox is shipping, read from the same API dump as the editor's autocomplete and cached in your browser. Search a class (Part), a member (Humanoid.Health, :Raycast), an enum, or an enum value: 101 or KeyCode 101 finds Enum.KeyCode.E.

A class page shows its inheritance chain, subclasses, and every member with its type, tags (deprecated, yields, read-only, hidden), security level and thread safety. Inherited, deprecated and engine-only members can be switched on and off. Each class, enum and datatype also lists where it appears: every property of that type, every method that returns it, and every parameter that takes it. Every view has its own link.

API changelog

What changed in the engine API between Studio builds: classes, members, enums and enum items that were added, removed, changed (type, parameters, security, tags) or deprecated. Pick any two builds to see everything in between, filter by name, and click a change to open it in the reference. The history comes from the Roblox Client Tracker on GitHub; mango.onl compares each pair of builds once and caches the result. There's an RSS feed with one item per build.

CFrame visualizer

Type a CFrame the way you would in a script, including local lines, *, + Vector3, :Inverse(), :ToObjectSpace(), :Lerp() and every constructor (Angles, fromOrientation, lookAt, fromAxisAngle, fromMatrix, quaternions, the 12 components). You see it in 3D with its Right, Up and Look vectors, and get its position, vectors, orientation, Euler angles, axis-angle, quaternion and matrix, with the same CFrame written five ways to copy. The sliders set position and orientation directly.

Tween & easing

Every EasingStyle in every direction, animated side by side. Click one for its curve up close and a block moving with it, set time, delay, repeats and reverses, and copy the TweenInfo.new(...) line and a TweenService:Create example.

RDD

mango.onl/rdd downloads any Roblox build, Windows or Mac, Player or Studio, straight from Roblox's own CDN. Use it to grab the build an executor needs, roll back after an update, or keep an old version around.

Your browser does all the downloading and puts the files together. Nothing passes through mango.onl, and the files are exactly what Roblox serves. It's based on RDD by Latte Softworks and WEAO's fork of it, rebuilt to work outside WEAO's site and to use a lot less memory.

Picking a build

First pick the platform and app (Windows Player, Windows Studio, Mac Player, Mac Studio), then which build:

OptionGets you
LatestThe build Roblox is serving right now.
PreviousThe build before the current one. For Player this comes from WEAO; for Studio it comes from mango.onl's own record of builds it has seen.
For a cheatThe Roblox build a given executor supports, from WEAO's list. Entries marked "not updated" are on an older build.
HashAny build by its version-xxxxxxxxxxxxxxxx hash. The version- part is optional.

The Build box shows exactly what you'll get (app, version number, hash and download size) before anything is downloaded. It checks that the build exists and matches the app you picked, so pasting a Studio hash while Windows Player is selected tells you straight away.

Current builds on the right lists what's live for all four apps. Use loads one into the form.

Options

OptionWhat it does
ChannelLIVE by default. Public channels like zcanary or zintegration work too.
Output: Save .zip to...Asks where to save and writes the zip straight to disk as it downloads. Uses the least memory. Chrome and Edge only.
Output: Download .zipA normal browser download. Works everywhere, but the whole build is held in memory until it's saved.
Output: Extract to folderUnpacks straight into a folder you pick, no zip. Windows builds only, Chrome and Edge only.
Mac archUniversal (Intel and Apple silicon) or arm64 only. Mac builds only.
Parallel downloadsDownloads four packages at once. Turn it off on a slow or unreliable connection.

Each package has its own progress bar, with the total under them. Downloads are checked against the size Roblox lists and retried up to three times if the connection drops. Cancel stops everything and discards the partial file.

Windows builds are rebuilt into the exact folder layout Roblox's installer uses. The compressed data is copied over as-is instead of being unpacked and packed again, which is why a 200 MB build finishes a couple of seconds after the download does.

Running it

Windows: unzip anywhere and run RobloxPlayerBeta.exe (or RobloxStudioBeta.exe). Nothing gets installed or registered, so your normal Roblox install is untouched. That also means clicking Play on the website still opens your installed Roblox, not this copy.

Mac: you get Roblox's own .app in a zip. Unzip it and move it to Applications, or run it from wherever you like.

Old builds generally won't let you join games once Roblox requires a newer version. That's Roblox, not the download.

Troubleshooting

"not found on Roblox's CDN"
That hash doesn't exist for the app you picked, or Roblox has deleted it. Check the Player/Studio and Windows/Mac choice. Very old builds do get removed.
"is a Studio build" / "is a Player build"
The hash is real but for the other app. Switch the selector.
Failed to fetch, or HTTP 403
Something between you and Roblox's CDN blocked the request: a school or work network, an ad blocker, or a regional block. A VPN such as Cloudflare WARP usually fixes it.
The tab crashes on Studio
Studio is over 500 MB. Use Save .zip to... or Extract to folder so it's written to disk as it goes instead of held in memory.
Previous isn't available
For Studio, mango.onl only knows builds it has seen since it started recording. Find the hash elsewhere and use Hash.
The versions look stale
Versions and the executor list are cached in your browser for 5 to 10 minutes so the page doesn't hit the same APIs every load. A build's file list is cached for a week, since a hash never changes. Clear the site's data to force a refresh.

Public API

These endpoints are open to anyone. All return JSON. They're rate limited to 120 per minute per IP; go over and you get HTTP 429 until the window resets. Cache what you fetch; nothing here changes faster than once a minute or so.

GET /api/version

The current Roblox build for each app, and the one before it. Checked against Roblox's client settings at most every 5 minutes. Sends Access-Control-Allow-Origin: *, so you can call it from a web page.

{
  "ok": true,
  "WindowsPlayer": {
    "version": "0.740.0.7400927",
    "hash": "version-2366ba214ec740ca",
    "since": 1790721811,
    "previous": { "version": "0.739.0.7390687", "hash": "version-4310300497aa4917", "since": 1789488015 }
  },
  "WindowsStudio64": { ... },
  "MacPlayer": { ... },
  "MacStudio": { ... }
}

since is a Unix timestamp for when mango.onl first saw the build, not necessarily when Roblox released it. previous is null until a second build has been seen.

GET /api/version/<binaryType>

One app, plus history: every build recorded for it, newest first (up to 25). The name isn't case-sensitive.

GET /api/version/WindowsStudio64

{
  "ok": true,
  "binaryType": "WindowsStudio64",
  "version": "0.741.19.7411056",
  "hash": "version-76e1a02649ad4f35",
  "since": 1790721811,
  "previous": null,
  "history": [
    { "version": "0.741.19.7411056", "hash": "version-76e1a02649ad4f35", "seen": 1790721811 }
  ]
}

An unknown name returns 404 with the list of valid ones. If Roblox couldn't be reached and nothing has been recorded yet you get 503.

From a Roblox script (HttpService must be enabled):

local HttpService = game:GetService("HttpService")
local data = HttpService:JSONDecode(HttpService:GetAsync("https://mango.onl/api/version"))
print(data.WindowsPlayer.version, data.WindowsPlayer.hash)

GET /api/status

The state of every script mango.onl serves. It's the same data as the status page. Cached for 15 seconds.

{
  "level": "ok",
  "label": "All Services Operational",
  "overall": "ok",
  "up": 3,
  "total": 3,
  "scripts": [
    { "name": "Example", "status": "up", "gameid": "123456", "lastupdated": "2026-09-28" }
  ],
  "handshakes": { "completed": 1520, "rejected": 12, "blocked": 3 }
}
FieldMeaning
levelok, degraded (something unstable) or down (something down or updating).
labelThe headline shown on the site.
scripts[].statusup, unstable, updating or down.
handshakesAll-time counts of loader checks that passed, were rejected, or were blocked.

GET /api/roblox/builds

Recent Studio builds with an API dump, newest first: {"ok":true,"builds":[{"sha":"…","version":"0.741.19.7411056","date":"…"}]}. sha is the Roblox Client Tracker commit for that build.

GET /api/roblox/changes?from=&to=

API changes between two builds from the list above; with no parameters, the latest build against the one before it. A pair nobody has asked for yet answers 202 with "status":"pending" while mango.onl works it out; poll every couple of seconds. When ready: "status":"ready", counts, and changes, a list of {change, what, name, kind?, detail?} where change is added, removed, changed, deprecated or undeprecated and what is class, member, enum or item. CORS-open; ready diffs are cached for a day.

GET /tools/api/changes.rss

The same changes as an RSS feed, one item per build.

Health and announcements

GET /api/health

Returns {"status": "ok"} if the site is up. Not rate limited, so it's safe for uptime monitors.

GET /api/announcements

The banner currently shown on the homepage, if any. Cached for 15 seconds.

{ "active": false }

{ "active": true, "severity": 2, "message": "Scripts are being updated for today's Roblox update." }

Severity is 1 for info, 2 for a warning and 3 for critical.

Questions or something missing from here? Ask in the Discord.