Releases / v0.82.0
v0.82.0 Way Out
2026-08-29 Engine Latest
Your main menu's Quit button works. So does a Video tab: frame pacing and internal resolution are things a script can read and change now, which they were not.
app.quit(), and the rest of a settings screen
Every game has a menu with Quit on it, and until now there was no way to
wire it — the button was either drawn dead or not drawn at all. There is a new
app table:
app.quit() |
end the game |
app.title() / app.version() |
for the top of a menu, and an about box |
app.vsync() / app.setVsync(mode) |
"On", "Adaptive" or "Off" |
app.retro() / app.setRetro(on) |
the retro presentation |
app.retroHeight() / app.setRetroHeight(px) |
the height it composites at |
app.retroIntegerScale() / app.setRetroIntegerScale(on) |
whole-number upscale, letterboxed |
What quit does depends on where the game is running, and they are honestly
different things. An exported build closes, with your save.* data flushed
first — somebody quitting from a settings menu expects the setting they just
changed to have been kept. The editor stops Play instead and says so, because an
editor that closed because a game under test called quit would take your
unsaved work with it.
The rest of an options menu was already here and is documented together for the
first time: audio through audio.track(name):setVolume(db), accessibility
through access.*, the picture through the Post Processing node's own
component, and rebindable controls through input.startRebind.
function start(node)
app.setVsync(save.get("vsync") or "On")
app.setRetroHeight(save.get("pixelHeight") or app.retroHeight())
end
A setting you change this way lasts for the session. These live in
project.ron, which is the file that ships to everybody who plays, so pressing
Stop puts it back — persist the player's choice yourself with save.*, the same
way access.* works. And a mode setVsync does not recognise is an error
rather than a shrug: a control that silently keeps the old value is a control
that appears to work.
Not here yet: window resolution, fullscreen and monitor choice. Those are one
question and it deserves answering properly. In a pixel-art game
app.setRetroHeight is the "resolution" a player means, and it was already a
live setting.
A broken project.ron says so
A project.ron the engine could not parse was quietly replaced with defaults.
One misplaced bracket and the project opened with no title, the wrong frame
pacing, none of its layers and none of its mixer — looking exactly like a
project nobody had configured, with nothing anywhere saying the file had even
been read.
It now names the file, the line and column, and says the project has opened with
default settings and that nothing has overwritten it. A project with no
project.ron at all stays quiet; that is a real state, not a fault.
The profiler stops blaming the wrong script
perf.scripts() times each script with a wall clock, so anything that happened
while your script was on the CPU — a garbage collection, the operating system
taking the core away — was reported as that script's cost. Scripts with no
update at all were being listed with 12–21 ms peaks, which sends you to
optimise a file that did nothing.
A script with no hook for a pass is no longer listed. A stall can still land on
one that is running — that is what sampling a wall clock means — so
docs/scripting.md now says which number to act on and what makes a worstMs
spike.
While looking at that: if the collector is what you are chasing, the biggest
source of it in most games is vectors. Every vec3 a script builds is an
object, so a + b, v:normalized() and node.pos each allocate, while scalar
reads like node.x and v:length() allocate nothing at all. A hot loop written
in components rather than vectors produces no garbage, and that is usually the
largest win available to a game script today.
Upgrading
Nothing to do.
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.