wasmcart
Write games in Lua 5.4. Ship them as cartridges.
A LÖVE-style API on a prebuilt engine — love.load, love.update(dt), love.draw. There is no compiler in your loop.
Eight prebuilt carts ship with every release — including Cavern, a complete LÖVE adventure game. Download a .wasc and run it:
npx wasmcart cavern.wasc
Nothing to install but Node 22+. The cart carries the Lua engine, the game, and its assets.
Everything below lives in wasmcart/wasmcart-lua.
The engine wasm ships in the repo, so there is no toolchain to set up. Copy the template and run it:
git clone https://github.com/wasmcart/wasmcart-lua
cp -r wasmcart-lua/template my-game
cd my-game
cp ../wasmcart-lua/build/engine.wasm main.wasm
npx wasmcart .
That opens a window. Edit app/main.lua and run npx wasmcart . again — that is the whole loop. The template also ships a run.sh that does exactly those two steps if you would rather type less.
This is a whole cart. Gamepad in, shapes out, 60 times a second:
local x, y = 640, 360
function love.update(dt)
if love.pad.isDown("left") then x = x - 6 end
if love.pad.isDown("right") then x = x + 6 end
end
function love.draw()
love.graphics.setColor(0.4, 0.8, 1)
love.graphics.circle("fill", x, y, 36)
love.graphics.print("hello from lua!", 40, 40)
end
wasmcart games are gamepad games: design for d-pad, face buttons and sticks. On a desktop the arrows and z/x map onto pad 1 so you can test without a controller.
Not a subset or a dialect: coroutines, metatables, varargs, the garbage collector, string.format, pcall, integer division. Pure-Lua libraries drop into app/ and require works.
One atlas and one draw call for a whole frame of sprites, with circle coverage computed in the fragment shader. A software rasterizer lives in the same binary for hosts with no GL.
love.graphics.newShader takes LÖVE's own effect and position functions and applies to every draw path, solids and sprites and text alike. newMesh gives arbitrary triangles with per-vertex colour and UVs, in fan, strip or triangles mode.
Box2D 3.2 compiled with WASM SIMD, behind both a windfield-style API and LÖVE-shaped entry points. Multiple independent worlds are supported.
Anything that compiles to Lua works unchanged: Teal, Fennel, YueScript, MoonScript. Transpile to .lua, drop it in app/.
love.math.random and math.random are both seeded by the host, so the same seed and inputs produce byte-identical frames on every host.
16 mixer voices at 48kHz, WAV and OGG, plus render targets you can draw into and then draw like an image.
When you want a single shippable file:
npx wasmcart pack --wasm main.wasm --assets app --name my-game \
--width 1280 --height 720 -o my-game.wasc
One .wasc that runs on every wasmcart host: the CLI, a browser, RetroArch through the libretro core, or a handheld.
Cavern, a complete open-source LÖVE adventure game, is ported in ports/cavern/ with all 97 of its Lua files byte-identical to upstream — only its physics wrapper is swapped for the engine's native one. That port is what drove Box2D, the Lua 5.1 compatibility layer, SpriteBatch and a dozen other gaps into the engine.