Lua Playground — Run Lua in Your Browser

A Lua scratchpad: tables, coroutines, metatables and the standard library, executed in a sandboxed iframe with the output beside the editor.

Ready — the first run loads the Lua runtime from jsDelivr once, then it is cached locally. Your code stays in this tab.

Outputprint and errors
 

Lua 5.3 through Fengari. Stop discards the iframe, which ends a loop that would not otherwise yield.

What this playground does

This page runs Lua snippets in the browser. It is built for the Lua you meet in games, embedded configuration, teaching examples and small algorithm exercises: tables as the one data structure, metatables, coroutines, string patterns and the standard library. Write a snippet, press Run, and read print() output beside the editor. The editor has Lua syntax highlighting, line numbers, bracket matching, completion for the standard library and Ctrl/Cmd + Enter to run. The isolation model is the same one described in running code in the browser without a server.

The interpreter is Fengari, Lua 5.3 implemented in JavaScript, running in a Worker inside a sandboxed iframe. It is not a JavaScript re-implementation of Lua syntax: the language semantics, multiple returns, metatables and coroutines come from the Lua VM. The trade is speed and access: it runs in the browser’s JavaScript engine and has no native modules.

How a run is executed

The first Run starts a Worker inside the sandbox and loads the Fengari browser build. The parent page fetches that script from jsDelivr once and stores it in the browser’s Cache Storage, then hands the text to the Worker, which evaluates it. That is a static script, about 215 KB; it is not an account or a remote shell. Your source is then compiled by Fengari’s load() and executed in that Worker. Before execution, the runner wraps the Worker console, which is where Fengari sends print() output. The wrapper copies each chunk to the output panel and calls the original console, so the same text is available in DevTools.

If compilation or execution raises an error, the error value and its location are written to the panel and the run is marked as failed. Lua’s error(), assert() and a failing pcall follow their normal semantics; pcall returns false, message rather than ending the run, exactly as in native Lua.

The sandbox boundary

The iframe has sandbox="allow-scripts" and no allow-same-origin. It therefore has an opaque origin: code inside cannot read this site’s DOM, cookies or localStorage, and cannot navigate the parent tab. The page communicates through postMessage only. This is the same boundary used by the Python and JavaScript playgrounds.

Lua has no browser bindings here, so there is no document or fetch to call from a snippet. The Worker inherits the iframe’s opaque origin and has no DOM. Stop discards the iframe, which terminates the Worker. The sandbox is an isolation boundary, not a guarantee about resource use: a tight loop can keep a core busy until you stop it, and a very large table can pressure the tab.

What Lua is good for here

  • Learning the language. Try multiple returns, varargs, metatables and coroutine scheduling in a few lines.
  • Algorithm practice. Tables, sorting, string patterns and recursion with immediate output.
  • Reducing an embedded-script bug. Strip a snippet to the part that changes a table or returns the wrong value.
  • Explaining Lua to someone else. A runnable example with visible output is easier to discuss than a code block.

Limits worth knowing

LimitWhyWorkaround
Lua 5.3, not 5.4 or LuaJITFengari implements the 5.3 language and standard libraryCheck version-specific syntax locally before relying on it
No LuaRocks or C modulesThe browser cannot load native Lua extensionsUse pure-Lua code or a local runtime
No files, processes or socketsThere is no operating-system layer in the sandboxKeep data in tables; use a local Lua for I/O
No DOM bindingsLua is not wired to the browser document hereUse print output; use the HTML playground for page work
Slower than native LuaExecution goes through a JavaScript VMUse it for snippets; benchmark large jobs locally

Privacy and cost

The first run downloads the Fengari script from jsDelivr once; later visits normally read it from the browser’s Cache Storage. That request reveals your IP address and browser to the CDN in the ordinary way. The CDN does not receive your Lua source: the source is sent only to the iframe on this page. If you open the browser’s DevTools, the same print() text appears there because the panel mirrors the console. The site has no account, no code endpoint and no server-side storage, and Stop leaves no Lua process running.

Frequently asked questions

Which Lua version is this?

Lua 5.3, running in the browser through Fengari, a Lua virtual machine implemented in JavaScript. It is a real Lua interpreter and standard library, not a translation of Lua into JavaScript. The runtime is fetched from jsDelivr on the first run and stored in the browser’s Cache Storage; later visits normally reuse it.

Is my code uploaded?

No. The editor and the interpreter run in this browser tab. Your source is sent by postMessage to a sandboxed iframe on the same page; the site has no endpoint that accepts code and no server-side execution. The runtime file comes from jsDelivr, which sees the request for the runtime but not your snippet.

Can I print output?

Yes. print() sends its arguments to the output panel, separated by tabs, with a newline at the end. The browser console receives the same text. io.write is not a reliable output path in this web build; use print when the result needs to be visible in the panel.

Can I use LuaRocks, C modules or the io library?

No. There is no LuaRocks and no native module loader in the browser. The pure-Lua standard library is available, including string, table, math, os date/time helpers, coroutines and metatables. Code that expects files, sockets, a process or a C extension cannot run. require() can load a module that is already embedded in the runtime, not an arbitrary package from disk.

Can I access the DOM or localStorage?

Not the parent page. The sandbox has an opaque origin and cannot read this site’s DOM, cookies or localStorage. Lua itself has no browser bindings in this tool, so there is no document or fetch to call unless the runtime exposes it. The output panel is the interface.

What happens if I write an infinite loop?

Press Stop. Lua runs in a Worker inside a sandboxed iframe, so the page can discard the iframe and terminate the Worker even though Lua itself is not yielding; the editor stays responsive. The next Run starts a fresh Worker, so variables and tables from the stopped run are gone. The Fengari runtime itself stays cached, so it is not downloaded again.

Is it fast enough for real scripts?

It is fine for snippets, exercises and algorithm demonstrations. Fengari executes Lua with a JavaScript virtual machine, so it is slower than native Lua, and a very large table, deep recursion or a numeric loop over millions of iterations will be noticeably slower than the same code locally. Measure locally when performance is the question.

Do Lua variables persist between runs?

Yes, until you press Stop. Each Run reuses the same Worker and the same Lua global environment, so a global assigned in one chunk is visible in the next. Local variables are scoped to their chunk. Stop discards the Worker, which is the reliable way to get a clean environment.

Can I use coroutines and metatables?

Yes. Those are part of the Lua language and the Fengari runtime, not browser features. coroutine.create, coroutine.resume, setmetatable and __index are available. The sandbox is the boundary; the language does not become a JavaScript task runner, and os.execute or file access is not available.

Sources

  • Lua 5.3 Reference Manual — the language, standard library, metatables and coroutines.
  • Fengari — the Lua VM in JavaScript and the fengari-web browser build used here.
  • MDN Web Docs — iframe sandbox, the same-origin policy and the browser security model.

Everything runs in your browser — nothing you type is uploaded.