Web Assembly

Based on ❤️ wasmoon

The lrocket-build-wasm compiler extension adds three WebAssembly build targets to lrc: .html, .js and .wasm.

It is based on the wasmoon project and works by loading base64-encoded versions of custom-tailored Lua binaries. This allows to embed compiled Lua bundles and native Lua modules (e.g. written in C) via WebAssembly.

The result is a single Javascript file which runs Lua code that can interact with the DOM / Javascript APIs and vice versa – meaning that other <script>-tags can interact with variables and functions provided by Lua (see Interacting with Javascript APIs).

Requirements

  • Make sure to have the WASI SDK and a compatible compiler (e.g. clang) installed

Compiling for Web Browsers

Command-Line Usage

The extension provides three targets for WebAssembly:

Javascript

You can compile to Javascript, to include your Lua code as Javascript module:

> lrc --otype wasm-js main.lua -o output-lib.js

HTML

You can compile directly to HTML, to write your complete page in Lua (starting off with document.body.innerHTML = [[...]]):

> lrc --otype wasm-html main.lua -o index.html

WASM

Or compile to .wasm to handle the embedding into the libwasmoon template yourself:

> lrc --otype wasm main.lua -o lua-module.wasm

If you would like to handle the compilation yourself (e.g. use a different compiler than lrc) virtually any compiler that targets wasm32 can be used to build your own Lua binary script.

For details see Using a Different Compiler.

Interacting with Javascript APIs

Libwasmoon makes the window-object available inside the global Lua environment – This allows to use any desired Javascript API via Lua syntax!

All is made possible by the fantastic work of the wasmoon project!

Examples

Button Callback

document.innerHTML = '<button>Click me!</button>'

local button = document.querySelector 'button'
button.addEventListener('click', function()
    alert('Lua function called!')
end)

Lua Function in Javascript

Tip self is an alias for the window object and since Javascript compiled with lrc works in Service Workers too, it is good practice to use self as it works in both regular and Service Worker contexts:

To export a function to the global Javascript environment you can add it to the self object:

-- better: self.myLuaFunction()
function window.myLuaFunction()
    return math.random(1, 42)
end
// in javascript
alert('The answer to everything is: ' + myLuaFunction());

Geo Location

navigator.geolocation.getCurrentPosition(function(loc)
    -- prints to console.log()
    print('You are here', loc.coords.latitude, loc.coords.longitude)
end)

Advanced Use

Using a Different Compiler

Any compiler that targets wasm32 (ideally wasm32-wasip1 / wasm32-wasip1-threads) can be used to build the binary that will be embedded into the libwasmoon template.

The compiled binary should export a symbol named void __lr_entrypoint(lua_State *L) (called by libwasmoon after the Lua-state has been initialized).

We recommend linking against lua-wasm32, which is a static lua library (the one lrc links against when compiling bundles for WebAssembly). It is available via luarocks (note that the rock also ships a copy of the libwasmoon template):

> luarocks install lua-wasm32

For example, the clang-compiler could be used to compile a custom lua interpreter which can then be embedded into the libwasmoon template:

> clang --target=wasm32-wasip1-threads -o lua-module.wasm my-custom-interpreter.c \
    <path-to-lua-wasm32>/lib/liblua-5.4.a

Turining lua-module.wasm into Javascript:

> sed dist/loader.js.tpl "s/{{libwasmoon:wasm_bytes}}/$(base64 -w0 lua-module.wasm)/g" > \
    output-lib.js

Using the module in HTML:

<!DOCTYPE html>
<body></body>
<script src="output-lib.js"></script>

You may also embed the script between an inline <script>...</script>-tag.