Skip to content

Releases: melonjs/melonJS

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 melonjs@19.3.0

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 melonjs@19.2.0

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 melonjs@19.1.0

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

v18.0.0

10 Mar 07:46

Choose a tag to compare

What's New in melonJS 18.0.0

New Features

  • Color constructor — now also accepts another Color object as parameter
  • Renderer — new backgroundColor property for clearing background between frames
  • Pool system — new type-safe createPool() API with improved performance; destroy renamed to release

Changed

  • Build system — replaced rollup with esbuild (@hornta)
  • Documentation — replaced webdoc with typedoc (@hornta)
  • Test framework — replaced mocha and puppeteer with vitest in browser mode (@hornta)
  • TypeScript — extensive conversion across the codebase: color, math, geometry, platform, DOM, keyboard, timer, application settings, pool, and more (@hornta)
  • Monorepo — migrated to pnpm workspaces with turbo (@hornta)
  • RenderableanchorPoint now uses the lighter ObservablePoint class instead of ObservableVector2d
  • Math — namespace Math is now deprecated and renamed to math for consistency
  • Deprecated API removal — all classes and methods deprecated since version 15 and lower have been removed (see Upgrade Guide)

Bug Fixes

  • Body — fix setCollisionType() not accepting numeric values; fix setVertices() fallback creating Point instead of Polygon
  • Camera — fix moveTo() upper bound clamping; fix focusOn() double-counting position
  • Container — fix addChildAt() rejecting valid index; fix getNextChild() returning wrong child; fix removeChildNow() crash when not attached to root
  • Physics — fix collision response velocity projection; fix shapes tunneling through polyline junctions; fix persistence for child bodies during reset (@Vareniel); fix step() crash with undefined ancestor
  • Renderable — fix Light2D color blending in Canvas mode (@Vareniel)
  • CanvasRenderTarget — fix arguments passed to convertToBlob() (@hornta)
  • TypeScript — fix missing OffscreenCanvas type for TextureAtlas constructor
  • Video — fix implicit global reference to HTMLVideoElement
  • Docs — fix floating default shown for UI elements (@SergioChan)

Performance

  • Collision — pre-built SAT function dispatch lookup table; reduced hot path overhead
  • Container — cache camera references outside per-child update loop
  • Physics — reduced iteration overhead in world.step()
  • QuadTree — reduced array allocations in retrieve() and insert()
  • Observable vectors — rewritten for better performance
  • Color — faster clone() method
  • Pool — optimized implementation

New Contributors

Full Changelog: 17.4.0...18.0.0