Releases / v0.105.0
v0.105.0 Plays Anywhere
2026-09-30 Latest
A browser build can play online. It reaches the web and Floptle Cloud, and it joins the same relay lobbies as desktop players. A game can photograph its own world, for a level's cover or a photo mode. An exported game keeps its players' saves in a folder of their own, so installing a new version no longer resets their settings and progress.
Online in a browser
A web build used to be a single-player cut of the game: nothing in a page could reach the network. Now:
http.*,cloud.*,account.*andassets.textureFromUrlwork in a page, through the browser's ownfetch. The server has to allow the page's address (CORS), and the message says so when it does not.- A page joins a relay lobby over WebSockets, in the same lobby as players on
the desktop build.
net.join("cloud://CODE")finds the region's browser leg by itself; for your own relay, start it with--ws-port 7789and join withnet.join("wss://relay.example:7789/CODE"). With--tls-certthe relay serves that port aswss://on the same certificate. - A page joins but does not host. A desktop build or a dedicated server hosts
and the page joins it.
net.hostand a directquic://join are refused in a page with a sentence saying so. app.platform()andapp.isWeb()tell a script where it is running, so a web build can hide what a page cannot do instead of showing buttons that do nothing.
Floptle Cloud from a page also needs fopull.com to allow web builds' addresses
and the managed relays to open their browser port. Until they do, cloud://
joins and Cloud calls from a page report that they could not connect.
Voice chat is not in browser builds yet.
Pictures of your own world
camera.capture renders what a camera sees into a PNG or JPEG:
camera.capture("CoverCam", { w = 480, h = 270 }, function(png, err)
if png then assets.writeBytes("user://levels/" .. name .. ".png", png) end
end)
Name the camera, pass the node, or leave it out for the active one. A camera
that is not the active one renders off-screen, so the player's view never
changes. The picture has the game's post-processing, fog and retro look. The
UI is left out unless you pass ui = true, and draw.line / draw.tri
shapes are left out unless you pass draws = true, so a cover never shows a
HUD or a debug line. camera.captureTexture gives the same picture as a
texture, for showing it without saving it. A capture costs one extra render of
the scene and never stalls the game waiting on the graphics card.
Players keep their data when you ship an update
An exported game used to save inside its own folder, so every new build started with none of the player's settings, progress or files, and a game installed somewhere read-only could not save at all. An exported game now keeps them in a per-user folder that outlives the build:
- Windows:
%APPDATA%\<studio>\<data_id>\ - macOS:
~/Library/Application Support/<data_id>/ - Linux:
~/.local/share/<data_id>/
save.* slots, replays and a crash report go there. data_id is set in
Edit ▸ Project Settings ▸ Game ▸ Data folder. If you leave it empty, your
next export fills it in from the title. Keep it once players have the game:
a new build finds their data by that name. The first time a new build runs, it
copies the saves an older build kept beside itself into the new folder, so a
player who updates in place keeps their progress.
A path starting user:// is the player's own files: levels they built,
replays, screenshots. assets.writeJson("user://levels/mine.json", level)
and the other assets.* calls read and write them,
assets.getContents("user://levels") lists them, assets.remove deletes one,
and app.dataPath() says where they are. Playing in the editor, floptle run
and floptle shot keep all of it in the project's save/ folder, so tests
never touch a player's real files and your builds never ship your own saves.
Because the folder no longer moves, Steam Auto-Cloud can sync it. The export guide lists the roots to add in Steamworks.
Bytes and compression for scripts
assets.readBytesandassets.writeBytesread and write any file as raw bytes.readTextrefuses a PNG or a compressed level, and that is what these are for.data.deflateanddata.inflatecompress and expand bytes (zlib, raw or gzip), anddata.base64Encodeanddata.base64Decodecarry them as text, so a level or a picture fits inside JSON.inflatestops at a size limit, so a small hostile blob cannot fill memory.
Floptle Hub
Engine
The binaries are not signed yet. On macOS, right-click the app and pick Open the first time. On Windows, if SmartScreen appears, pick More info then Run anyway.