Skip to content

Releases: melonjs/melonJS

v19.4.0

12 May 09:27
a6b8c21

Choose a tag to compare

What's New in melonJS 19.4.0

New Features

  • GPU-accelerated WebGL 2 tile rendering for orthogonal TMX maps — each visible layer now renders as a single quad whose fragment shader walks the per-layer GID index texture and samples the tileset atlas, with no per-tile draw loop. Supports animated tiles, flip bits (H/V/AD), per-layer opacity/tint, per-layer blend mode, and oversized bottom-aligned tiles. Enabled by default via Application.settings.gpuTilemap; falls back transparently to the legacy CPU renderer on isometric/staggered/hexagonal layers, collection-of-image tilesets, non-zero tileoffset, or non-WebGL-2 contexts. Rough win on a mid-tier mobile GPU with a 3-layer 800×600 viewport: ~2–4 ms down to ~0.3–0.8 ms per frame; up to ~5–8× on dense large maps; effectively free on desktop GPUs.
  • GLSL ES 3.00 custom shadersGLShader now accepts both vertex and fragment source written with #version 300 es. The precision injector and attribute extractor handle both 1.00 and 3.00. ShaderEffect remains 1.00-only because WebGL requires both stages of a program to share a version, and it pairs the user's fragment with the built-in 1.00 quad vertex shader.
  • TextureResource / BufferTextureResource — a renderer-agnostic source for textures synthesized from raw byte buffers rather than loaded from an image. Flows through the standard TextureCache and batcher path. Supports rgba8 and rgba8ui (WebGL 2) formats. Used internally by the GPU TMX renderer.

Changed

  • WebGL 1 NPOT warning is now scoped — removed the unconditional [Texture] ... is not a POT texture warning. The engine handles NPOT correctly (clamp wrap, non-mipmapped filters). A targeted warning now fires only when repeat: "repeat*" is requested on an NPOT texture under WebGL 1, the one case where the user's intent is silently downgraded.
  • throttle(fn, wait) is now generic over its argument tuple. throttle<T extends unknown[]>((...args: T) => void, wait) preserves the wrapped function's parameter types.

