Skip to content

v19.2.0

Choose a tag to compare

@github-actions github-actions released this 29 Apr 11:13
· 113 commits to master since this release
9493187

What's New in melonJS 19.2.0

New Features

  • state.freeze(duration, music?) + Application.pause/resume/freeze — hit-stop / hit-pause primitive that pauses the stage for a fixed duration then auto-resumes. Returns a Promise<void> that resolves on unfreeze. Reentrant calls extend (don't stack); window blur cancels the freeze. Convenience proxies on Application for the same.
  • Multi-pass post-effect chainingRenderable.postEffects array with addPostEffect() / getPostEffect() / removePostEffect() / clearPostEffects(). Effects run in sequence via FBO ping-pong; single-effect renderables use a zero-overhead fast path.
  • FBO-based camera post-processing pipeline — assign a ShaderEffect to any camera's shader property for full-screen post-effects. Multi-camera independent (e.g. main + minimap with different effects).
  • Camera effects system — extensible CameraEffect base class with lifecycle. Extracted ShakeEffect, unified FadeEffect (in/out direction), and new MaskEffect for shape-based level transitions (Ellipse / Polygon shrink/reveal).
  • Camera2d.colorMatrix — built-in ColorMatrix for color grading (brightness / contrast / saturate / hueRotate / sepia / invertColors). Always applied as the final post-pass; zero overhead when identity.
  • ColorMatrix class + ColorMatrixEffect — chainable color adjustments. SepiaEffect, InvertEffect, DesaturateEffect now share a single GLSL shader via the matrix.
  • VignetteEffect — built-in shader effect to darken screen edges with configurable strength and size.
  • RenderTarget abstract base (designed for future WebGPU support) with concrete WebGLRenderTarget (FBO) and CanvasRenderTarget (canvas) implementations. New RenderTargetPool replaces the WebGL-specific FBO pool.
  • ParticleEmitter.autoDestroyOnComplete (default false) — emitter removes itself from its parent once all particles have died. Solves the leak in fire-and-forget burstParticles() use cases. Companion onComplete callback fires on completion.
  • ParticleEmitter.accurateBounds (default false) — opt-in per-frame bounds refresh for debug visualization or collision. Default off saves significant matrix work at high particle counts.
  • ParticleEmitterSettings is now an exported TypeScript interface — constructor and reset() accept Partial<ParticleEmitterSettings> for compile-time validation.
  • Text.visibleCharacters / visibleRatio — progressive text reveal / typewriter effects on Text and BitmapText. Animate visibleRatio with a Tween for character-by-character display.
  • Tween.repeatDelay(ms) — adds a delay before each repeat cycle.
  • Renderer-agnostic state methodssetViewport(), clearRenderTarget(), enableScissor(), disableScissor(), setBlendEnabled() (no-ops on Canvas, real on WebGL). Eliminates direct GL calls from the post-effect pipeline.
  • Trigger.transition — accepts "fade" (default) or "mask" for shape-based level transitions; new color setting replaces legacy fade property (backward compatible).
  • Matrix3d.transform() — accepts either 16 values (full 4x4) or 6 values (2D affine, promoted to 4x4). Mirrors the Matrix2d.transform() API.

Changed

  • Renderable.shader is now a backward-compatible getter/setter for postEffects[0] (deprecated in favour of addPostEffect() / clearPostEffects()).
  • Camera.shake() / fadeIn() / fadeOut() are now convenience wrappers that create ShakeEffect / FadeEffect instances — same signatures, fully backward compatible. Multiple shakes can coexist.
  • Trigger internally uses FadeEffect / MaskEffect instead of viewport.fadeIn() / viewport.fadeOut().
  • ParticleEmitter.reset() now clamps reversed range pairs (minLife > maxLife, min/maxStartScale, min/maxEndScale, min/maxRotation) by lowering min to max. Catches the common footgun of overriding only one half of a range.
  • Particle hot-path performance — closed-form transform construction folded into a single setTransform() call, _halfW / _halfH cached on reset, _deltaInv cached on the emitter, frame-skip bookkeeping gated behind framesToSkip > 0. ~240k matrix ops/sec saved at 1000 particles, 60fps.

Bug Fixes

  • WebGL1 stencil masking (setMask / MaskEffect) now works correctly in preferWebGL1: true mode. WebGLRenderTarget was relying on gl.DEPTH_STENCIL / gl.DEPTH_STENCIL_ATTACHMENT being exposed on the WebGL1 context, which some browser/driver combinations leave undefined. Now uses spec-defined numeric fallbacks (0x84F9 / 0x821A) and validates completeness via gl.checkFramebufferStatus() with a depth-only fallback path.
  • ImageLayer.mask, shader / postEffects, flipX() / flipY() now apply correctly. ImageLayer.preDraw() was a stripped-down copy of Renderable.preDraw() that handled only alpha / tint / blend mode. Coordinate-sensitive setup (flip, stencil mask) is now applied in draw() after the per-camera zoom transforms, so it stays correctly aligned at any viewport.zoom and across multi-camera setups.
  • state.freeze() no longer leaves the game in inconsistent states when interacting with manual pause/resume or window blur:
    • The freeze timer's auto-resume now respects whether the game was already paused when freeze started — won't unpause a manually-paused game on expiry
    • Calling state.resume() or state.stop() mid-freeze cancels the timer and resolves the freeze promise immediately
    • Window BLUR cancels the freeze (the visual "moment" is over by the time the user returns; regular pauseOnBlur still keeps the game paused while away)
  • ParticleEmitter constructor was using bitwise | instead of logical || for the width/height fallback, silently rounding any provided value to the next odd number (e.g. width: 16 → 17, width: 32 → 33).
  • Particle hitbox now tracks the visual — anchor + transform + bounds are kept consistent (rotation pivots on the visual center, bounds match the visual extent). Previously the hitbox was offset by (w/2, h/2) and lagged one frame behind the visual.
  • Canvas setMask(shape, true) now uses evenodd clipping for proper inverted mask support (was using destination-atop composite which didn't clip subsequent draws).
  • Ellipse clone() now uses the ellipse pool — consistent with Polygon.clone().
  • WebGL WebGLRenderTarget constructor and resize() now explicitly use TEXTURE0 to avoid corrupting the multi-texture batcher's texture unit bindings.
  • WebGL post-effect pipeline now explicitly sets viewport on every FBO bind and saves/restores the projection matrix, fixing rendering issues after canvas resize.

Install

npm install melonjs@19.2.0