A browser-based Luau code playground with execution and IDE features.
- Code Execution: Run Luau code directly in the browser via WebAssembly
- Type Checking: Type check Luau code directly in the browser via WebAssembly
- Syntax Highlighting: Full Luau syntax support with light/dark themes
- IDE Features:
- Real-time diagnostics (type errors, lint warnings)
- Autocomplete with type information
- Hover tooltips for type inspection
- Multi-file Support: Create and manage multiple files with tabs
- Sharing: Compress and share playground state via URL
- Mobile Friendly: Responsive design that works on phones and tablets
- Bytecode View: View the bytecode of the compiled code
- Frontend: Svelte 5 + Vite
- Editor: CodeMirror 6 with custom Luau language mode
- Runtime: Luau compiled to WebAssembly
- Node.js 18+
- npm or pnpm
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run build
# Preview production build
npm run previewThe Luau WASM module needs to be built separately using Emscripten:
cd wasm
# Install Emscripten if not already installed
# See: https://emscripten.org/docs/getting_started/downloads.html
# Activate Emscripten
source ~/emsdk/emsdk_env.sh # Adjust path as needed
# Build
./build.sh releaseThe built WASM file will be copied to public/wasm/.
Share playground state via URL hash:
https://play.luau.org/#<compressed-state>
The state is LZ-String compressed and versioned JSON:
v2:
c: Optional single file code contents, mutually exclusive withff: Optional object mapping filenames to content, mutually exclusive withca: Currently active filename, optional if only one file existsv: 2: Version number for compatibilitys: Optional and partial (all properties are optional) compiler/type-checking settingsmode:"strict"|"nonstrict"|"nocheck"solver:"new"|"old"optimizationLevel:0|1|2debugLevel:0|1|2compilerRemarks:boolean
b: Optional boolean to show bytecode panel, default false
v1:
files: Object mapping filenames to contentactive: Currently active filenamev: 1: Version number for compatibilitysettings: Optional compiler/type-checking settingsmode:"strict"|"nonstrict"|"nocheck"solver:"new"|"old"optimizationLevel:0|1|2debugLevel:0|1|2compilerRemarks:boolean
showBytecode: Optional boolean to show bytecode panel
Embed the playground in an iframe with a minimal UI:
https://play.luau.org/?embed=true#<compressed-state>
Query parameters:
embed=true: Enables embed mode (hides settings, bytecode toggle, share button)theme=light|dark: Force a specific theme (defaults toautowhich follows system preference)controls=icons: Use icon labels instead of text (defaults to text)expandId: Id of the host checkbox that grows the frame. Present only when the code is taller than the collapsed height (set by generated snippets)
Embeds always include a copy button for the active file. When the embed contains a single file there is no tab bar — the controls float over the editor instead.
An iframe cannot resize itself, so the frame's height belongs to the host page. When the code does not fit the requested height, the generated snippet brings a checkbox and a stylesheet that grow the frame. The expand button is inside the frame, first in the action row (expand, copy, open, check, then play), and posts a message that checks the box:
<div class="luau-embed" style="--luau-collapsed: 400px; --luau-expanded: 828px;">
<input class="luau-embed-toggle" id="luau-embed-k3f9qa" type="checkbox">
<iframe src="https://play.luau.org/?embed=true&expandId=luau-embed-k3f9qa#<state>"></iframe>
<script>/* sets the checkbox when the frame posts luau-embed-expand */</script>
</div>The expanded height is measured from the code when the snippet is generated — line count
against the editor's line height, plus the tab bar for multi-file embeds — and capped at
80vh. Code that already fits gets a plain iframe with no toggle. Editing the code inside
an embed does not change these heights; regenerate the snippet to update them.