Introduces deterministic simulation: give a system a seed and it reproduces its output exactly — the same seed produces the same result, on any machine — unlocking reproducible previews, thumbnails, tests and replays. The public API is backward-compatible (everything new is additive), but reproducibility and output behaviour changed, so this is a major release. Most consumers need no code changes; see the Determinism & seeding docs.
Breaking
- The engine no longer draws from the global
Math.random. Each system advances its own isolated, seeded stream. If you seededMath.randomglobally to make particle output reproducible, that no longer has any effect — usesystem.setSeed(seed)instead. Unseeded systems still vary run to run as before. - Default particle arrangements differ from 12.x. The same system renders a different (still random) arrangement because the random source changed. Re-generate any visual/snapshot baselines you keep.
- Particle
idformat changed from a UUID to a deterministicparticle-<emitterSeed>-<spawnIndex>. Ids remain unique and opaque; only code that parsed the old format is affected.
Added
System.setSeed(seed)— sets the root of a seed hierarchy (system → emitter → particle) so the whole simulation is reproducible from the seed (also accepted as the thirdSystemconstructor argument). Drive with a fixed number ofupdate()calls for byte-identical output.System.tick(realDeltaSeconds)— fixed-timestep real-time driver: advances the sim by real elapsed time in fixed steps, so playback speed is correct on any refresh rate (callingupdate()once per frame runs ~2× too fast on a 120Hz display). Stall catch-up is clamped; tunable viasystem.fixedTimeStepandsystem.maxSubSteps.update(dt)remains the single-fixed-step primitive.
Fixed
GPURendererparticles stopped rendering after heavy churn — buffer slots were never released, so with deterministic per-spawn ids the slot space grew past the buffer's capacity and silently dropped particles. Slots are now recycled on particle death.