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:

sh
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:

lua
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

Linux x86_64 7.5 MB Download → sha256 5efc25a2efce1d0246d597202f1c2f59976d04961cb01f831870ba417a1a0ec1
macOS Apple Silicon 5.7 MB Download → sha256 a0cc549967bc112c9f42ee65c04b4d0bda300a420409d86195c04aa139baa33c
macOS Intel 6.2 MB Download → sha256 583040d093ace3af5799dccf0952aa89b654e15392579f5e5b2aa2d2f2f697e2
Windows x86_64 7.1 MB Download → sha256 c9ff967ceb69d6667f33a102cb45722db18ffef70f4d5a6bc6abedc9eedffed4

Engine

Linux x86_64 21.0 MB Download → sha256 d28ac173055441032e760425f2768ea165cdbd254dc1e561ad3e2872ac839f8c
macOS Apple Silicon 16.9 MB Download → sha256 191e90a929a2787211a907c518cddbef1f58c1ec9eec2dd9fdc5d33b654c881a
macOS Intel 18.3 MB Download → sha256 7320539feac0217309c492c3ee177d75a7412ecec2580bca5ade6d69d2f8377e
Windows x86_64 20.7 MB Download → sha256 44b8b151b082f22350a0b81fad728325b4af8b6f887957dc0c667f0a953f2598

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.