Playing sounds
There is no Lua function that plays a sound. A browser page can, because it is a normal web page with the HTML audio element. A client script loads a small hidden page and tells it what to play. The server can trigger that for one player or for all.
Sounds played this way are plain stereo. They have no position in the world and do not get quieter with distance.
The resource
resources/sounds/manifest.lua
name "sounds"
server "server.lua"
client "client.lua"
files { "ui/*" }
resources/sounds/ui/index.html
<!doctype html>
<meta charset="utf-8">
<style>html, body { margin: 0; background: transparent; }</style>
<script>
const cache = {};
modkit.events.add("play", (file, volume) => {
const audio = cache[file] || (cache[file] = new Audio(file));
audio.volume = Math.max(0, Math.min(1, volume));
audio.currentTime = 0;
audio.play().catch(error => modkit.trigger("soundBlocked", file, error.name));
});
modkit.events.add("stop", () => {
for (const name in cache) { cache[name].pause(); }
});
modkit.trigger("soundsReady");
</script>
Put your sound files next to it, for example ui/horn.ogg and ui/rain.ogg. Use .ogg or .wav. The files { "ui/*" } line sends them to the clients.
resources/sounds/client.lua
local page
local ready = false
local waiting = {}
modkit.events.add("onClientReady", function()
page = modkit.browser.add("ui/index.html")
if not page then return end
page:on("soundsReady", function()
ready = true
for _, item in ipairs(waiting) do page:call("play", item[1], item[2]) end
waiting = {}
end)
page:on("soundBlocked", function(file, reason)
modkit.log.warn("could not play " .. tostring(file) .. ": " .. tostring(reason))
end)
end)
modkit.events.add("sounds:play", function(file, volume)
if type(file) ~= "string" or file:find("..", 1, true) then return end
volume = tonumber(volume) or 1
if ready then page:call("play", file, volume) else waiting[#waiting + 1] = { file, volume } end
end)
modkit.events.add("sounds:stop", function()
if ready then page:call("stop") end
end)
resources/sounds/server.lua
modkit.exports.register("play", function(player, file, volume)
player:call("sounds:play", file, volume or 1)
end)
modkit.exports.register("playAll", function(file, volume)
modkit.events.callAll("sounds:play", file, volume or 1)
end)
modkit.commands.add("horn", function()
modkit.events.callAll("sounds:play", "horn.ogg", 0.8)
end, true)
modkit.events.add("zoneEnter", function(player, zone)
if zone.name == "sanctuary" then player:call("sounds:play", "chime.ogg", 0.5) end
end)
Other resources play a sound with modkit.exports.sounds.playAll("horn.ogg").
Things to know
- The page never becomes active (
setActiveis not called), so it takes no input and the player does not see it. - The browser is the Chromium build that ships with the game. Chromium decides through its autoplay policy whether a page may start audio without a click. When it refuses,
play()fails withNotAllowedError, and the page above reports that to the client log ascould not play <file>: NotAllowedError. A sound that follows a click inside an active page is always allowed. - File names are relative to the page,
horn.oggmeansui/horn.ogg. - Only players who joined with the Isle Modkit launcher hear anything. Others have no client scripts.
- Client scripts start some time after the join. A sound the server sends before that is lost, which is fine for effects and wrong for anything important.
- Keep files small. Every player downloads the whole
uifolder once after joining. - Looping music works the same way. Set
audio.loop = truein the page and add astopcall.