Releases / v0.117.0-rc2

v0.117.0-rc2 Whole Worlds

2026-10-20 Latest

Planets several times the size generate in seconds, with mountain ranges, rivers, lakes and climates. Wind and lightning, props that keep their own look, craft that articulate, and meshes and textures a script builds itself.

Bigger planets, made in seconds

A generated planet now costs its surface, not its volume. A 400 m world that took nearly three minutes fills in about four seconds and keeps a third of the memory. deferCaves = true leaves the buried cave zone until a dig reaches it, so a world fills and keeps about a quarter less; with it, a 1.5 km world fills in under a minute on a 16-core desktop. Two players' games agree on every cave either way.

terrain.generatePlanet takes new layers: ridged mountain ranges, warped and terraced ground, plateaus, overhangs and arches, steep-walled ravines, and rivers that run downhill in valleys they carve. biomes paints the surface by latitude, altitude, slope, moisture and nearness to a river. terrain.planetRivers(opts) and terrain.planetLakes(opts) say where the rivers run and where lakes would settle, without generating anything. docs/planet-generator.md has five example worlds to start from.

Ground that looks the way it collides

Terrain within 64 m of the camera is always drawn at full detail, and nothing within 120 m is drawn coarser than half detail, so mountains no longer appear only once you walk into them and pits are always drawn. Coarse ground far away settles onto the real surface instead of floating metres above it. Undug ground shows its own texture, not the subsoil under it.

A dig no longer stalls the frame: collision is rebuilt in the background and the old ground holds until the new one lands. A ray past a planet costs about what a ray into it does, so a script can cast several a frame.

Wind and lightning

weather.addWind{ velocity = vec3(4, 0, 0), gust = 0.4, turbulence = 0.2 } declares a wind, and weather.windAt(point) reads it back. A wind given a parent turns with its planet. One given a center and radius is a local cell, a storm or a valley's down-draught, that adds to the winds under it. Gusts travel downwind, so one is seen crossing a field instead of everything moving at once.

weather.bolt(from, to) gives a lightning bolt as strands to draw, a jagged channel with forks. The same seed gives the same bolt on every machine.

Scattered props look and behave like the model

Props now draw from their model's own colour, maps and glow, and a source's tint and emissive shade them instead of replacing them, so one model serves a field of any colour. A source on a planet lands on the right face of it. Instance ids survive a round trip through Lua, so scatter.remove takes the prop scatter.near found. Props settle a few at a time, so walking into a field is smooth, and a script's sources are freed when it is hot-reloaded or its node is destroyed. "Smooth + mipmaps" on a model's textures now builds the mip chain, so far textures stop shimmering.

Craft that articulate

Either end of a joint can be an assembly, so a vessel built from several assemblies moves: hinges, pistons, rotors, robotic arms. A hinge or slider motor can drive to a goal, set from a script each tick with Joint.goal. A wheel hung from an assembly grips the ground and parks on a slope. assembly.grip welds a craft to the ground under it, or to another body.

A part's mass is readable and writable from Lua, and the assembly re-weighs itself on the next tick. A parked assembly falls asleep and costs nothing, and a fast one stops at the ground instead of tunnelling in and being thrown out.

Meshes and textures from a script

mesh.build{ positions, indices [, normals, uvs, colors] } builds a mesh and answers a name that goes anywhere a model path does, node.model and scatter.create included. assets.textureFromPixels(w, h, pixels) makes a texture from raw RGBA. anim:setBones poses many bones in one call, and node.name can be assigned.

Materials and sky

A glTF model's metallic-roughness, normal, occlusion and emissive textures come in with it, and draw exactly as the same maps set by hand. Materials take an emissive map. Surface shaders get litPbr, the built-in Physical lighting, and the derivatives dpdx, dpdy and fwidth. Shots, captures and docked Game views draw a material's maps and shading, where before they drew it flat.

A model's textures are filtered the way its file says: a texture exported with nearest filtering keeps crisp pixels, and one exported with linear filtering draws smooth, with mipmaps. A model's embedded texture filter in the Inspector still chooses for the whole model.

On a round world with air, flat fog fills only the view inside the air, so the sun and other planets keep their colour. The sun low in thick air turns orange instead of fading, and a world's cloud deck can be tinted, darkened and raised from setCelestial.

Smaller things

  • app.monitors(), app.displayModes() and app.setDisplayMode{...} let a game offer resolution and monitor settings, and ui.engineSettings(container, "controls") builds the engine's rebinding page into a game's own panel.
  • save.set keeps tables nested 16 deep.
  • Renaming, retagging or reparenting a node no longer rebuilds the scripts' copy of the whole scene.
  • A sound or shader under a linked project folder resolves inside the project.
  • Mixer changes hold across scene.load; audio.tracks() lists the mixer.
  • perf.buckets() names gpuWait, present and assets, and what is left is untimed.
  • In a flat 2D view, a click selects what is drawn under the cursor, not the tilemap behind it.
  • A package's scene.docs and mesh.read are answered the next frame, even when it queued nothing else, and xpcall(f, debug.traceback) catches an error in a package with the stack that raised it.

The Hub

The Hub now counts installs with an anonymous ping once a day: a random install id made on your machine, the Hub's version, your OS and CPU type, and its update channel. Nothing that identifies you is sent. The Hub tells you before it sends anything, and Settings ▸ Install ping turns it off and deletes the id. FOPULL_NO_PING=1 turns it off for every Fopull app. The editor never pings.

Upgrading

Nothing to do. Five things look or behave differently:

  • A glTF part with a metallic-roughness texture now shades as Physical. Parts that name only a base colour draw as before.
  • A model whose file says its textures are filtered linearly now draws them smooth, where before every model drew crisp. Blender exports linear unless an image's interpolation is set to Closest. To keep a model crisp, set its embedded texture filter to crisp.
  • On a round world with air, flat fog no longer covers things out in space.
  • perf.ms("other") still answers the time no bucket measured; the bucket is now named untimed.
  • Joints get twice the solver passes, so they hold stiffer.

Floptle Hub

Linux x86_64 10.7 MB Download → sha256 68d500d71f5c6be94c408015dc50ccb286281764352962d57aa1e56fff5913cd
macOS Apple Silicon 7.4 MB Download → sha256 84bbe6b6756f425d1cb1e7a1b092c422fb73c9aded54036fe9dd8cda94f29ddc
macOS Intel 7.9 MB Download → sha256 7605aa36999fa89da787140b99c307c19886a096090aa23718ffbd18ff732cf3
Windows x86_64 9.1 MB Download → sha256 c59bd952e4ec2f4fea5a16f4edffa44e7c195cebf90b115164b30e74076045d4

Engine

Linux x86_64 55.9 MB Download → sha256 3099c5c42a0ab1d648a98402124c695fd707a442bcc23f98b9bd5e20035f1b7f
macOS Apple Silicon 43.7 MB Download → sha256 f44ad07c1416ca46ffe8f44859f729de07ba1b427faad10c9257b1813e852588
macOS Intel 47.0 MB Download → sha256 225f7f9bf51e7aa0233c84e18a2a0e01d58b7bb30201a0a44d2da8a34ef29530
web 5.2 MB Download → sha256 b1c24d44f2c9e7b2fafb0e4d2479ab4383c38606b790676dc2d114aba3f19b71
Windows x86_64 53.0 MB Download → sha256 41ebd790b5be57edf69173b747eacc142600283889d54403d7634a6a44ab5ee6

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.