Skip to content

v0.4.0 — Mobile browser, native-engine parity, Ball Flood

Choose a tag to compare

@Stink-O Stink-O released this 30 Jul 01:16
· 37 commits to main since this release

The demo runs properly on phones, the two native engines finally match Box3D on the samples they were quietly getting wrong, and there is a new stress sample that pours unlimited balls into a glass tank.

The browser demo lives on itch.io

Play it on itch.io — the multi-threaded build, full-size scenes, no download. Desktop, Android and iOS all run that build there.

An earlier release claimed Safari could not run the threaded build and was bounced to a fallback on GitHub Pages. That was wrong, and the Pages deployment is retired — it is stale and receives no updates. itch is the only page that is kept current. The threaded build's isolation guard still exists, but as a readable notice for genuinely un-isolated hosts, not as a Safari path.

Mobile

The browser demo was close to unusable on a phone. All of this is gated behind touch/mobile feature detection, so desktop behaviour is untouched:

  • Multitouch no longer flails the camera. Touches are tracked per finger index instead of through Godot's emulated mouse, which had been hopping between fingers. A joystick finger's drags can no longer register as a phantom pinch.
  • Landscape only. Portrait did not merely look wrong, it failed to load on iOS; the page now prompts to rotate.
  • The UI fits. Panels sit on their own layer above the touch controls, are opaque, and scroll by drag — the settings menu was previously cut off with no way to reach the rest.
  • Async physics step no longer softlocks the tab. It is now refused on every wasm build, threaded included, and the sidebar hides the checkbox when the build refuses it.
  • The Reset button's ⟲ rendered as a tofu box on web, where there are no system fonts to fall back to.

Godot Physics and Jolt: parity fixes

The engine selector rebuilds each sample with native bodies. Several samples were rebuilding into something quietly different from what Box3D shows:

  • Multicoloured meshes were flat grey. Materials chosen in _ready do not exist on the unparented instance the rig walks; bodies now publish that choice explicitly.
  • The bomb did nothing. There is now a native twin with the same fuse, blink and blast.
  • The Huge Pyramid did not appear at all — its 16,290 blocks are generated in code, which the rig had no way to see. On Jolt the top half then froze in mid-air: bodies past Jolt's default 10,240 cap are silently never created, so the caps are raised in project.godot.
  • The marble emitter spawned nothing, and the gyroscopic precession tops did not exist.
  • The gyroscopic torque handle did not spin. Its spin is imparted in _ready, so it never reached the rig; the handle floated motionless in the sample's zero gravity.

That last one comes with a measured caveat now shown in the sample's port notes: with the spin restored, both native servers hold the handle's spin axis on a smooth drift for a full minute where Box3D swings it through repeated inversions. Neither integrates the gyroscopic term, so the Dzhanibekov flip — the reason the sample exists — cannot appear there, and a steadily spinning handle would otherwise read as the sample working.

Ball Flood

A new stress sample: three uncapped emitters pour immortal balls into a glass tank, with an on-screen body counter and a Pause Emitters button. It is a rendering benchmark as much as a solver one — per-ball MeshInstance3Ds died at 45,000+ draw calls and 21 fps while the solver read 0.00 ms. Every emitter ball now draws through one shared MultiMesh: 25,202 bodies at 81 draw calls.

The body counter is opt-in from Settings, drags anywhere, and remembers both its position and whether it is shown.

Marble Run was rebuilt around the same flood, with node scales baked into the shapes — Box3D shape frames never read node scale, so a scaled node moved the mesh and left the collider behind.

Also

  • Emitter spawns are spread over a disc. Perfectly aligned overlapping spawns extrude upward as a growing column on Godot Physics, which resolves an axis-aligned sphere stack purely vertically.
  • The engine badge no longer reads "(unverified)" indefinitely — the behavioural probe retries until it can actually conclude.
  • Social card templates for the GitHub preview and the itch cover live in godot/tools/social_cards/.

Assets

Drop the library for your platform into godot/demo/bin/ next to box3d.gdextension, then open godot/demo/project.godot in Godot 4.7. template_debug is the one to use with the editor; template_release is for exported games.

file what it is
libbox3d_godot.linux.template_{debug,release}.x86_64.so Linux
libbox3d_godot.windows.template_{debug,release}.x86_64.dll Windows — cross-built with MinGW, never run on Windows
libbox3d_godot.android.template_{debug,release}.{arm64,x86_64}.so Android
libbox3d_godot.web.template_release.wasm32.wasm Web, threaded
libbox3d_godot.web.template_release.wasm32.nothreads.wasm Web, single-threaded
box3d-demo-android.apk the demo as an installable app, debug-signed, arm64 + x86_64
box3d-demo-web-threaded.zip the demo for itch.io or any host sending real COOP/COEP headers
box3d-demo-web.zip the demo for a plain static host — no special headers, works anywhere

Pick the web zip by what your host can do, not by which sounds faster: a threaded wasm module declares shared memory and cannot load without cross-origin isolation. On itch, enable the SharedArrayBuffer option and use the threaded zip.

Caveats that have not changed

  • The Windows DLLs are cross-compiled from Linux with MinGW-w64 and have not been run on Windows. They import only KERNEL32.dll and msvcrt.dll, and -ffp-contract=off is preserved, but treat them as untested.
  • Determinism on wasm is unverified — upstream's Emscripten CI is build-only — so the browser builds are not a reference for behaviour.
  • The browser build is a preview. WebAssembly costs something over native and it renders through Compatibility (WebGL2) because Godot 4.7 has no WebGPU. Judge performance from a desktop run.