Releases / v0.84.2
v0.84.2
2026-09-03 Engine Latest
A performance patch, and it is a large one. A scripted node cost the engine about 4.5 KB of memory a frame before your script did anything at all, and a body standing on a mesh floor cost far more than it should have. Both are fixed, and the profiler that should have shown you the first one now does.
On the first-person game these numbers come from, a frame went from 6.4–7.6 ms to 3.5–3.7 ms, and the memory its scripts churn through went from 478 KB a frame to 48 KB. Nothing to do on upgrade — open your project and it is faster.
Your scripts were being blamed for the engine's memory
floptle run --alloc reports how much memory a frame's scripts make. On that
game it said 478 KB a frame, and the biggest single line was a pickup script
running on 34 items. Emptying that script's update completely changed the
figure by 4%.
The memory was the engine's. Every scripted node has a node table that the
engine refreshes before each hook and reads back after it, and the read-back was
going the slow way around: about 96 bytes for every field, ten fields, twice a
pass, three passes a frame, on every scripted node in the scene. A node with an
update that does nothing at all was costing 4,522 bytes a frame.
It now costs 375.
Nothing about how you write a script changes. If you have been watching
--alloc and wondering why the numbers were so much larger than the tables your
code builds, this is why, and the number you see now is much closer to yours:
floptle run --frames 600 --alloc --seed 7
Standing on a mesh floor got cheap
Collision against an imported mesh — a level, a terrain, any model you gave a mesh collider — asks the mesh "how far is this point from your surface". That question was searching a fixed block of 125 cells every time it was asked, however close the answer turned out to be, and a body is asking it dozens of times per tick. On the same game, seven moving bodies against a level's meshes were most of the physics budget.
The search now stops when it can prove nothing else is closer, and never looks outside the mesh's own extent. Same answers, down to which triangle — a replay still replays identically — for about a fifth of the work.
The frame profiler adds up now
The ⏱ panel and perf.ms("scripts") used to time only the hooks themselves.
Everything the engine did to reach a hook — the setup per node, the reference
params, the scene sync — was in no bucket at all, so a heavily scripted frame
could report 5.3 ms of a 12.9 ms step and leave you looking in the wrong place.
That is how the memory above stayed hidden for a release.
scripts is now the whole pass, and the scene sync has a bucket of its own:
perf.enable(true)
print(perf.ms("scripts")) -- the whole pass
print(perf.ms("mirror")) -- the ECS-to-Lua sync inside it, counted once
for _, s in ipairs(perf.scripts()) do
print(s.name, s.ms) -- hook time: part of "scripts", not all of it
end
perf.buckets() gains mirror, and the panel says which of its numbers is
which. On a scene of sixty scripted nodes the buckets now account for 99% of the
step where they accounted for 34%.
If you have an assertion like perf.ms("scripts") < 4 in a script, it is now
measuring more than it used to and may need a larger number. That is the point:
it was measuring a fraction of the pass.
Also in this release
- The two design records the engine's own source comments point at — the
command-line design, and the decision to move to one script VM and a
WebGPU-only browser target — are published at
docs/adr/, so following a citation lands somewhere. - The web export page now marks plainly which of its steps shipped in 0.84.0 and which are still plan.
Upgrading
Nothing to do. No project file changes, no script changes, no settings moved.
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.