Releases / v0.100.0-rc1
v0.100.0-rc1 Keeps Up
2026-09-28 Engine + Hub Latest
Big levels get cheaper: nodes nobody is touching stop costing time every frame, walking characters on mesh levels cost a fraction of what they did, far-away enemies can be put to sleep, and hidden characters stop animating. A game that still can't keep up plays in slow motion instead of falling to a few frames a second, and says why. Games also get a render-resolution setting, every building casts its shadow, and scenes stop picking up paths that only work on your machine. This is a beta: switch the Hub to the beta channel to try it.
Changing a value every frame no longer costs the whole level
Setting a camera's fovY, a light's intensity, node.visible or a
post-processing value every frame used to make the engine re-read every node in
the scene for the scripts, several times a frame. The cost grew with the level,
and the profiler blamed no script. Now only the node that changed is re-read.
Writing a value a node already has costs nothing at all.
Adding, removing, renaming, reparenting or retagging a node still rebuilds the
scripts' copy of the scene. perf.counts().mirrorRebuilds says how often that
happens, and perf.mirrorCause() names the node and the change behind the last
one.
A game that can't keep up slows down instead of stalling
When the fixed update costs more than the time it simulates, catching up only
makes each frame longer, and a game could settle at 2 frames a second. It now
runs one update per frame instead, so it plays in slow motion at a frame rate you
can still see and debug. The Console says so once, with the three biggest costs.
perf.overloaded() lets a loading screen tell the player their machine is
struggling. A networked session keeps catching up, and so does a project with
tick_overload: CatchUp in project.ron.
floptle run --timing now breaks each step down by subsystem and by script,
counts the steps that ran over, and gives the node count.
Characters on mesh levels cost a fraction of what they did
A walking capsule on a level built from models used to check its collisions against every model whose bounding sphere it was inside, which for large buildings was most of them. It now skips models it can't reach and answers the rest faster. On one level, fifty-three awake enemies went from 16 ms of physics per step to under 4, with every collision exactly as before.
Put far-away enemies to sleep
function fixedUpdate(node, dt)
if (find("Player").worldPos - node.worldPos):length() > 80 then
node.vel = vec3()
node.asleep = true -- the physics body stops simulating
script.sleep{ wakeWithin = 70 } -- the engine stops calling this script
return
end
end
A sleeping script isn't called at all. It wakes when the camera comes within
wakeWithin metres, after seconds, when something collides with its node, or
when another script calls script.wake(node, "enemy"). script.setRate(10)
runs a script's fixedUpdate ten times a second instead of every tick.
Every script also got cheaper to run: the engine looks at each script file once per frame instead of once per node.
Hidden and off-screen characters stop animating
An Animation Controller now has a culling setting beside stepped. only
when visible skips a model that is hidden or outside the camera's view, and
catches it up when it comes back without firing the events it missed. reduced
when far also poses every third frame past 30 m. New controllers start at only
when visible; existing ones keep animating always. From a script,
anim:setCulling(mode) overrides it for one node and anim:setEnabled(false)
switches an animator off. perf.counts().animators and .animatorsCulled show
the effect.
Ask where a bone is
node:bonePos("Head"), node:boneRot("Head") and node:bones() read an
animated model's pose this frame. A ragdoll no longer needs an empty node
attached to every bone to find out where they are.
A render-resolution setting for weak GPUs
app.setRenderScale(0.67) renders the 3D scene at two-thirds of the window and
upscales it smoothly, while the game's UI stays at full resolution and sharp.
On a typical level that took 44% off the GPU time, far more than turning every
lighting feature down. render_scale sets the default in project.ron and in
Project Settings. It's separate from retro mode, which still decides the
resolution itself when it's on.
Every building casts its shadow
Only 16 collidable models could cast the soft distance-field shadow at once, and
which ones missed out depended on their file names. Copies of one model now
share a single bake, 32 can cast at once, and when there are more, the ones
nearest the camera get them. A model with casts shadows off no longer uses
the budget. shadow_volumes in project.ron lowers it for a game short on GPU.
Scenes keep paths that work on every machine
Picking a model from the Inspector's list saved the model's full path on your computer, so the scene loaded for you and was missing it everywhere else. Scenes and prefabs now always save paths inside the project as project-relative. A scene that already has full paths is fixed the next time you save it.
floptle check now fails on a full path in any project file, naming the file,
line, node and field, and floptle check --fix rewrites the ones inside the
project.
Upgrading
Nothing to do. To pick up animation culling on an existing controller, choose only when visible in its header.
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.