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.* and assets.textureFromUrl work in a page, through the browser's own fetch. 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 7789 and join with net.join("wss://relay.example:7789/CODE"). With --tls-cert the relay serves that port as wss:// 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.host and a direct quic:// join are refused in a page with a sentence saying so.
  • app.platform() and app.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:

lua
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.readBytes and assets.writeBytes read and write any file as raw bytes. readText refuses a PNG or a compressed level, and that is what these are for.
  • data.deflate and data.inflate compress and expand bytes (zlib, raw or gzip), and data.base64Encode and data.base64Decode carry them as text, so a level or a picture fits inside JSON. inflate stops at a size limit, so a small hostile blob cannot fill memory.

Floptle Hub

Linux x86_64 9.0 MB Download → sha256 2fdf5438925abde5bdb339cb6b85fcd19cfbd4ad57f1245ad448cf03a3469c5b
macOS Apple Silicon 5.9 MB Download → sha256 79e13cccd455c8bda6daa4894113c11865d75167538a0ef3469840dc2f083646
macOS Intel 6.4 MB Download → sha256 dc9f26e948e559d6a76626876a1993901b6d842b6f78f84c40ebf3c4a49145f0
Windows x86_64 7.3 MB Download → sha256 702e620cd1da72fb53d16c6376ef571218bf16de1b3a1462dab08454d0a5c377

Engine

Linux x86_64 51.8 MB Download → sha256 3d05d2eed571d6fb1162ffba6e293443f0ab0e258f5323c855c77d42516defc8
macOS Apple Silicon 39.9 MB Download → sha256 446053ce75d98336cccc45e243c1f7d4a80bcf7f3eac78607849c4d2bfc48724
macOS Intel 43.0 MB Download → sha256 39dc3e1e2a60218b1c59af82ccf7655dc019f0a0870fbf29ec484e1485ca127b
web 4.8 MB Download → sha256 bf12ce4f56152626f5cd4a6701dad23f978af15fc9553b9f78c2a01bba5e8079
Windows x86_64 48.6 MB Download → sha256 d01f2b769dcd8e83a92f8d2516a0e40c1d6a8a6b54047d9919667acbbd664c8b

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.