Releases / v0.84.0
v0.84.0 Half The Time
2026-09-03 Engine Latest
The performance release. A game that was slow on an older machine is faster on every machine, fullscreen works in every build whether or not your menu offers it, and the engine runs a new Lua underneath — with a choice of vector to go with it.
Your game spends half the time it did
A report of a Floptle game running at 10–15 fps on a 2017 laptop turned out to have nothing to do with the graphics card. The picture cost under two milliseconds; the rest of the frame was the engine getting ready to run your scripts. A level with sixty scripted nodes and a couple of hundred mesh colliders cost 12.5 ms of CPU per frame before this release. It costs 6.4 ms now, on the same machine, running the same scripts.
Where it went, in one sentence each:
- A script only pays for the hooks it has. A file with just
updatein it was doing the full per-frame setup three times a frame — once forupdate, and twice more for thefixedUpdateandlateUpdateit did not define. paramsis built when it changes, not on every call. A script that never writes into it is never scanned for writes.- The scene mirror your handles read from is refreshed, not rebuilt, when only positions moved between two passes — which, with physics running, is nearly always.
- Mesh colliders have a size now. They used to be offered to every body on every tick, wherever in the level the body was, and the collision index was rebuilt every tick whether or not anything had changed.
Scripts allocate about two and a half times less per frame as a result, so the collector pauses less often as well. Nothing in your project changes.
Fullscreen, in every build
F11 and Alt+Enter toggle fullscreen in every exported game — borderless, on the monitor the window is on, no mode switch — with no code on your part. For a Video tab:
app.setFullscreen(true) -- or false to go back to a window
if app.fullscreen() then … -- the real state, including after F11
Like app.quit(), this is honest about where it is running: in the editor the
window is the editor's, so the call leaves it alone and says so once in the
Console. The setting is for this session; persist the player's choice with
save.*.
A new Lua underneath
Scripts now run on Luau, the Lua dialect Roblox maintains, instead of LuaJIT. Two reasons: it is faster at what this engine actually does — a script here spends its time crossing into the engine to read a position or set one, and Luau is quicker at every crossing — and it is the only one of the two that can ever reach a browser (web-export.md is the plan).
The whole scripting reference, every shipped script and every tutorial were run on both and compared before the switch. What you will notice:
- The 60-upvalue ceiling is gone. A long controller file no longer stops
loading because it declared one more file-scope
local. - A runtime error names the typo.
node.postion.xused to fail as "attempt to index nil with 'x'". It now says which expression was nil and quotes the line it happened on. gotono longer compiles. It was a LuaJIT extension. Rewrite the loop withbreakor a function; the error names the line.bit.*is still there and answers the same numbers.
If something in your game behaves differently and you cannot see why, tell us.
For this one release the engine can still be built from source against LuaJIT
(--features vm-luajit) to compare.
Pick your vector
Luau has a vector type of its own: three 32-bit floats carried inside the
value, costing nothing to make and nothing to collect. Floptle positions are
64-bit on purpose — a solar system needs the range — so which one a vec3 is
made of is now a project setting, Script vec3 in ⚙ Project Settings.
exact |
fast |
|
|---|---|---|
| components | 64-bit | 32-bit |
| can be changed in place | yes | no |
| allocates | one small object per vector | nothing |
| useful out to | the solar system | ~131 000 units from the origin |
Every project that exists today is exact, and stays that way until you
change it. New projects start fast. The methods, operators and constructor
forms are identical in both; the one thing you write differently is changing a
component:
v.x = 0 -- works in exact, raises in fast (naming the fix)
v = v:withX(0) -- works in both, and the better habit either way
floptle lint --vec3 lists every line a project would have to change to
switch, with the fix beside each. In fast, a script that builds a vector
past the precision limit is told so in the Console, once, by name — you will
not have to diagnose the jitter.
Measure it yourself
Three flags answer the question "where does the frame go" without a window:
floptle run --frames 600 --timing # p50 / p95 / p99 / worst step, in real ms
floptle run --frames 600 --alloc # how many KB of Lua heap a frame allocates
floptle shot --timing # what each render pass cost on the GPU
--alloc stops the collector across a window in the middle of the run, which
is the only way allocation can be measured — a per-frame reading of
collectgarbage("count") under-reports by a wide margin, because a collection
inside the frame eats part of what the frame allocated.
Upgrading
Nothing to do. The first time an existing project is opened in the editor, one
line is written into its project.ron — script_vec3: Some(Exact) — recording
the vector it has always had, so no later default can change it underneath a
shipped game. If a script of yours used goto, it will name the line.
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.