Embedding Lua in the Web
Hello world
Starlight enables you to run Lua scripts in a webpage by placing Lua code in <script>
tags.
Here's a simple example:
<!DOCTYPE html>
<html>
<body>
<script type="application/lua">
print('Hello world')
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="http://paulcuth.me.uk/starlight/browser-lib/starlight.js" data-run-script-tags></script>
</body>
</html>
Hello world
As you can see we wrap the Lua code in a <script>
tag with type="application/lua"
. This tells the browser not to parse it as JavaScript and also tells Starlight that it should parse and translate the code within.
We're also including the Babel browser runtime because Starlight outputs ES6 and, for the time being at least, most browsers do not fully support ES6. Hopefully over time the need to include Babel will reduce significantly. See also Using Starlight with Grunt to discover how to precompile your scripts and negate the need for Babel.
You'll also need to include Starlight itself. We're using the boolean attribute data-run-script-tags
here to tell Starlight to execute the scripts on page load. Without this attribute the scripts will need to be executed manually.
That's all you need to get started. Notice that the print()
function will output to the browser's console window. This can be modified in the Starlight configuration.
MIME types
Starlight will parse any <script>
tags with the following type
s:
- text/lua
- text/x-lua
- application/lua
- application/x-lua
Including remote scripts
As with JavaScript, you can also include and execute Lua from remote scripts using the src
attribute.
One difference, however, is that files are loaded over
⋮
<p class="result">0</p>
<p><button>+1</button></p>
<script type="application/lua" src="http://paulcuth.me.uk/starlight/lua/counter-app.lua"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.34/browser.min.js"></script>
<script src="http://paulcuth.me.uk/starlight/browser-lib/starlight.js" data-run-script-tags></script>
⋮
window.extract()
local counter = 0
local ui = {
button = document:getElementsByTagName('button')[1],
result = document:querySelector('.result'),
}
function updateUI ()
ui.result.textContent = counter
end
function increment ()
counter = counter + 1
updateUI()
end
function init ()
updateUI()
ui.button:addEventListener('click', increment)
end
init()
Scripts are loaded and parsed in the order that they appear in the page.
You can add a defer
attribute to defer the loading of the script until after others have loaded, so that your script does not block.
The defer
attribute is ignored on inline script tags.
Modules
You can also turn your script tag into a Lua module by giving it a data-modname
attribute.
This script will be preloaded but not executed and can later be required by any other Lua script.
Make sure you define a module in a tag that precedes those that require it, in the order they appear in the page.
⋮
<script type="application/lua" data-modname="fibonacci">
function fibonacci(n)
a, b = 0, 1
for i = 1, n do
a, b = b, a + b
end
return a
end
return fibonacci
</script>
<script type="application/lua">
local fib = require('fibonacci')
print(fib(500))
</script>
⋮
1.394232245616977e+104
The data-modname
attribute can also be used on remote script tags.
When using script tags, all modules need to be explicitly defined within the page; using require()
with a relative file path will fail unless the required script is defined in the markup. This does not apply when you use Starlight to precompile your Lua to JavaScript, see Using Starlight with Grunt for more information.
⋮
<p>
<input value="20" />
<button>Fibonacci</button>
</p>
<p class="result"></p>
<script type="application/lua" src="http://paulcuth.me.uk/starlight/lua/fibonacci-module.lua" data-modname="fibonacci"></script>
<script type="application/lua" src="http://paulcuth.me.uk/starlight/lua/fibonacci-app.lua"></script>
⋮
fibonacci-module.lua / fibonacci-app.lua
Executing Lua programmatically from JavaScript
You can use Starlight to execute arbitrary Lua code from within JavaScript, using starlight.parser.parse()
.
⋮
<button id="click-me">Click me</button>
<script>
var button = document.getElementById('click-me');
button.addEventListener('click', function () {
var lua = "print('Button clicked!!')";
var handler = starlight.parser.parse(lua);
handler();
});
</script>
⋮
A nice way to use the parser in this manner is to place the Lua code in script tags and execute these on demand. In this case, remember to prevent your Lua scripts from executing on page load, by not adding the data-run-script-tags
attribute.
⋮
<button id="click-me">Click me</button>
<script type="application/lua" id="click-handler">
print('Button clicked!!')
</script>
<script>
function init () {
var lua = document.getElementById('click-handler').textContent;
var handler = starlight.parser.parse(lua);
var button = document.getElementById('click-me');
button.addEventListener('click', handler);
}
window.addEventListener('load', init);
</script>
⋮
Configuration
Starlight can be configured by creating a configuration object in JavaScript before including the Starlight browser library.
The configuration object is window.starlight.config
and can currently be used to override the destination of stdout
and add variables to the Lua global namespace.
In the following example, we are redirecting the stdout
to output to a DOM element instead of the browser's console window.
⋮
<pre id="output"></pre>
<script type="application/lua">
print('Hello world')
</script>
<script>
var outputEl = document.getElementById('output');
window.starlight = {
config: {
stdout: {
writeln: function () {
var args = Array.prototype.splice.call(arguments, 0);
outputEl.textContent += args.join('\t');
}
}
}
};
</script>
⋮
To use the configuration object to initialise the Lua global environment, see Interacting with JavaScript.