# Love2D

Run Love2D games on the web with 2dengine's love.js player and host them on Wavedash.

Source: https://docs.wavedash.com/engines/love2d

<GithubLink href="https://github.com/wvdsh/examples/tree/main/example-love2d" />
<PlaytestLink href="https://wavedash.com/playtest/love2d-example/93c6f086-5ad8-4bb5-b168-23ed8291cda6" />

Love2D reaches the browser through [2dengine's love.js player](https://github.com/2dengine/love.js) — a standalone player that runs `.love` files in the browser via an Emscripten build of LÖVE 11.5. No compile step: just package your game as a `.love` and drop the player's `player.js`, `love.js`, and `love.wasm` alongside.

## Build for web

1. Download the [2dengine/love.js repo](https://github.com/2dengine/love.js) and extract it — call the extracted dir `LOVEJS_DIST`. It must contain `player.js`, `style.css`, `11.5/love.js`, `11.5/love.wasm`, and `lua/normalize1.lua` + `lua/normalize2.lua`.
2. Package your game as a `.love` (zip `conf.lua`, `main.lua`, and any other modules).
3. Copy `player.js`, `style.css`, the `11.5/` folder, and the `lua/` folder alongside your `.love` and an `index.html` that loads `player.js?g=your-game.love&v=11.5`.

The example's `build.sh` does all this for you — see [`example-love2d/build.sh`](https://github.com/wvdsh/examples/blob/main/example-love2d/build.sh).

## SDK integration

2dengine's `love.js` ships a JS-interop trick rather than a true FFI: Lua can synchronously evaluate JavaScript by calling `os.execute("javascript:<code>")`. Internally that hops `love.system.openURL` → `window.open` (intercepted by `player.js`) → `eval`. The result is written to `window._output`, which `io.read()` can read back via `player.js`'s `window.prompt` override. `normalize1.lua` glues all of that together, so `os.execute` returns the evaluated string and no JS shim is needed.

```lua
-- wavedash.lua
local M = {}

function M.init()
  os.execute([[javascript:
    window.WavedashJS && window.WavedashJS.init({ debug: true })
  ]])
end

function M.update_load_progress(fraction)
  local clamped = math.max(0, math.min(1, fraction or 0))
  os.execute(string.format([[javascript:
    window.WavedashJS && window.WavedashJS.updateLoadProgressZeroToOne(%f)
  ]], clamped))
end

return M
```

## Call it from the game

At the end of `love.load`:

```lua
local wavedash = require("wavedash")

function love.load()
  wavedash.update_load_progress(0.3)   -- engine ready
  -- load fonts, assets ...
  wavedash.update_load_progress(0.7)   -- assets loaded
  -- final game setup ...
  wavedash.update_load_progress(1)     -- all done
  wavedash.init()
end
```

## Adding more SDK methods

Embed the arguments directly into the JS literal — `%q` quotes strings safely for Lua-into-JS string contexts:

```lua
function M.submit_score(name, score)
  os.execute(string.format([[javascript:
    window.WavedashJS && window.WavedashJS.getLeaderboard(%q).then((lb) => {
      if (lb.success) window.WavedashJS.uploadLeaderboardScore(lb.data.id, %d, true);
    })
  ]], name, score))
end
```

Then call from Lua: `wavedash.submit_score("level-1", 3500)`.

For methods that need to return data back to Lua, append a `return` to the JS string and read it with `io.read()`:

```lua
function M.get_username()
  os.execute([[javascript:'return ' + (window.WavedashJS ? window.WavedashJS.getUsername() : '')]])
  return io.read() or ""
end
```

Async (Promise-returning) SDK methods are the ceiling of this approach — you'd need a JS-side dispatcher that resolves into `window._output` before Lua reads it.

## wavedash.toml

```toml
game_id = "YOUR_GAME_ID_HERE"
upload_dir = "./build"
```

<Note>
love.js 11.5 bundles the entire LÖVE runtime (~10-15MB). Compress images and prefer OGG for audio to keep the initial payload small.
</Note>
