v19.5.0
What's New in melonJS 19.5.0
A physics-focused release. The headline is the new PhysicsAdapter abstraction — the same game code can now run on either the engine's built-in SAT physics (default, unchanged behavior) or on a third-party rigid-body engine via the physic Application setting. Two official adapters ship alongside the engine: @melonjs/matter-adapter (wraps matter-js) and @melonjs/planck-adapter (faithful Box2D 2.3.0 port via planck.js). Collision dispatch gains a proper lifecycle with a receiver-symmetric contract, spatial queries (raycast / queryAABB) become portable across every adapter, and a new procedural audio surface lets you fire envelope-shaped tones and noise without sample assets.
New Features
PhysicsAdapterinterface — pluggable rigid-body abstraction. The newBuiltinAdapterwraps the existing SAT detector +Bodyclass as the default; third-party adapters plug in by passing an instance toApplication'sphysicsetting (new Application(w, h, { physic: new MatterAdapter() })). The contract covers body lifecycle, kinematics, collision filtering, queries, and acapabilitiesflag set so portable code can branch on what the active engine supports.@melonjs/matter-adapter— first official third-party adapter, packaged separately. Wraps matter-js'sEngineand translates thePhysicsAdaptercontract to matter's API. Constraints (springs/hinges/pins), sleeping bodies, continuous collision detection, raycasts, native rotational dynamics. Reach the engine viaadapter.enginefor matter-specific extensions.@melonjs/planck-adapter— second official adapter. Faithful Box2D 2.3.0 port via planck.js. Native joints, CCDbulletflag, sleeping bodies, native raycasts, per-body gravity scale. Reach the engine viaadapter.world.createJoint(...). Showcased by the new Neon Plinko example.- Portable spatial queries —
adapter.raycast(from, to)andadapter.queryAABB(rect)are now portable across every adapter with the same return shapes (RaycastHit = { renderable, point, normal, fraction },queryAABB → Renderable[]).raycastcomputes precise entry-point geometry per shape (line-segment vs polygon edges, quadratic ray vs ellipse), with the normal flipped to point back toward the ray origin per Box2D / planck convention. - Collision lifecycle hooks on every
Renderable—onCollisionStart/onCollisionActive/onCollisionEnd. Dispatched consistently by all three adapters with a receiver-symmetric contract (response.a === this,response.b === other,response.normalis the MTV of the receiver). The portable stomp idiom isresponse.normal.y < -0.7— same expression on every adapter. LegacyonCollisionis preserved unchanged for backward compatibility but superseded byonCollisionActiveon a per-renderable basis. - Procedural audio primitives —
me.audio.tone({ freq, attack, decay, gain, wave, filter, pitchSlide })andme.audio.noise({ type: "white" | "pink" | "brown", duration, gain, filter, filterSweep }). Envelope-shaped oscillators and band/lowpass/highpass-filterable noise, perfect for SFX without sample assets. Publicme.audio.getAudioContext()/me.audio.getMasterGain()accessors let custom WebAudio graphs mix with the engine's master volume / mute. - Three new showcase examples —
Matter Platformer(the canonical platformer ported to matter),Pool (Matter)(top-down 8-ball pool, drag-to-aim cue with physics-driven break shots and pocket sensors), andNeon Plinko (Planck)(ball-ball collision + procedural WebAudio peg clack / pentatonic chime / row-pitch arpeggio with stereo pan). BodyDefinition— declarative body description set viarenderable.bodyDefand auto-registered byContainer.addChild. Maps portable fields (type,shapes,collisionType,collisionMask,frictionAir,restitution,density,gravityScale,maxVelocity,isSensor) onto whichever adapter is active.
Changed
onCollisionActivesupersedes legacyonCollision— definingonCollisionActiveon a renderable suppresses the legacy every-frameonCollisiondispatch on that same renderable. Per-side, so renderable A can be on the modern handler while renderable B is on the legacy one in the same pair; each gets the contract it asked for.- Built-in push-out semantics matter-aligned — dynamic non-sensor bodies separate by default whether or not
onCollisionis defined. The pre-19.5 "noonCollisiondefined ⇒ implicit sensor" quirk is gone; mark a body as a sensor explicitly viabodyDef.isSensor: trueorbody.setSensor(true)to opt out of push-out.
Bug Fixes
Container.removeChildNowevicts from the QuadTree broadphase — pool-recycled entities frequently take theremoveChildNow(child, true)path; previously the broadphase retained a stale reference until the next full rebuild. Eviction is now eager.QuadTreesubtree-count + scratch reuse + collapse — the broadphase now tracks accurate sub-tree counts (was off after a collapse), reuses a single per-frame scratch array forretrievecalls, and collapses leaf-only subtrees afterremoveContainer. Combined with re-entrancy-safe public APIs, callingqueryAABB/raycastfrom inside anonCollisionStarthandler no longer clobbers the outer detector iteration.CanvasRenderTarget.destroysource-keyed lookup —destroy()was using the target's own canvas as the cache key instead of the source; targets created viaCanvas.from(source)survived their explicit destroy and leaked until GC.OffscreenCanvasnon-destructive texture upload — uploading anOffscreenCanvas-backed texture no longer resets its render context, so subsequent draws to the same surface keep working.debug-pluginheap formatting — the heap-usage line in the debug panel formats correctly across browsers that report bytes differently.
Install
npm install melonjs@19.5.0
Optional new adapter packages:
npm install @melonjs/matter-adapter
npm install @melonjs/planck-adapter