Bug Fixes

  • WebGL: MaterialBatcher.uploadTexture POT check used destination quad size — the w and h parameters (the destination quad size, not the texture's) drove the isPOT check, which gates both the wrap-mode fallback and generateMipmap. Visible as a GL_INVALID_OPERATION from gl.generateMipmap on WebGL 1; silent wasted work on WebGL 2. Texture dimensions are now derived from the source itself.
  • SAT: ellipse collisions silently failed inside a centered leveltestEllipseEllipse and testPolygonEllipse built the relative-position vector by adding a.ancestor.getAbsolutePosition() where they should have subtracted it, shifting the circle by 2 * ancestor.absPos. Manifests when level.load auto-centers the level container on a wider viewport, breaking every circle-vs-anything collision. The polygon/polygon path is unaffected — it builds two absolute positions and lets isSeparatingAxis do the subtraction. Latent because every existing SAT unit test wired the mock ancestor to (0, 0), where the sign error is arithmetically invisible.
  • TMX: static children kept stale absolute bounds after the level was centeredTMXTileMap.addTo sets container.pos after adding children, so each child's cached absolute bounds (computed at addChild time) didn't include the centering offset. Children that moved on their own refreshed via the pos observer, but TMX layers, Tiled collision shapes, triggers, and decorative sprites stayed stuck at their pre-centering bounds — visible as debug overlay shapes drawn at the wrong screen position, and as broken viewport culling for anything outside the pre-centering box. _setBounds now walks the container subtree and refreshes absolute bounds after the position actually moves (both initial load and viewport resize).
  • ImageLayer: repeat-x / repeat-y / no-repeat produced different output on Canvas vs WebGL (closes #1290) — ImageLayer.draw asked the renderer to fill viewport.width * 2 × viewport.height * 2 regardless of repeat mode, then leaned on each renderer's overflow behavior on the non-tiling axis. Canvas left the overflow transparent (HTML spec); WebGL stretched the bottom row / right column via GL_CLAMP_TO_EDGE. The draw extent is now clamped to the source dimensions on any axis that isn't tiling, so neither renderer enters its overflow path and both produce the same strip-shaped output. Matches Pixi's TilingSprite mental model — the tile rectangle is the tile rectangle.

Performance

  • Uint16Array-backed TMX layer data — TMX tile layers now back layerData with a flat Uint16Array and the orientation renderers read directly from it, with no Tile allocations during map parse or per-frame rendering. Per-layer memory drops ~25× (40 KB vs ~1 MB on a 100×100 layer); modest FPS gain on Canvas (~2–5% in tile-heavy scenes). Public API is unchanged.
  • Engine-wide uniform value cache — every shader the engine builds (sprite batchers, light effects, post-effect chains, the TMX GPU renderer, user-authored GLShader / ShaderEffect) now caches the last value sent for each uniform and skips redundant gl.uniform* calls. Vec/mat values compare element-wise so a reused scratch Float32Array is detected correctly. Biggest beneficiaries are the per-frame projection-matrix upload (now skipped after the first frame) and the TMX GPU renderer's layer-lifetime constants. Modest on its own (~0.1–0.5 ms saved per frame on mid-tier mobile), more in scenes with many custom shaders or post-effect chains, but stacks cleanly with every other rendering win.
  • TMX GPU renderer fragment-shader fast path — branches on uOverflow == (0, 0) and uses a single-cell fast path for tilesets whose tiles fit the cell exactly (the common case), skipping the worst-case 25-iteration candidate-cell loop entirely. The slow path (oversized bottom-aligned tiles) is unchanged. Roughly 10–25% fragment-shader cost reduction for the common case (~0.05–0.2 ms per frame on mid-tier mobile, lost in the noise on desktop GPUs); the win compounds with viewport size since fragment work scales with pixel count.

Install

npm install [email protected]

v19.3.0

08 May 08:26
58a0050

Choose a tag to compare

What's New in melonJS 19.3.0

New Features

  • Normal-map sprite lighting (closes #1416) — Sprite.normalMap accepts a paired normal-map image (or auto-detected from TextureAtlas({ normalMap })). Sprites with a normal map render through a dedicated LitQuadBatcher whose fragment shader runs a Lambertian light loop over the active Light2d instances — up to 8 concurrent lights, configurable Stage.ambientLightingColor, quadratic attenuation. The lit batcher only kicks in when both normalMap and active lights are present, so unlit scenes pay zero overhead.
  • Light2d is now a first-class world Renderable — add via app.world.addChild(light) (or any container, so the light follows parent transforms). Auto-registers with the active stage's lighting set via onActivateEvent / onDeactivateEvent. The legacy Stage.lights.set() API still works (entries are auto-adopted into the world tree on stage reset).
  • Lights now render inside the camera's post-effect FBO bracket (closes #1398) — vignette, scanlines, ColorMatrix, and any other camera shader effect now wrap the lighting output. The Stage.draw() lighting block has been removed; rendering happens via the world tree walk and a public Stage.drawLighting(renderer, camera) pass invoked by each camera (subclassable for custom lighting).
  • Procedural Light2d (closes #1430) — new Renderer.drawLight(light) API replaces the per-light offscreen-canvas pipeline. WebGL renders lights as quads through a shared RadialGradientEffect shader; per-light color and intensity flow through the vertex tint attribute, so N lights = 1 program switch + 1 flush. Canvas caches a small Gradient config per light (rebuilt only on radii / color / intensity change) and shares one CanvasRenderTarget across every gradient. Light2d is now pure data — no canvas, no shader knowledge.
  • Light2d.illuminationOnly (default false) — when true, the light's own gradient isn't drawn but it still feeds the cutout pass and the lit-sprite shader. Useful for SpriteIlluminator-style demos where the light is a logical source, not a visible glow.
  • Light2d.lightHeight (default max(radiusX, radiusY) * 0.075) — Z component of the light direction in the lit shader's dot(normal, lightDir). Low values graze across the surface (dramatic detail); high values produce more uniform brightness.
  • Light2d.setRadii(rx, ry) — updates both radii and the underlying bbox so getBounds() and getVisibleArea() track the new size. Fixes a latent bug where mutating radiusX/Y after construction left the rendered light stale while the cutout pass moved.
  • RadialGradientEffect — generic procedural radial gradient shader (video/webgl/effects/radialGradient.js). Solid color at center fading linearly to transparent at the host quad's edge. Accepts { color, intensity } plus setColor / setIntensity setters.
  • RenderState.peekScissor() — inspect the scissor box that the next restore() would install, without mutating state. Returns a live reference into the internal stack (read-only). Used by WebGLRenderer.restore() to flush only when the scissor actually changes.
  • Three new examplesNormal Map (three procedurally-generated 3D orbs reacting to a moving cursor light), SpriteIlluminator (port of CodeAndWeb's cocos2d-x dynamic-lighting demo), and Clipping (nested + animated Container.clipping).

Changed

  • Breaking: Light2d is now centered on its pos (anchorPoint = (0.5, 0.5)), matching Sprite and Ellipse(x, y, w, h) conventions. Constructor x/y and light.pos.x/y denote the light's center, not the bounding-box top-left. Transforms applied via light.scale(...) or light.rotate(...) now pivot around the visual center. Existing new Light2d(x, y, r) callers passing top-left coords need to add radius: new Light2d(x + r, y + r, r). Code using light.centerOn(x, y) is unaffected.
  • Bounds.addFrame now short-circuits the 4-corner walk when the matrix is identity (or omitted) — beneficial for WebGLRenderer.clipRect / enableScissor and any other call site that doesn't pre-filter.

Bug Fixes

  • Multi-light support — lifted the historical "Canvas mode only supports one light per stage" limitation. Multiple Light2d instances now render correctly under both Canvas and WebGL. Root cause was in the underlying setMask(shape, true) implementation on both renderers: chained calls did not accumulate cutouts. Canvas now adds the outer rect once per mask sequence (made tractable by the evenodd groundwork from #1369); WebGL switched to an INCR-based stencil protocol so each shape adds independently.
  • Container clipping under nested transforms (closes #1349) — a clipping container nested inside a translated, scaled, or rotated parent was mis-positioned. Container.draw now applies its own translate before calling clipRect and passes container-local (0, 0, width, height); WebGL clipRect transforms the four input corners through currentTransform and uses the AABB as the screen-space scissor box (so scale and rotation are honored). Canvas's context.rect was already matrix-aware.
  • WebGL flush ordering on scissor change — pending PrimitiveBatcher vertices now drain when a save()/restore() pair changes the scissor box. Previously WebGLRenderer.restore() reverted the GL scissor without flushing, so vertices queued inside a deeper clip could survive past restore() and flush later under a more permissive scissor.
  • CanvasRenderer.setMask(shape) X/Y swap — with a Rect, Bounds, or RoundRect mask, args were being passed in the wrong order to context.rect / context.roundRect. Masks at off-diagonal positions clipped at the wrong location. Latent because nothing in core or examples used those shape types as masks.
  • Stage.drawLighting cutout alignment — ambient-overlay cutouts now align with each light's rendered gradient when the camera is scrolled or the light is parented to a translated container. getVisibleArea() returns world-space coords, but drawLighting runs after the world container's translate(-cameraPos) has been popped — so cutouts were landing at world coords inside a camera-local FBO. Fix re-applies the camera's world-to-screen translate inside drawLighting.
  • WebGL vertex attribute leak between batchers — each batcher owns its own attribute layout (e.g. LitQuadBatcher 5 attrs at stride 28 vs PrimitiveBatcher 3 at stride 20). On batcher switch the previous batcher's enabled attribute locations stayed live with their old stride/offset and could throw INVALID_OPERATION on the next draw. Batcher.unbind() now disables them on every switch.
  • WebGL gl.useProgram leak after setLightUniformsCamera2d.draw() called setLightUniforms(...) every frame even when the scene had zero lights, leaving the GL program pointed at the lit shader. The next sprite draw (4-attribute vertex data) was being fed to the lit shader (5 attributes), rendering as garbage. Fixed by restoring the active batcher's program after the upload.
  • WebGL stale custom shader past setBatcher — the previous fast path returned early when the active batcher matched and no shader was provided, so a custom shader bound by a prior call could keep rendering subsequent batches through the wrong program. setBatcher now always reconciles the active shader.
  • QuadBatcher.blitTexture texture-unit cache desyncblitTexture did not sync currentTextureUnit / boundTextures[0] with the GL state it mutated. After a blit ran with a non-zero unit, subsequent bindTexture2D calls could short-circuit on the stale cached unit and bind the new texture on the wrong unit, corrupting the next sprite batch.
  • Stale Light2d gradient on radius / color / intensity change — pre-#1430 the gradient was baked once at construction. The new drawLight path auto-invalidates: the Canvas cache rebuilds when any of radiusX/radiusY/color/intensity differ; WebGL reads light.color / light.intensity live each call.

Install

npm install [email protected]

v19.2.0

29 Apr 11:13
9493187

Choose a tag to compare

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 [email protected]

v19.1.0

16 Apr 09:30
df65218

Choose a tag to compare

What's New in melonJS 19.1.0

New Features

  • Multi-texture batching — up to 16 textures drawn in a single batch/draw call, eliminating GPU flushes on texture changes. Automatically falls back to single-texture mode when a custom ShaderEffect is active. ~80% fewer draw calls on the platformer example (14 vs ~70 flushes/frame), with an estimated 30-50% FPS improvement on low-end mobile devices.
  • highPrecisionShader application setting — when false, caps shader precision at mediump for better performance on mobile GPUs that support highp but run faster at mediump. Default true (auto-detect highest precision).

Bug Fixes

  • getSupportedCompressedTextureFormats() no longer crashes when the GL context is unavailable
  • Examples updated for 19.0 API changes (Text standalone draw removal, Application pattern)

Install

npm install [email protected]

v19.0.0

14 Apr 10:00
a36a29f

Choose a tag to compare

What's New in melonJS 19.0.0

New Features

  • 3D mesh renderingMesh class for textured 3D triangle meshes with OBJ/MTL model loading, drawMesh() on both WebGL and Canvas renderers, Matrix3d.perspective() for perspective projection, mesh-to-canvas/ImageBitmap export and convex hull collision shapes
  • 15 built-in ShaderEffect presetsFlashEffect, OutlineEffect, GlowEffect, DesaturateEffect, PixelateEffect, BlurEffect, ChromaticAberrationEffect, DissolveEffect, DropShadowEffect, ScanlineEffect (with CRT curvature/vignette), TintPulseEffect, WaveEffect, InvertEffect, SepiaEffect, HologramEffect. All extend ShaderEffect with configurable uniforms.
  • Trail renderable — fading, tapering ribbons behind moving objects with auto-follow or manual point mode, color gradient, width curve, opacity, and blend mode support. Works on both WebGL and Canvas.
  • Gradient color samplingGradient.getColorAt(position, out) interpolates colors at any position along a gradient with float-space interpolation and lazy-parsed Color cache
  • Math utilitieslerpArray(values, position) for interpolating across evenly-spaced value arrays; computeVertexNormal(points, index, out) for perpendicular normals at polyline vertices
  • Vertex utilitiesnormalizeVertices(), projectVertices(), convexHull() for 3D vertex operations

Changed

  • BREAKING: Renderable.currentTransform is now a Matrix3d (was Matrix2d) — code accessing currentTransform.val indices must update: translation at [12],[13] (was [6],[7])
  • BREAKING: Matrix3d.scale(x, y, z) default z changed from 0 to 1 — prevents accidental Z-axis flattening
  • BREAKING: Text.draw() and BitmapText.draw() no longer accept text, x, y parameters — standalone draw removed (deprecated since 10.6.0)
  • BREAKING: Tween uses event-based lifecycle instead of adding to game.world. Public API unchanged.
  • BREAKING: depthTest application setting removed — GPU depth sorting is incompatible with 2D alpha blending. Depth testing remains available for 3D mesh rendering only.
  • BREAKING: UITextButton settings backgroundColor/hoverColor removed — use hoverOffColor/hoverOnColor
  • Container sortOn getter/setter with cached comparator function
  • customShader property moved to base Renderer class
  • exactOptionalPropertyTypes re-enabled for stricter TypeScript checking

Performance

  • Color hex conversion — pre-computed 256-entry lookup table replaces per-call nibble extraction + string concat
  • Color.toHex/toHex8 — read glArray directly, bypassing getter overhead
  • Gradient.getColorAt — float-space interpolation via toArray()/setFloat(), avoids int↔float round trips
  • Trail rendering — zero-allocation draw loop with pre-allocated normals, hoisted divisions, single-splice point expiry

Bug Fixes

  • Rect.setSize() now calls updateBounds() — fixes pointer event regression from July 2024 TS conversion
  • WebGL depth buffer correctly used for 3D mesh rendering
  • Canvas backface culling corrected for Y-flipped screen space
  • Canvas triangle seam expansion (0.5px) for anti-aliasing gaps

Dependencies

  • vite 8.0.3 → 8.0.8
  • esbuild 0.27.4 → 0.28.0
  • typescript-eslint 8.58.1 → 8.58.2
  • react 19.2.4 → 19.2.5, react-dom 19.2.4 → 19.2.5
  • typedoc 0.28.18 → 0.28.19
  • vite-plugin-glsl 1.5.6 → 1.6.0

v18.3.0

06 Apr 23:48
2f42bbb

Choose a tag to compare

What's New in melonJS 18.3.0

New Features

  • Bezier curve drawingbezierCurveTo(), quadraticCurveTo(), and arcTo() path methods, matching the Canvas 2D API. WebGL renderer tessellates via Path2D.
  • Gradient fillscreateLinearGradient() and createRadialGradient() methods for both Canvas and WebGL renderers, usable with all fill methods
  • Dashed linessetLineDash() and getLineDash() for stroke operations on both renderers
  • Tiled object factory registry — extensible registerTiledObjectFactory() and registerTiledObjectClass() APIs for plugins to register custom Tiled object handlers without modifying engine code

Changed

  • Application as entry pointnew Application(width, height, options) auto-calls boot(), making it a standalone entry point. video.init() is deprecated.
  • Game singleton decoupled — internal modules no longer import from the barrel index.js; game singleton uses setDefaultGame pattern
  • Stage lifecycleonResetEvent(app, ...args) and onDestroyEvent(app) now receive the Application instance as first parameter
  • Container defaults — dimensions default to Infinity (no intrinsic size, no clipping), removing dependency on game.viewport
  • EventEmitter — native context parameter support on on()/once(), eliminating .bind() closure overhead

Bug Fixes

  • BitmapText bounding box — width uses last glyph visual extent; height uses actual glyph extents instead of capHeight; baseline shifts use real glyph metrics for all baselines; y offset starts at first visible pixel
  • BitmapText multiline baseline — shift applied once for entire text block instead of per-line (which caused accumulating offsets)
  • Camera2d — floating containers with Infinity bounds (e.g., HUD) are now always visible, fixing a regression where HUD elements stopped rendering
  • Sprite flicker — time-based flickering (~15 flashes/sec) replaces per-draw-call toggle that broke with multi-camera setups
  • Path2D — fix quadraticCurveTo/bezierCurveTo startPoint reference bug and adaptive segment count
  • Application — fix settings mutation, white flash on load, pool cleanup errors
  • WebGLRenderer — fix setBlendMode premultipliedAlpha tracking
  • Text — fix multiline textBaseline y offset, power-of-two texture sizes

Performance

  • BitmapText — precompute glyphMinTop/glyphMaxBottom once in font parsing; cache measureText results in setText/resize

v18.2.2

29 Mar 13:41
a9147da

Choose a tag to compare

What's Changed

melonJS Team

  • Chore: fix missing README on the npm release (58a534d)
  • Copy root README into melonjs package during dist (6ef58cd)
  • Bump spine-plugin to 2.0.1 (501de1f)

Full Changelog: 18.2.1...18.2.2

v18.2.1

29 Mar 13:21
b4d6a68

Choose a tag to compare

What's Changed

melonJS Team

  • Fix loading screen logo persisting after preload completes (3f0be6f)

Full Changelog: 18.2.0...18.2.1

v18.2.0

29 Mar 10:26
37eaf7f

Choose a tag to compare

What's New in melonJS 18.2.0

New Features

  • Multi-camera support — stages can now have multiple cameras for split-screen, minimaps, and multi-viewport layouts
  • Extensible batcher system — custom Batcher subclasses with configurable maxVertices, indexed drawing (settings.indexed), and custom projection uniform names (settings.projectionUniform)
  • Tiled 1.8–1.12 full support — oblique maps, capsule shapes, list properties, embedded base64 images, tile sub-rectangles, tilerendersize/fillmode, layer blend modes, per-object opacity/visibility, repeatx/repeaty, parallaxoriginx/parallaxoriginy, class-type properties, isCollection flag
  • RoundRect as collision shape — can now be used for SAT collision via polygon-approximated rounded corners
  • Expanded blend modes — Canvas: overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion; WebGL2: darken, lighten via gl.MIN/gl.MAX

Changed

  • TypeScript — converted plugin, camera, particles emitter, state, audio, application, input, UI, and pointer modules to TypeScript
  • CDN URLs — README now uses cdn.jsdelivr.net instead of esm.run (which is still in beta)

Bug Fixes

  • WebGLRenderersetBatcher() now rebinds the shared vertex buffer when switching batchers, allowing custom batchers with their own GL buffers
  • WebGLRenderersetBlendMode() now accepts a premultipliedAlpha parameter for correct blending with non-premultiplied textures
  • Renderer — add base setBlendMode() and GPURenderer property to fix TypeScript casts
  • Pluginplugin.register() now uses pluginClass.name for reliable name derivation
  • TMXTileset — fix animation key using first frame tile ID instead of the tile's own ID
  • CanvasRenderer — replace bezier ellipse approximation with native context.ellipse() (with polyfill)
  • Plugin — fix plugin.get() throwing TypeError when searching by name with no match
  • Events — fix duplicate BLUR entry (was missing FOCUS)
  • UIBaseElement — fix isDraggable JSDoc and released default
  • Application — fix constructor options not being optional, fix getUriFragment() unsafe cast
  • CanvasRenderersetProjection() now properly applies the projection matrix as a canvas 2D transform

Spine Plugin

  • @melonjs/spine-plugin migrated into the monorepo — custom SpineBatcher with two-color tinting, indexed drawing, expanded example with 17 official Spine characters

v18.1.0

23 Mar 04:19
b444554

Choose a tag to compare

What's New in melonJS 18.1.0

New Features

  • ShaderEffect class — simplified custom shader API that only requires a fragment apply(color, uv) function. Vertex shader, uniforms, and texture sampling boilerplate are handled automatically. Silently disabled in Canvas mode.
  • create-melonjs CLI — scaffold a new game project in seconds with npm create melonjs my-game
  • Inline source code viewer — examples now have a "Show Code" button to view syntax-highlighted source alongside the running game
  • API docs landing page — new home page with quick start, feature overview, and common tasks
  • API docs categories — sidebar organized into 14 categories (Rendering, Physics, Input, etc.)

Changed

  • Physics — collision response is now mass-proportional; overlap and velocity correction are split based on relative mass
  • Entity — deprecated in favor of Sprite/Renderable + Body (#1008)
  • Loader — modernized with Promise-based asset loading for improved parallel performance; onload/onProgress/onError deprecated in favor of events
  • TMX — refactored TMXUtils into reusable decode.ts and xml.ts modules
  • Compositor → BatcherCompositor, QuadCompositor, PrimitiveCompositor renamed to Batcher, QuadBatcher, PrimitiveBatcher (old names still work with deprecation warnings)

Bug Fixes

  • Texture cache overflow — flush and rebatch when GPU texture units are exhausted instead of throwing (#1280)
  • setAntiAlias — now controls GL texture filtering (GL_NEAREST vs GL_LINEAR) on all bound textures (#1279)
  • createPattern leak — clean up previous GPU texture when repeat mode changes (#1278)
  • Custom shader support — properly flush and restore default shader per draw call, fix setUniform using wrong GL program, reset sampler uniform on shader switch
  • TMX — fix hexagonal pixelToTileCoords mutation, canvas memory leak, collision shape dimensions, XML parsing crash, GC pressure from vector allocations
  • Sprite — fix body-renderable misalignment with trimmed atlas frames, visual vibration on flip, jumping on rotated frames (#1201, #1214)
  • Path2D — fix SVG arc parsing and ellipse rotation (#1198)
  • WebGLRenderer — fix polygon corruption from vertex mutation, scissor restore, drawVertices vertex count, resize setAntiAlias corruption
  • Z-ordering — fix reset during collision response

Performance

  • TMX loading — ~20-40% faster via tileset caching, pre-allocated decode buffers, and fast path for base64 data
  • WebGL renderingfillRect, fillEllipse, fillArc, fillRoundRect, fillPolygon now generate geometry directly, bypassing path2D and earcut
  • WebGL batching — quad rendering uses gl.drawElements with index buffer (33% less vertex data), redundant sampler calls eliminated, zero-allocation save/restore stacks
  • Collision — index-based pool access instead of array push/pop in SAT detection
  • Container — O(n) accumulator pattern in getChildByProp/getChildByType instead of O(n²)

Developer Experience

  • 74 JSDoc typo and grammar fixes across 32 files
  • Coin glow shader example in the platformer demo
  • Updated typescript-boilerplate and plugin-template for melonJS 18
  • Archived es6-boilerplate with redirect notice

Full Changelog: 18.0.0...18.1.0