#225 by GitHub stars

melonjs/melonJS

A modern & fully featured HTML5 game engine (2D & 3D) for JavaScript/TypeScript, with WebGL/WebGPU rendering, built-in physics, Tiled map & glTF support and WebAudio.

History

Review weekly changes to SKILL.md before updating. Dates mark the weeks in which we observed each version; checks apply to the recorded commits.

View weekly changes

packages/melonjs/skills/melonjs/SKILL.md

Week of Nov 2, 2026 · +3 / −2 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 37 | [melonjs-input](../melonjs-input/SKILL.md) | Keyboard actions, pointer and touch handling, drag, gamepad, virtual controls. |
+ 37 | [melonjs-input](../melonjs-input/SKILL.md) | Keyboard actions, pointer and touch handling, drag, gamepad, virtual controls (fixed or floating sticks and buttons, tap to act). |
- 40 | [melonjs-tilemaps](../melonjs-tilemaps/SKILL.md) | Tiled maps — TMX/TSX loading, spawning entities from objects, collision layers, isometric maps. |
+ 40 | [melonjs-tilemaps](../melonjs-tilemaps/SKILL.md) | Tiled maps — TMX/TSX loading, spawning entities from objects, collision layers, isometric maps, reading and changing tiles at run time (a tile layer as the game board). |
+ 115 | [`@melonjs/gamepad-plugin`](https://www.npmjs.com/package/@melonjs/gamepad-plugin) | On-screen touch controls for phones and tablets: sticks and buttons reported as a gamepad, so the game's `bindGamepad` bindings drive it; fixed or floating under the thumb, hidden until touched, tap to act. Use it rather than writing virtual controls. |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-20-migration/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-3d/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-3d-assets/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-audio/SKILL.md

Week of Nov 2, 2026 · +7 / −5 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 466 **Credentials use the same global switch as every other asset type** —
- 467 `loader.setOptions({ withCredentials: true })`, not an audio-specific option.
- 468 Buffered audio fetches through the shared loader path, so it honours it exactly
- 469 as an image or a JSON file does. A streamed clip (`stream: true`, or
+ 466 **Credentials work as for every other asset type.** Buffered audio on the
+ 467 game's own origin carries its cookies and HTTP auth with no option; a clip on
+ 468 another origin behind a login needs `loader.setOptions({ withCredentials: true })`,
+ 469 not an audio-specific option, and that server must allow credentials. Buffered
+ 470 audio fetches through the shared loader path, so it behaves exactly as an image
+ 471 or a JSON file does. A streamed clip (`stream: true`, or
- 501 | audio behind a login fails, or the preload hangs | `loader.setOptions({ withCredentials: true })` — the same switch every asset type uses |
+ 503 | audio on another origin behind a login fails, or the preload hangs | `loader.setOptions({ withCredentials: true })` — the same switch every asset type uses. Audio on the game's own origin needs nothing (before 20.13 it needed it too) |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-camera-and-drawing/SKILL.md

Week of Nov 2, 2026 · +12 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

+ 208 A stage's cameras draw in the order they were added, each over the ones before
+ 209 it, so a full-screen second camera is a layer: a `Camera3d` drawing a 3D
+ 210 background first, the game's own camera over it, each kept to its part of the
+ 211 world with `visibilityLayer` / `cullMask` (below). A camera's post effects
+ 212 apply to what that camera draws: the first camera's pass starts the frame, and
+ 213 a later camera's is blended over what is already there (20.13 and later;
+ 214 before, it wiped out the cameras before it).
+ 215 
+ 239 - Particles are on their emitter's layers, and follow a change to them on the
+ 240   emitter's next update, so a camera that draws only the emitter's layer draws
+ 241   its particles too.
+ 397 | a second camera's post effect wipes out what the first camera drew | before 20.13 a later camera's post-effect pass replaced the whole frame; update, or put the effects on a container instead of the camera |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-deployment/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-display-and-scaling/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-effects-and-shaders/SKILL.md

Week of Nov 2, 2026 · +24 / −8 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 157 camera's effect, a mesh's texture. Do not pass `textureSize` to get this: a
+ 157 camera's effect, a mesh's texture. With other effects after it, it still
+ 158 measures in the sprite's own pixels, at any scale and under a camera's zoom
+ 159 (on a container, in the container's own), so adding a second effect never
+ 160 changes how wide the first comes out. Do not pass `textureSize` to get this: a
- 162 // 2 pixels of the sprite's art, whatever the size of its atlas
+ 165 // 2 pixels of the sprite's art, whatever the size of its atlas, its scale,
+ 166 // the camera's zoom, or the effects after it
- 164 ```
+ 168 sprite.addPostEffect(new GlowEffect(app.renderer, { width: 6 }));
+ 169 ```
+ 170 
+ 171 With two or more effects, the sprite is drawn into a capture and each effect
+ 172 reads that, at the game's texture filter, as a sprite reads its own texture:
+ 173 under `"nearest"` (or `antiAlias` off) a width that lands between two screen
+ 174 pixels, such as 2 pixels of a sprite drawn at 0.3, stays crisp, and under
+ 175 `"linear"` it blends, on WebGL and WebGPU alike. The result lands inside the
+ 176 clip the sprite is drawn under, a minimap camera's view or a clipping
+ 177 container, as with one effect.
- 721 `ShaderEffect` on a Canvas renderer logs a warning, leaves `enabled = false`, and
- 722 turns every method into a no-op. Nothing throws. The same happens on a GPU
- 723 backend when no body matches its language — a GLSL-only effect under WebGPU.
- 724 
- 725 That is fine for decoration. It is **not** fine when the shader draws your
+ 734 `ShaderEffect` on a Canvas renderer leaves `enabled = false` and turns every
+ 735 method into a no-op. Nothing throws, and the console gets one warning, from the
+ 736 first such effect; the rest stay quiet. The same happens on a GPU backend when
+ 737 no body matches its language — a GLSL-only effect under WebGPU.
+ 738 
+ 739 That is fine for decoration, which needs no guard: create and add the effect as
+ 740 on any renderer. It is **not** fine when the shader draws your
+ 761 | a camera's post effect hides another camera drawn before it | before 20.13 a later camera's pass replaced the whole frame; update. Since then each camera's effects apply to what it draws, over the cameras before it |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-events/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-getting-started/SKILL.md

Week of Nov 2, 2026 · +1 / −2 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 140 loader.setOptions({ crossOrigin: "anonymous" });   // needed for remote assets
- 176 | cross-origin textures fail to load | missing `loader.setOptions({ crossOrigin: "anonymous" })` |
+ 175 | an asset on another origin fails to load, the error naming it | that server sends no `Access-Control-Allow-Origin` header (see melonjs-loading-assets) |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-input/SKILL.md

Week of Nov 2, 2026 · +61 / −3 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 3 description: "Use this skill for keyboard, pointer, mouse, touch and gamepad input in melonJS. Covers the bindKey action indirection, registerPointerEvent on regions, the isKinematic requirement that silently blocks pointer events, which of several overlapping objects receives an event and how to consume it, world vs screen pointer coordinates, and gamepad mapping onto the same actions. Triggers on: input, bindKey, isKeyPressed, KEY, registerPointerEvent, releasePointerEvent, pointerdown, pointerup, pointermove, POINTERMOVE, gamepad, bindGamepad, triggerKeyEvent, KEYDOWN, KEYUP, press any key, splash screen, click, touch, drag, hit test, event propagation, stop propagating, overlapping, which object gets the click."
+ 3 description: "Use this skill for keyboard, pointer, mouse, touch and gamepad input in melonJS. Covers the bindKey action indirection, registerPointerEvent on regions, the isKinematic requirement that silently blocks pointer events, which of several overlapping objects receives an event and how to consume it, world vs screen pointer coordinates, and gamepad mapping onto the same actions. Triggers on: input, bindKey, isKeyPressed, KEY, registerPointerEvent, releasePointerEvent, pointerdown, pointerup, pointermove, POINTERMOVE, gamepad, bindGamepad, triggerKeyEvent, virtual gamepad, touch controls, joystick, gamepad-plugin, floating stick, floating button, floating joystick, two thumbs, tap to fire, hideAtRest, mobile controls, KEYDOWN, KEYUP, press any key, splash screen, click, touch, drag, hit test, event propagation, stop propagating, overlapping, which object gets the click."
- 276 On-screen buttons synthesise key events, again reaching the same actions:
+ 276 For on-screen touch controls, use `@melonjs/gamepad-plugin` rather than
+ 277 writing them: sticks and buttons drawn over the game and reported as a pad in
+ 278 a gamepad slot, so the `bindGamepad` bindings above answer to them unchanged.
+ 279 
+ 280 ```js
+ 281 import { GamepadPlugin } from "@melonjs/gamepad-plugin";
+ 282 
+ 283 // once the application is created: a stick and two buttons, on touch screens
+ 284 plugin.register(GamepadPlugin, "gamepad", {
+ 285     buttons: [{ label: "A" }, { label: "B" }],
+ 286 });
+ 287 ```
+ 288 
+ 289 The left stick presses the D-pad buttons (`GAMEPAD.BUTTONS.UP` ...) in 8
+ 290 directions and reports `AXES.LX`/`LY`; the buttons press `FACE_1`, `FACE_2`...
+ 291 in turn, or a key with `{ key: input.KEY.SPACE }`. Each finger is followed on
+ 292 its own, a touch starting on the controls does not reach the game, and they
+ 293 hide while a real pad is in use. `plugin.cache.gamepad.visible` says whether
+ 294 they show, to word a prompt ("tap" or a key name).
+ 295 
+ 296 Sticks and buttons are fixed in a corner by default. **`floating: true`**
+ 297 puts one under the finger wherever it lands inside its `area` (`"half"`, its
+ 298 own half of the screen, by default; `"screen"`; or `{ x, y, width, height }`),
+ 299 and **`hideAtRest: true`** draws it only while held. The usual mobile
+ 300 two-thumb layout, nothing on screen until touched (1.1):
+ 301 
+ 302 ```js
+ 303 plugin.register(GamepadPlugin, "gamepad", {
+ 304     sticks: [{ floating: true, hideAtRest: true }],                 // left half
+ 305     buttons: [{ floating: true, hideAtRest: true, label: "A" }],    // right half
+ 306 });
+ 307 ```
+ 308 
+ 309 Or one thumb for both: a stick over the whole screen whose quick tap (lifted
+ 310 within `tap.time`, 250 ms, without leaning to `dpadThreshold`) presses a key
+ 311 or button for one frame, and no button at all:
+ 312 
+ 313 ```js
+ 314 plugin.register(GamepadPlugin, "gamepad", {
+ 315     sticks: [{ floating: true, area: "screen", hideAtRest: true,
+ 316                tap: { key: input.KEY.SPACE } }],
+ 317     buttons: 0,
+ 318 });
+ 319 ```
+ 320 
+ 321 Where areas overlap, a finger takes the first free control there: fixed
+ 322 buttons, fixed sticks, floating buttons, floating sticks. So a floating button
+ 323 on its `"half"` carves the right half out of a `"screen"` stick, and several
+ 324 floating buttons sharing an area split it by distance to where each rests.
+ 325 Every press lasts at least one frame, so a 15 ms thumb tap still reaches
+ 326 `isKeyPressed`, and Safari's pinch gesture is cancelled while the controls
+ 327 show, so a second thumb works on iOS.
+ 328 
+ 329 A single key can also be pressed directly, reaching the same actions:
+ 349 | floating stick ignores the right half of the screen | its `area` defaults to `"half"`, its own side — use `area: "screen"` |
+ 350 | `area: "screen"` stick still ignores part of the screen | a floating button's area comes first there — the overlap order is fixed buttons, fixed sticks, floating buttons, floating sticks |
+ 351 | a quick tap on a stick moves instead of acting | it leaned past `dpadThreshold` or outlasted `tap.time` before lifting |
- 298 | a widget under an overlapping panel still reacts | the panel consumes nothing — return `false` from its `onClick` and `onOver` |
+ 354 | a widget under an overlapping panel still lights up | the panel consumes no hover: return `false` from its `onOver` (presses already stop at UI elements; `UIPanel` with `blocking` does both) |
+ 358 | touch controls do nothing | nothing bound to their slot — `bindGamepad` on the plugin's `index` (0 by default), or give the buttons a `key` |
+ 359 | touch controls never show on a desktop | the gamepad plugin's `enabled` defaults to `device.touch` — pass `enabled: true` to try them there |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-lighting/SKILL.md

Week of Nov 2, 2026 · +4 / −3 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 177 `renderer.setLightUniforms()` is a no-op there, and the first frame that passes a
- 178 non-empty light list logs a one-shot warning. Normal-mapped sprites simply render
- 179 unlit — no error.
+ 177 `renderer.setLightUniforms()` is a no-op there, and the first normal-mapped
+ 178 sprite drawn while lights are on logs a one-shot warning (a scene with lights
+ 179 and no normal maps stays quiet). Normal-mapped sprites simply render unlit — no
+ 180 error.

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-loading-assets/SKILL.md

Week of Nov 2, 2026 · +36 / −12 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 15 
- 16 loader.setOptions({ crossOrigin: "anonymous" });   // needed for remote assets
- 380 Two settings, both global and both applying to **every** asset type — there is
- 381 no per-asset or per-type spelling to remember:
+ 378 **Nothing to set for the usual cases.** Every fetched asset (image, json,
+ 379 binary, tmx, tsx, shader, gltf/glb, obj, mtl, aseprite, buffered audio) is
+ 380 requested the same way:
+ 381 
+ 382 - **On the game's own origin**, it carries the browser's cookies and HTTP
+ 383   auth, as the page and its scripts do. A game behind a login or a session
+ 384   cookie loads its own assets with no option.
+ 385 - **On another origin** (a CDN), it is a CORS request: it loads when that
+ 386   server sends `Access-Control-Allow-Origin`, and fails at load when not, with
+ 387   an error naming the asset and the header it needs. No `crossOrigin` needed.
+ 388 
+ 389 | asset on… | default | `withCredentials: true` |
+ 390 |---|---|---|
+ 391 | the game's own origin | loads, with its cookies | loads, with its cookies |
+ 392 | another origin sending `Access-Control-Allow-Origin` | loads, no cookies | **blocked** if it answers `*`; needs the page's exact origin plus `Access-Control-Allow-Credentials: true` |
+ 393 | another origin allowing credentials for the page | loads, no cookies | loads, with that origin's cookies |
+ 394 | another origin with no CORS headers | fails at load, naming the asset | fails at load, naming the asset |
+ 395 
+ 396 Two global settings cover the rest, for every asset type — there is no
+ 397 per-asset or per-type spelling to remember:
- 385     crossOrigin: "anonymous",   // CORS mode for remote assets
- 386     withCredentials: true,      // send cookies / auth with the request
+ 401     withCredentials: true,      // send cookies / auth to OTHER origins too
+ 402     crossOrigin: "anonymous",   // attribute of the <video>, <script> and glTF
+ 403                                 // image elements the loader creates
- 390 `withCredentials` is what an asset behind a session cookie or an
- 391 `Authorization`-style login needs. Every fetched asset — image, json, binary,
- 392 tmx, tsx, shader, gltf/glb, obj, mtl, aseprite, video — carries it, and so does
- 393 buffered audio.
+ 407 `withCredentials` is for assets on **another** origin behind a session cookie
+ 408 or a login; that server must then allow credentials for the page's origin, or
+ 409 the browser blocks the request. `crossOrigin: "use-credentials"` does the same
+ 410 for fetched assets, and sets the attribute on the elements.
+ 411 
+ 412 Before 20.13 both were needed far more often: assets on the game's own origin
+ 413 went out with no cookies unless `withCredentials` was set (a 401 or 403 behind
+ 414 a login), and an asset on another origin without `crossOrigin: "anonymous"`
+ 415 was read as an empty response, failing later with "Unexpected end of input" or
+ 416 "The source image could not be decoded" even when its server allowed it.
- 411 | cross-origin textures fail | missing `loader.setOptions({ crossOrigin: "anonymous" })` |
- 412 | assets behind a login 401 / audio preload hangs | missing `loader.setOptions({ withCredentials: true })` — one switch, all asset types |
+ 434 | `Failed to load "…" (Failed to fetch). An asset on another origin loads only if its server allows it…` | that origin sends no `Access-Control-Allow-Origin` header — or, with `withCredentials`, sends `*` or no `Access-Control-Allow-Credentials: true`. Fix the server's headers, or serve the asset from the game's own origin |
+ 435 | an asset on another origin behind a login 401s / audio preload hangs | missing `loader.setOptions({ withCredentials: true })` — one switch, all asset types. Assets on the game's own origin need nothing (before 20.13 they needed it too) |
+ 436 | a remote JSON fails with "Unexpected end of input", or an image with "The source image could not be decoded" | melonJS before 20.13, which read assets on another origin without CORS unless `crossOrigin: "anonymous"` was set |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-math/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-particles-and-trails/SKILL.md

Week of Nov 2, 2026 · +72 / −8 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 3 description: "Use this skill for particle effects and motion trails in melonJS — ParticleEmitter configuration, burst versus stream emission, the referenceSpace frame of reference introduced in 20.2, 3D particle motion through elevation and minSpread/maxSpread in 20.8, and the Trail renderable. Covers the emitter that silently does nothing until you call an emit method. Triggers on: Trail3d, ribbon, exhaust trail, speed streak, ski track, tyre mark, ribbonNormal, ParticleEmitter, particles, burstParticles, streamParticles, referenceSpace, ParticleEmitterSettings, elevation, minSpread, maxSpread, spawn area, Trail, explosion, smoke, sparks, debris, ring, exhaust, emitter, tintOverLife, colour over life, color over life, flame, fire."
+ 3 description: "Use this skill for particle effects and motion trails in melonJS — ParticleEmitter configuration, burst versus stream emission, the referenceSpace frame of reference introduced in 20.2, 3D particle motion through elevation and minSpread/maxSpread in 20.8, and the Trail renderable. Covers the emitter that silently does nothing until you call an emit method. Triggers on: Trail3d, ribbon, exhaust trail, speed streak, ski track, tyre mark, ribbonNormal, ParticleEmitter, particles, particle editor, tune particles, spawn depth, volume, burstParticles, streamParticles, referenceSpace, ParticleEmitterSettings, elevation, minSpread, maxSpread, spawn area, Trail, explosion, smoke, sparks, debris, ring, exhaust, emitter, tintOverLife, colour over life, color over life, flame, fire."
+ 30 
+ 31 Without an `image`, particles are white squares of `textureSize` pixels (8 by
+ 32 default), coloured by `tint`. Every such emitter draws the renderer's one
+ 33 shared white pixel, stretched, so an emitter per explosion or a `reset()` on
+ 34 every tweak costs no texture.
+ 35 
+ 36 ## Tuning an effect: the Particle Editor
+ 37 
+ 38 Settings are easier to find by eye than by reasoning. The
+ 39 [Particle Editor](https://melonjs.github.io/melonJS/examples/#/particle-editor)
+ 40 runs one emitter live, with every setting on a slider, from a preset (fire,
+ 41 smoke, rain, explosions, 3D bursts and rings…), a built-in texture or a sprite
+ 42 (sheet) of your own, under a 2D camera or an orbiting 3D one. It writes out the
+ 43 matching `new ParticleEmitter(...)` call, listing only what differs from the
+ 44 defaults, and the same settings as JSON, in the emitter's own units, to load at
+ 45 run time:
+ 46 
+ 47 ```js
+ 48 // preloaded with { name: "smoke-fx", type: "json", src: "data/fx/smoke.json" }
+ 49 const effect = loader.getJSON("smoke-fx");
+ 50 const emitter = new ParticleEmitter(x, y, {
+ 51     ...effect.settings,
+ 52     // no `image` for the plain square: `getImage` would give null
+ 53     ...(effect.image ? { image: loader.getImage(effect.image) } : {}),
+ 54 });
+ 55 ```
+ 56 
+ 57 Point a user there when they are tuning numbers by trial and error.
+ 58 
+ 59 It covers every setting that changes how one emitter looks, but not:
+ 60 
+ 61 - **Layered effects.** It shows one emitter at a time. A campfire (flames,
+ 62   sparks, smoke) or an explosion (flash, debris, smoke) is several emitters
+ 63   at the same spot: tune each in the editor, then add them side by side,
+ 64   the one drawn on top last (or with a higher `z`).
+ 65 - **Trails.** `Trail` and `Trail3d` are separate renderables; see below.
+ 66 - **Code-only settings.** `onComplete`, `autoDestroyOnComplete`,
+ 67   `accurateBounds`, and a `Container` as `referenceSpace` (it offers
+ 68   `"local"` and `"world"`). `textureAdditive` is left out: `blendMode`
+ 69   replaces it.
+ 70 - **Changes at run time.** Calling `burstParticles()` when something
+ 71   happens, or assigning `emitter.blendMode` while particles are alive, is
+ 72   game code. Its "moving emitter" option previews the common case: an
+ 73   emitter attached to something that moves.
- 110 Give it the size of whatever broke. The area is measured from the emitter's own
- 111 position, NOT centred on it, so offset by half or the whole burst sits down and
- 112 to the right of its source:
+ 154 Give it the size of whatever broke. The constructor centres the area on the
+ 155 point it is given, so pass the centre of the source:
- 116 const emitter = new ParticleEmitter(x - area / 2, y - area / 2, {
- 117     width: area, height: area,
- 118 });
+ 159 const emitter = new ParticleEmitter(x, y, { width: area, height: area });
+ 160 ```
+ 161 
+ 162 After that, `pos` is the area's top-left corner, as on any renderable. To move
+ 163 the emitter to a new source later, offset by half, or the burst sits down and
+ 164 to the right of it:
+ 165 
+ 166 ```js
+ 167 emitter.pos.set(x - area / 2, y - area / 2);   // re-aimed at (x, y)
+ 168 emitter.burstParticles();
+ 169 ```
+ 170 
+ 171 Under a `Camera3d`, `depth` (20.13) gives the area its third side: each
+ 172 particle is born at a random depth within it, centered on the emitter's own
+ 173 depth, unlike `width` and `height`. Without it every particle starts on one
+ 174 plane facing the camera, so rain or dust seen from the side is a flat sheet. A
+ 175 2D camera does not show depth, though with an order-dependent blend the
+ 176 particles are still sorted by it. It is the spawn box's size, not the
+ 177 emitter's `depth` property, which is where the emitter sits along Z.
+ 178 
+ 179 ```js
+ 180 new ParticleEmitter(x, y, { width: 600, height: 20, depth: 400 });   // a volume of rain
+ 386 | a camera that draws only the emitter's layer shows no particles | before 20.13 particles stayed on layer 1 whatever their emitter was on; update |
- 338 | the burst sits down-right of its source | the spawn area starts at the emitter's position; offset by half |
+ 401 | the burst sits down-right of its source | the emitter was moved by setting `pos` to the source: `pos` is the area's top-left corner, so offset by half (only the constructor centres it) |
+ 402 | the burst sits up-left of its source | the constructor was given the top-left corner: it centres the area on (x, y), so pass the centre |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-performance/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-physics/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-plugins/SKILL.md

Week of Nov 2, 2026 · +25 / −1 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 3 description: "Use this skill when adding a melonJS plugin or adapter — the debug panel, Spine skeletal animation, compressed Tiled map support, Capacitor packaging — or when writing your own plugin. Covers plugin.register and its version gate, plugin.cache, plugin.patch, and why physics adapters are not plugins. Triggers on: plugin, plugin.register, plugin.cache, plugin.get, plugin.patch, BasePlugin, debug-plugin, DebugPanelPlugin, SpinePlugin, TiledInflatePlugin, CapacitorPlugin, adapter, custom plugin, plugin version mismatch, already registered."
+ 3 description: "Use this skill when adding a melonJS plugin or adapter — the debug panel, Spine skeletal animation, compressed Tiled map support, on-screen touch controls, Capacitor packaging — or when writing your own plugin. Covers plugin.register and its version gate, plugin.cache, plugin.patch, and why physics adapters are not plugins. Triggers on: plugin, plugin.register, plugin.cache, plugin.get, plugin.patch, BasePlugin, debug-plugin, DebugPanelPlugin, SpinePlugin, TiledInflatePlugin, GamepadPlugin, gamepad-plugin, touch controls, virtual joystick, floating joystick, floating button, CapacitorPlugin, adapter, custom plugin, plugin version mismatch, already registered."
+ 91 ### `@melonjs/gamepad-plugin`
+ 92 
+ 93 On-screen touch controls, reported as a gamepad: one or two sticks and any
+ 94 number of buttons, drawn over the game and read by the engine as a pad in a
+ 95 gamepad slot. The game's `bindGamepad` bindings drive it unchanged, so one set
+ 96 of bindings covers a real pad and a touch screen.
+ 97 
+ 98 ```js
+ 99 import { GamepadPlugin } from "@melonjs/gamepad-plugin";
+ 100 plugin.register(GamepadPlugin, "gamepad", {
+ 101     buttons: [{ label: "A" }, { label: "B" }],
+ 102 });
+ 103 // plugin.cache.gamepad.visible — showing, to word prompts ("tap" or a key)
+ 104 ```
+ 105 
+ 106 Shown on touch screens by default (`enabled`), hidden while a real pad is in
+ 107 use. The left stick also presses the D-pad; a button can press a key instead
+ 108 of a pad button. Since 1.1 a stick or button can be `floating` (shows up
+ 109 under the finger anywhere in its `area`), `hideAtRest` (drawn only while
+ 110 held), and a stick can take a `tap` (a quick touch that does not steer presses
+ 111 a key or button) — so the mobile layouts, two thumbs or one thumb with
+ 112 tap-to-act, need no code of their own. See `melonjs-input` for the bindings,
+ 113 the layouts and the overlap rules.
+ 114 

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-renderables/SKILL.md

Week of Nov 2, 2026 · +4 / −3 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 177 the Canvas renderer (`renderer.shaderLanguage === null`) logs a warning, stays
- 178 at `enabled === false` and no-ops every method rather than throwing — and
- 179 `beginPostEffect` filters disabled effects out. Since `video.AUTO` picks the
+ 177 the Canvas renderer (`renderer.shaderLanguage === null`) stays at
+ 178 `enabled === false` and no-ops every method rather than throwing — and
+ 179 `beginPostEffect` filters disabled effects out. The first one logs a warning,
+ 180 the rest stay quiet, so a decorative effect needs no renderer check. Since `video.AUTO` picks the

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-renderer-backends/SKILL.md

Week of Nov 2, 2026 · +2 / −2 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 50 warns once (`Light2d normal-map lighting requires the WebGL renderer`) and then
- 51 renders sprites unlit. A `Mesh` still draws too, sorted with the painter's
+ 50 warns once (`Light2d normal-map lighting requires a GPU backend`), the first time
+ 51 a normal-mapped sprite is drawn under a light, and then renders sprites unlit. A `Mesh` still draws too, sorted with the painter's

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-scenes-and-state/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-sprites-and-animation/SKILL.md

Week of Nov 2, 2026 · +0 / −0 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-tilemaps/SKILL.md

Week of Nov 2, 2026 · +74 / −2 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 3 description: "Use this skill for Tiled maps in melonJS — loading TMX/TSX levels, spawning entities from Tiled objects, collision shapes authored in Tiled, isometric and hexagonal maps, and image layers. Covers registerTiledObjectClass and the pool.register name contract, camera bounds, compressed maps needing the inflate plugin, and the level director API. Triggers on: Tiled, TMX, TSX, tilemap, level.load, level.load async, await level.load, tileset, ImageLayer, isometric, hexagonal, staggered, pool.register, Collectable, Trigger, object layer, collision layer, parallax, registerTiledObjectClass, registerTiledObjectFactory, TMXObject, resolvedProperty, resolvedProperties, tile class, tile properties."
+ 3 description: "Use this skill for Tiled maps in melonJS — loading TMX/TSX levels, spawning entities from Tiled objects, collision shapes authored in Tiled, isometric and hexagonal maps, and image layers, and reading or changing tiles while the game runs. Covers registerTiledObjectClass and the pool.register name contract, camera bounds, compressed maps needing the inflate plugin, and the level director API. Triggers on: Tiled, TMX, TSX, tilemap, setTile, clearTile, cellAt, getTile, getTileById, change a tile, tile at run time, game board, grid, tile properties at run time, getTileProperties, map properties, level.load, level.load async, await level.load, tileset, ImageLayer, isometric, hexagonal, staggered, pool.register, Collectable, Trigger, object layer, collision layer, parallax, registerTiledObjectClass, registerTiledObjectFactory, TMXObject, resolvedProperty, resolvedProperties, tile class, tile properties."
+ 188 ## Reading and changing tiles at run time
+ 189 
+ 190 A tile layer is a live grid, not just a picture: read it as the game board,
+ 191 and change cells while the game runs — a door opening, a floor panel lighting
+ 192 up, a wall blown away. A loaded tile layer is a `TMXLayer` in the world, under
+ 193 the name it has in Tiled:
+ 194 
+ 195 ```js
+ 196 await level.load("map1", { container: this.app.world, async: true });
+ 197 const board = this.app.world.getChildByName("ground")[0];   // a TMXLayer
+ 198 board.cols; board.rows;              // the grid, in cells
+ 199 board.tilewidth; board.tileheight;   // a cell, in pixels
+ 200 ```
+ 201 
+ 202 Two coordinate systems, and the methods do not share one:
+ 203 
+ 204 | method | takes | returns |
+ 205 |---|---|---|
+ 206 | `cellAt(col, row)` | column and row | the `Tile` there, or `null` for an empty cell or outside the grid |
+ 207 | `getTile(x, y)` | **world pixels** | the same `Tile` object `cellAt` gives, or `null` |
+ 208 | `setTile(tile, col, row)` | column and row | puts `tile` there, and returns it |
+ 209 | `clearTile(col, row)` | column and row | empties the cell: `cellAt` then returns `null` |
+ 210 | `getTileById(gid, col, row)` | a global tile id, column and row | a new `Tile`, to pass to `setTile` |
+ 211 
+ 212 A `Tile` carries `tileId` (its **global** id), `col`, `row` and `tileset`.
+ 213 Tiled shows a tile's *local* id in the tileset editor; the global id is
+ 214 `tileset.firstgid + localId`, and only the global one works with `setTile`.
+ 215 The tile's custom properties and class, set in Tiled's tileset editor, come
+ 216 from its tileset by global id:
+ 217 
+ 218 ```js
+ 219 const tile = board.cellAt(col, row);
+ 220 const props = tile && tile.tileset.getTileProperties(tile.tileId);  // { kind: "floor" } or undefined
+ 221 const cls = tile && tile.tileset.getTileClass(tile.tileId);
+ 222 ```
+ 223 
+ 224 To swap a cell for another tile of the tileset, build the tile and set it. Look
+ 225 global ids up by property once, rather than hard-coding them, so a designer
+ 226 can rearrange the tileset:
+ 227 
+ 228 ```js
+ 229 const tileset = board.tilesets.getTilesetByIndex(0);   // the layer's first tileset
+ 230 const gidByKind = new Map();
+ 231 for (let gid = tileset.firstgid; gid <= tileset.lastgid; gid++) {
+ 232     const kind = tileset.getTileProperties(gid)?.kind;
+ 233     if (kind !== undefined) gidByKind.set(kind, gid);
+ 234 }
+ 235 
+ 236 // light a floor panel up
+ 237 board.setTile(board.getTileById(gidByKind.get("floor-lit"), col, row), col, row);
+ 238 // knock a wall down
+ 239 board.clearTile(col, row);
+ 240 ```
+ 241 
+ 242 The layer redraws the changed cell by itself. What it does **not** change is
+ 243 collision: solid ground comes from collision object layers (below), so a tile
+ 244 set or cleared changes what is drawn and what `cellAt` returns, not what a body
+ 245 runs into. Keep a layer you edit often off `preRender` (see the symptom table).
+ 246 
+ 247 The map's own custom properties (Map → Map Properties in Tiled) are set on the
+ 248 map object, which `level.getCurrentLevel()` returns once it is loaded: a
+ 249 `theme` string property is `level.getCurrentLevel().theme`. They are set
+ 250 **over** the map's own fields, so a property named like one replaces it: a
+ 251 custom `name` property becomes the level's `name`. Give level settings names of
+ 252 their own (`title`, not `name`).
+ 253 
+ 413 | objects a screen added just before `level.load` draw in the order they were added (a background over everything) | before 20.13 the reset inside `level.load` dropped the sort still pending for the persistent children it kept; update, or call `sort()` on their container once the level is loaded |
- 364 | a tileset's transparent color (`trans`) still shows (the console says it could not be applied) | the image cannot be read back: one from another origin needs CORS headers on the server and `loader.setOptions({ crossOrigin: "anonymous" })` (without them WebGL and WebGPU cannot draw it at all), and a compressed texture cannot be changed: export it with an alpha channel instead |
+ 431 | a tileset's transparent color (`trans`) still shows (the console says it could not be applied) | the image cannot be read back: an image the game made itself from another origin without CORS (an `<img>` with no `crossOrigin`) cannot be, so load it with the loader, which only loads an image another origin's server allows (its CORS headers) and can always read back; and a compressed texture cannot be changed: export it with an alpha channel instead |
+ 432 | `cellAt` returns `null` (or the wrong tile) for a cell that has one | it was given pixels: `cellAt`, `setTile` and `clearTile` take column and row, only `getTile` takes world pixels |
+ 433 | `setTile` shows the wrong tile, or nothing | it was given Tiled's local tile id: use the global id, `tileset.firstgid + localId`, through `getTileById` |
+ 434 | a layer is `undefined` from `getChildByName(name)[0]` | the level is not in the world yet (await `level.load(id, { async: true })`), or the name differs from the layer's in Tiled |
+ 435 | a tile set or cleared at run time does not block or free movement | tile layers carry no collision: collision comes from collision object layers |
+ 436 | the level's `name` is not its id after loading | a map custom property named `name` replaced it: map properties are set over the map's own fields |

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

packages/melonjs/skills/melonjs-ui-and-text/SKILL.md

Week of Nov 2, 2026 · +519 / −24 lines

Before Network or broad shell → After Network or broad shell

05ed64b14baa → a41d6a328557

- 3 description: "Use this skill for HUDs, buttons, menus, dialogue panels, progress bars and on-screen text in melonJS. Covers UIBaseElement/UISpriteElement/UITextButton, ProgressBar, UIPanel, Draggable and DropTarget, the floating screen-space container pattern, which of two overlapping panels gets the pointer, Text and BitmapText, web font loading, and NineSliceSprite panels. Triggers on: UI, HUD, button, UIBaseElement, UISpriteElement, UITextButton, ProgressBar, progress bar, health bar, gauge, Draggable, DropTarget, menu, dialogue, overlapping panels, moveToTop, onOver, onClick, bindKey, keyboard shortcut, button keyboard, Tween, easing, animate a label, wavy text, per-glyph effect, glyphEffect, shake text, rainbow text, Text, BitmapText, fillStyle, fillGradient, gradient text, tint, font, fontface, wordWrapWidth, NineSliceSprite, score display, floating., fullscreen, full screen, requestFullscreen, exitFullscreen, isFullscreen, fullscreen button, fullscreen option"
+ 3 description: "Use this skill for HUDs, buttons, menus, options screens, dialogue panels, progress bars and on-screen text in melonJS. Covers UIBaseElement/UISpriteElement/UITextButton, UICheckbox, UISlider, ProgressBar, UIPanel (skins, layout, scrolling), Draggable and DropTarget, the floating screen-space container pattern, which of two overlapping panels gets the pointer, Text and BitmapText, web font loading, and NineSliceSprite panels. Triggers on: UI, HUD, button, checkbox, radio button, UICheckbox, slider, UISlider, volume slider, options menu, settings screen, scrolling list, scroll panel, layout, column, grid, UI skin, UIBaseElement, UISpriteElement, UITextButton, ProgressBar, progress bar, health bar, gauge, Draggable, DropTarget, menu, dialogue, overlapping panels, moveToTop, onOver, onClick, bindKey, keyboard shortcut, button keyboard, Tween, easing, animate a label, wavy text, per-glyph effect, glyphEffect, shake text, rainbow text, Text, BitmapText, fillStyle, fillGradient, gradient text, tint, font, fontface, wordWrapWidth, NineSliceSprite, score display, floating., fullscreen, full screen, requestFullscreen, exitFullscreen, isFullscreen, fullscreen button, fullscreen option"
- 9 ## Outlined text: the stroke eats into the glyph
- 10 
- 11 `Text` draws `fillText` and then `strokeText`, so the outline lands **on top of
- 12 the fill** and is centred on the glyph edge — half of it inward. On a small or
- 13 chunky face a `lineWidth` of 3 leaves the letters solid black. Keep it to 1,
- 14 and raise the font size rather than the stroke.
+ 9 ## Outlined text: behind the glyphs, or over them
+ 10 
+ 11 An outline is centred on the glyph's edge, so half of it lies inside the
+ 12 glyph. `Text` paints it **behind** the glyphs by default (`paintOrder:
+ 13 "stroke"`): the letters stay whole and the outline shows only around them, so
+ 14 a thick outline works on any face. What shows is half of `lineWidth`: ask for
+ 15 twice the width you want to see.
+ 16 
+ 17 `paintOrder: "fill"` paints the glyphs first and the outline over them, as a
+ 18 plain canvas does with `fillText` then `strokeText`, and as melonJS did before
+ 19 20.13. Its inner half then covers the letters: on a small or thin face a
+ 20 `lineWidth` of 3 already leaves them solid outline colour.
+ 21 
+ 22 ```js
+ 23 const style = {
+ 24     font: "sans-serif", size: 32,
+ 25     fillStyle: "#ffd23f", strokeStyle: "#1b1b1b", lineWidth: 6,
+ 26 };
+ 27 new Text(x, y, { ...style, text: "BEHIND" });                     // the default
+ 28 new Text(x, y, { ...style, paintOrder: "fill", text: "OVER" });   // the old look
+ 29 ```
+ 47 The outline's corners are set by `lineJoin`, the same property a canvas
+ 48 context has, and `miterLimit`:
+ 49 
+ 50 | setting | corners | use it for |
+ 51 |---|---|---|
+ 52 | `lineJoin: "round"` (default) | rounded: the outline is the same width all around | almost everything: HUDs, buttons, titles |
+ 53 | `lineJoin: "bevel"` | cut flat | a harder edge with no spikes |
+ 54 | `lineJoin: "miter"` | pointed, as a plain canvas strokes text | a sharp, angular look |
+ 55 | `miterLimit: n` (default 10) | with `"miter"` only: a corner pointing further than `n` half line widths is cut flat instead | taming the spikes `"miter"` grows on "A", "V", "M" |
+ 56 
+ 57 At the canvas default of 10, a thick `"miter"` outline grows spikes several
+ 58 times its width from sharp glyphs; 2 to 4 keeps the corners pointed and the
+ 59 spikes short:
+ 60 
+ 61 ```js
+ 62 const outline = {
+ 63     font: "sans-serif", size: 48,
+ 64     fillStyle: "#ffffff", strokeStyle: "#000000", lineWidth: 6,
+ 65 };
+ 66 new Text(x, y, { ...outline, text: "ROUND" });                    // the default
+ 67 new Text(x, y, { ...outline, lineJoin: "bevel", text: "BEVEL" });
+ 68 new Text(x, y, { ...outline, lineJoin: "miter", text: "MITER" }); // long spikes
+ 69 new Text(x, y, { ...outline, lineJoin: "miter", miterLimit: 3, text: "MITER 3" });
+ 70 ```
+ 71 
+ 72 Whichever you pick, the bake leaves room for it on all four sides, so no corner
+ 73 is cut and the label's position and bounds do not move; pointed corners just
+ 74 cost a bigger texture. Changed on a live label, they show from the next
+ 75 `setText()`.
+ 76 
- 145 | stroke | `strokeStyle` + `lineWidth` | none |
+ 190 | stroke | `strokeStyle` + `lineWidth`, behind or over the glyphs with `paintOrder`, corners with `lineJoin` / `miterLimit` | none |
+ 338 ### Which widget for what
+ 339 
+ 340 | you want | use | notes |
+ 341 |---|---|---|
+ 342 | a button with a label | `UITextButton` | drawn, or skinned from an atlas; `onClick` setting |
+ 343 | an image-only button or icon | `UISpriteElement` | a `UIBaseElement` holding a `Sprite` (`sprite`); override `onClick` |
+ 344 | an on/off option, or one of several | `UICheckbox` | a radio button with `group` |
+ 345 | a value in a range: volume, sensitivity | `UISlider` | horizontal or vertical |
+ 346 | health, shield, cooldown, loading | `ProgressBar` | not clickable; `value` redraws |
+ 347 | a frame, dialog or menu card | `UIPanel` | skin, title, layout, scrolling, drag, shadow |
+ 348 | your own clickable widget | extend `UIBaseElement` | pointer handlers, `enabled`, focus, bounce |
+ 349 | a group with nothing drawn | `Container` | set `floating` for screen space |
+ 350 
+ 351 ### A whole options menu
+ 352 
+ 353 Everything below in one place: a skinned panel that lays its widgets out
+ 354 as a column, a checkbox, a slider, two buttons, a drop shadow, a little
+ 355 bounce, and keyboard / gamepad play. Each piece has its own section after
+ 356 this.
+ 357 
+ 358 ```js
+ 359 import {
+ 360     UICheckbox, UIPanel, UISlider, UITextButton, audio, state,
+ 361 } from "melonjs";
+ 362 
+ 363 // one style object for every widget's text
+ 364 const FONT = { font: "kenpixel", fontSize: 14, labelFillStyle: "black" };
+ 365 
+ 366 const menu = new UIPanel(40, 40, {
+ 367     width: 360, height: 100,           // autoSize grows the height to fit
+ 368     skin: { atlas, region: "grey_panel", insetx: 10, insety: 10 },
+ 369     title: "OPTIONS", titleFont: "kenpixel", titleFontSize: 20,
+ 370     layout: "column", padding: 16, gap: 12, align: "stretch", autoSize: true,
+ 371     shadowColor: "#00000059", shadowOffsetY: 6, shadowBlur: 10, radius: 6,
+ 372 });
+ 373 
+ 374 const music = menu.addChild(new UICheckbox(0, 0, {
+ 375     ...FONT,
+ 376     labelOn: "Music ON", labelOff: "Music OFF",
+ 377     checked: settings.music,
+ 378     onChange: (on) => { settings.music = on; },   // user input only
+ 379 }));
+ 380 
+ 381 menu.addChild(new UISlider(0, 0, {
+ 382     ...FONT,
+ 383     label: "Volume", showValue: true,
+ 384     valueFormat: (v, ratio) => `${Math.round(ratio * 100)}%`,
+ 385     value: audio.getVolume(),
+ 386     onChange: (v) => audio.setVolume(v),           // while dragging
+ 387     onChangeEnd: () => saveSettings(),             // once, on release
+ 388 }));
+ 389 
+ 390 menu.addChild(new UITextButton(0, 0, {
+ 391     ...FONT, text: "Back", height: 40,
+ 392     hoverScale: 1.04, pressScale: 0.96,            // the bounce
+ 393     onClick: () => state.change(state.MENU),       // a completed click
+ 394 }));
+ 395 
+ 396 app.world.addChild(menu, 10);

This preview is limited. See the complete change on GitHub.

Compare on GitHub

Week of Oct 26, 2026 · Line counts unavailable

Before Not checked yet → After Network or broad shell

First observation → 05ed64b14baa

No earlier version was observed for comparison.

Compare on GitHub

View on GitHubAdd to my skills

Verified commit a41d6a328557d69a0441d79bbeb9cd6c430275b5

git clone --filter=blob:none --no-checkout https://github.com/melonjs/melonJS.git .hotskills/melonjs-melonjs && git -C .hotskills/melonjs-melonjs checkout --detach a41d6a328557d69a0441d79bbeb9cd6c430275b5 # Copy each selected skill directory from .hotskills/melonjs-melonjs into .claude/skills/<skill-name>/
git clone --filter=blob:none --no-checkout https://github.com/melonjs/melonJS.git .hotskills/melonjs-melonjs && git -C .hotskills/melonjs-melonjs checkout --detach a41d6a328557d69a0441d79bbeb9cd6c430275b5 # Copy each selected skill directory from .hotskills/melonjs-melonjs into .agents/skills/<skill-name>/
6.4kGitHub stars
12stars in 7 days
25skills in this repository
98commits in 30 days

Heat

4.3
  • Star growth, last 7 daysAhead of 52% of tracked repositories
  • Star growth, last 24 hoursAhead of 5% of tracked repositories
  • Rank movement, last 7 daysAhead of 57% of tracked repositories
  • Commits, last 30 daysAhead of 94% of tracked repositories
  • Accounts asking about it through MCP, last 7 daysNot available
  • Hot votes this weekAhead of 0% of tracked repositories

Heat never changes the order by stars, and sponsorship never touches it. How heat works

Stars over time

Aug 2, 2026Nov 5, 20266,441

Individual skills in this repository

How it works

melonjs

When your agent uses it

Use this skill FIRST for any melonJS task; it routes to the right specialised skill and lists the companion packages. melonJS is an open-source 2D, 2.5D and 3D HTML5 game engine running on WebGPU, WebGL 2 or Canvas. Covers Application setup, the scene graph, renderables, sprites, tilemaps, physics, audio, particles, shaders, and the 3D tier — perspective cameras, meshes and glTF scenes. Triggers on: melonjs, melonJS, me.game, Application, app.init, app.world, Renderable, Sprite, Stage, state.change, Camera2d, Camera3d, Mesh, tilemap, Tiled, TMX, ParticleEmitter, ShaderEffect, how do I make a game, HTML5 game engine.

What it loads into context

Frontmatter

177 tokens

Always available to the agent

SKILL.md

2,971 tokens

When the skill is activated

Reference documents

127,214 tokens

Loaded on request

Token counts are estimates. Scripts run separately; they are not counted as reference context.

The playbook

  1. How to use this skill
  2. Skill router
  3. Escape hatch: the full API index
  4. The three rules that catch everyone
  5. Companion packages
  6. Prefer built-in features

Files

  • ../../../../.claude-plugin/marketplace.jsonasset431 bytes
  • ../../../../.claude-plugin/marketplace.jsonasset431 bytes
  • ../../../../.claude-plugin/plugin.jsonasset1,760 bytes
  • ../melonjs-20-migration/SKILL.mddoc7,594 bytes
  • ../melonjs-3d-assets/SKILL.mddoc26,460 bytes
  • ../melonjs-3d/SKILL.mddoc56,504 bytes
  • ../melonjs-audio/SKILL.mddoc26,478 bytes
  • ../melonjs-camera-and-drawing/SKILL.mddoc20,373 bytes
  • ../melonjs-deployment/SKILL.mddoc4,577 bytes
  • ../melonjs-display-and-scaling/SKILL.mddoc19,677 bytes
  • ../melonjs-effects-and-shaders/SKILL.mddoc39,462 bytes
  • ../melonjs-events/SKILL.mddoc7,173 bytes
  • ../melonjs-getting-started/SKILL.mddoc7,012 bytes
  • ../melonjs-input/SKILL.mddoc18,082 bytes
  • ../melonjs-lighting/SKILL.mddoc10,771 bytes
  • ../melonjs-loading-assets/SKILL.mddoc22,006 bytes
  • ../melonjs-math/SKILL.mddoc13,549 bytes
  • ../melonjs-particles-and-trails/SKILL.mddoc20,859 bytes
  • ../melonjs-performance/SKILL.mddoc19,442 bytes
  • ../melonjs-physics/SKILL.mddoc42,524 bytes
  • ../melonjs-plugins/SKILL.mddoc9,478 bytes
  • ../melonjs-renderables/SKILL.mddoc15,438 bytes
  • ../melonjs-renderer-backends/SKILL.mddoc9,678 bytes
  • ../melonjs-scenes-and-state/SKILL.mddoc17,964 bytes
  • ../melonjs-sprites-and-animation/SKILL.mddoc8,296 bytes
  • ../melonjs-tilemaps/SKILL.mddoc24,480 bytes
  • ../melonjs-ui-and-text/SKILL.mddoc54,522 bytes
  • SKILL.mdcore11,519 bytes
  • SKILL.mdcore11,519 bytes
  • SKILL.mdcore11,519 bytes
Read SKILL.md

# melonJS

Entry point for the melonJS skill collection. melonJS is an open-source HTML5
game engine for indie developers, covering 2D, 2.5D and 3D — perspective and
orthogonal cameras, GPU-accelerated tilemap rendering, post-processing effects,
custom shaders, 3D meshes, polygon-accurate physics and modern Tiled workflows.
It runs on WebGPU, WebGL 2 or Canvas with automatic fallback, and has no runtime
dependencies.

## How to use this skill

1. Find the specialised skill below that matches the task and follow it.
2. If the task involves anything 3D, read `melonjs-3d` **first** — melonJS's
   coordinate conventions are the inverse of OpenGL's, so untutored 3D instincts
View the complete source on GitHub

Skills included

  • melonjs
  • melonjs-20-migration
  • melonjs-3d
  • melonjs-3d-assets
  • melonjs-audio
  • melonjs-camera-and-drawing
  • melonjs-deployment
  • melonjs-display-and-scaling
  • melonjs-effects-and-shaders
  • melonjs-events
  • melonjs-getting-started
  • melonjs-input
  • melonjs-lighting
  • melonjs-loading-assets
  • melonjs-math
  • melonjs-particles-and-trails
  • melonjs-performance
  • melonjs-physics
  • melonjs-plugins
  • melonjs-renderables
  • melonjs-renderer-backends
  • melonjs-scenes-and-state
  • melonjs-sprites-and-animation
  • melonjs-tilemaps
  • melonjs-ui-and-text

Repository facts

License
MIT
Created
Apr 11, 2011
Last pushed
Nov 6, 2026
Forks
668
Open issues
14
Project activity
Active

Rank over the last 12 weeks

Now #225 · best #168 · down 25 in 30 days

  1. #221 on Oct 30
  2. #222 on Oct 31
  3. #223 from Nov 1 to Nov 2
  4. #225 on Nov 3
  5. #224 from Nov 4 to Nov 5
  6. #225 since Nov 6
51 earlier periods
  1. #162 on Aug 2
  2. #163 from Aug 7 to Aug 8
  3. #162 on Aug 9
  4. #167 on Aug 10
  5. #168 from Aug 13 to Aug 16
  6. #170 on Aug 17
  7. #173 on Aug 18
  8. #172 on Aug 21
  9. #168 on Aug 22
  10. #174 on Aug 23
  11. #173 from Aug 24 to Aug 25
  12. #174 on Aug 26
  13. #177 on Aug 27
  14. #183 on Sep 1
  15. #179 on Sep 2
  16. #185 on Sep 3
  17. #181 on Sep 4
  18. #182 on Sep 5
  19. #182 on Sep 7
  20. #185 on Sep 8
  21. #186 from Sep 10 to Sep 11
  22. #188 on Sep 12
  23. #189 on Sep 13
  24. #190 on Sep 14
  25. #189 on Sep 16
  26. #190 on Sep 17
  27. #192 on Sep 18
  28. #190 on Sep 19
  29. #193 on Sep 20
  30. #194 on Sep 22
  31. #195 on Sep 23
  32. #194 on Sep 24
  33. #193 on Sep 25
  34. #194 on Sep 26
  35. #197 on Oct 1
  36. #199 on Oct 2
  37. #196 on Oct 3
  38. #200 from Oct 7 to Oct 8
  39. #204 on Oct 9
  40. #201 on Oct 10
  41. #205 on Oct 12
  42. #208 on Oct 13
  43. #206 on Oct 15
  44. #204 on Oct 16
  45. #208 on Oct 18
  46. #209 on Oct 20
  47. #210 from Oct 21 to Oct 22
  48. #211 on Oct 24
  49. #214 on Oct 25
  50. #214 on Oct 28
  51. #218 on Oct 29

Before you install

Network or broad shell

Automated signals, not a guarantee

Checked Nov 5, 2026

Not fully analyzed: some files were too long, unreadable or missing. This never lowers the level.

Show 57 notes
  • .claude-plugin/marketplace.json:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • .claude-plugin/marketplace.json:1 Plugin-wide analysis incomplete; see repository notes
  • packages/melonjs/skills/melonjs-3d/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-3d-assets/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
  • packages/melonjs/skills/melonjs-audio/SKILL.md:1 Analysis incomplete: Referenced executable target unavailable; not analyzed
Show all 52 signals by file

.claude-plugin/marketplace.json

  • Network accessline 2
    "name": "melonjs", 	"description": "Skills for building games with melonJS, the open source 2D, 2.5D and 3D HTML5 game engine.", 	"owner": { "name": "melonJS", "url": "https://melonjs.org" },
  • Network accessline 3
    "description": "Skills for building games with melonJS, the open source 2D, 2.5D and 3D HTML5 game engine.", 	"owner": { "name": "melonJS", "url": "https://melonjs.org" }, 	"plugins": [
  • Network accessline 4
    "owner": { "name": "melonJS", "url": "https://melonjs.org" },

.claude-plugin/plugin.json

  • Network accessline 5
    "author": { 		"name": "melonJS", 		"url": "https://melonjs.org"
  • Network accessline 6
    "name": "melonJS", 		"url": "https://melonjs.org" 	},
  • Network accessline 7
    "url": "https://melonjs.org"
  • Network accessline 8
    }, 	"homepage": "https://melonjs.org", 	"repository": "https://github.com/melonjs/melonJS",
  • Network accessline 9
    "homepage": "https://melonjs.org",
  • Network accessline 10
    "repository": "https://github.com/melonjs/melonJS",

packages/melonjs/skills/melonjs-deployment/SKILL.md

  • Network accessline 38
    <script type="module">     import { Application } from "https://cdn.jsdelivr.net/npm/melonjs@20/+esm";
  • Network accessline 38
    <script type="module">     import { Application } from "https://cdn.jsdelivr.net/npm/melonjs@20/+esm";
  • Network accessline 39
    import { Application } from "https://cdn.jsdelivr.net/npm/melonjs@20/+esm";
  • Network accessline 39
    import { Application } from "https://cdn.jsdelivr.net/npm/melonjs@20/+esm";
  • Network accessline 76
    The thing that breaks first in both is asset loading: the app is served from `file://`, where `fetch()` cannot read local files. melonJS's loader falls back
  • Network accessline 76
    The thing that breaks first in both is asset loading: the app is served from `file://`, where `fetch()` cannot read local files. melonJS's loader falls back
  • Network accessline 77
    `file://`, where `fetch()` cannot read local files. melonJS's loader falls back
  • Network accessline 77
    `file://`, where `fetch()` cannot read local files. melonJS's loader falls back
  • Network accessline 78
    to XHR for that scheme, so engine-loaded assets work — but any `fetch()` in
  • Network accessline 78
    to XHR for that scheme, so engine-loaded assets work — but any `fetch()` in
  • Network accessline 104
    | blank page, syntax error in the console | browser older than the ES2022 target | | transpiling changes nothing | build config excludes `node_modules` | | assets 404 in a packaged native app | `fetch()` in game code — `file://` needs XHR |
  • Network accessline 104
    | blank page, syntax error in the console | browser older than the ES2022 target | | transpiling changes nothing | build config excludes `node_modules` | | assets 404 in a packaged native app | `fetch()` in game code — `file://` needs XHR |
  • Network accessline 105
    | transpiling changes nothing | build config excludes `node_modules` | | assets 404 in a packaged native app | `fetch()` in game code — `file://` needs XHR | | a built-in method is missing after transpiling | transpiling is syntax-only; pol
  • Network accessline 105
    | transpiling changes nothing | build config excludes `node_modules` | | assets 404 in a packaged native app | `fetch()` in game code — `file://` needs XHR | | a built-in method is missing after transpiling | transpiling is syntax-only; pol
  • Network accessline 106
    | assets 404 in a packaged native app | `fetch()` in game code — `file://` needs XHR |
  • Network accessline 106
    | assets 404 in a packaged native app | `fetch()` in game code — `file://` needs XHR |

packages/melonjs/skills/melonjs-lighting/SKILL.md

  • Network accessline 156
    See it running: [Normal Map example](https://melonjs.github.io/melonJS/examples/#/normal-map).
  • Network accessline 156
    See it running: [Normal Map example](https://melonjs.github.io/melonJS/examples/#/normal-map).

packages/melonjs/skills/melonjs-loading-assets/SKILL.md

  • Network accessline 325
    loader.setBaseURL("image", "data/img/"); loader.setBaseURL("audio", "data/sfx/"); loader.setBaseURL("*", "https://cdn.example.com/");   // every 2D type at once
  • Network accessline 325
    loader.setBaseURL("image", "data/img/"); loader.setBaseURL("audio", "data/sfx/"); loader.setBaseURL("*", "https://cdn.example.com/");   // every 2D type at once
  • Network accessline 327
    loader.setBaseURL("*", "https://cdn.example.com/");   // every 2D type at once
  • Network accessline 327
    loader.setBaseURL("*", "https://cdn.example.com/");   // every 2D type at once
  • Access to secretsline 382
    [Credential-related source omitted]
  • Access to secretsline 382
    [Credential-related source omitted]

packages/melonjs/skills/melonjs-particles-and-trails/SKILL.md

  • Network accessline 38
    Settings are easier to find by eye than by reasoning. The [Particle Editor](https://melonjs.github.io/melonJS/examples/#/particle-editor)
  • Network accessline 38
    Settings are easier to find by eye than by reasoning. The [Particle Editor](https://melonjs.github.io/melonJS/examples/#/particle-editor)
  • Network accessline 39
    [Particle Editor](https://melonjs.github.io/melonJS/examples/#/particle-editor)
  • Network accessline 39
    [Particle Editor](https://melonjs.github.io/melonJS/examples/#/particle-editor)

packages/melonjs/skills/melonjs-tilemaps/SKILL.md

  • Network accessline 9
    > melonJS's flagship content pipeline. Maps are authored in > [Tiled](https://www.mapeditor.org/) and loaded through the `level` director —
  • Network accessline 9
    > melonJS's flagship content pipeline. Maps are authored in > [Tiled](https://www.mapeditor.org/) and loaded through the `level` director —
  • Network accessline 10
    > [Tiled](https://www.mapeditor.org/) and loaded through the `level` director —
  • Network accessline 10
    > [Tiled](https://www.mapeditor.org/) and loaded through the `level` director —

packages/melonjs/skills/melonjs/SKILL.md

  • Network accessline 65
    <https://melonjs.github.io/melonJS/llms.txt>
  • Network accessline 108
    | Package | Use when | |---|---| | [`@melonjs/planck-adapter`](https://www.npmjs.com/package/@melonjs/planck-adapter) | You want full rigid-body dynamics — stacking, joints, realistic restitution. Pass it as the Application's `physic` setti
  • Network accessline 109
    |---|---| | [`@melonjs/planck-adapter`](https://www.npmjs.com/package/@melonjs/planck-adapter) | You want full rigid-body dynamics — stacking, joints, realistic restitution. Pass it as the Application's `physic` setting; the portable `rayca
  • Network accessline 110
    | [`@melonjs/planck-adapter`](https://www.npmjs.com/package/@melonjs/planck-adapter) | You want full rigid-body dynamics — stacking, joints, realistic restitution. Pass it as the Application's `physic` setting; the portable `raycast` and `q
  • Network accessline 111
    | [`@melonjs/matter-adapter`](https://www.npmjs.com/package/@melonjs/matter-adapter) | Same, backed by matter-js. Choose one adapter per game. |
  • Network accessline 112
    | [`@melonjs/tiled-inflate-plugin`](https://www.npmjs.com/package/@melonjs/tiled-inflate-plugin) | **Required** to load gzip-, zlib- or zstd-compressed Tiled maps. Without it, parsing such a map throws `No inflate function set`. |
  • Network accessline 113
    | [`@melonjs/spine-plugin`](https://www.npmjs.com/package/@melonjs/spine-plugin) | Skeletal animation authored in Spine, as a melonJS renderable. |
  • Network accessline 114
    | [`@melonjs/debug-plugin`](https://www.npmjs.com/package/@melonjs/debug-plugin) | An in-game debug panel — FPS, draw counts, collision-shape and bounds overlays. Register it after `app.init()`. |
  • Network accessline 115
    | [`@melonjs/gamepad-plugin`](https://www.npmjs.com/package/@melonjs/gamepad-plugin) | On-screen touch controls for phones and tablets: sticks and buttons reported as a gamepad, so the game's `bindGamepad` bindings drive it; fixed or floati
  • Network accessline 116
    | [`@melonjs/capacitor-plugin`](https://www.npmjs.com/package/@melonjs/capacitor-plugin) | Wrapping a game as a native iOS/Android app: it bridges Capacitor's lifecycle — pause/resume, the hardware back button, orientation lock, splash scre
  • Network accessline 117
    | [`create-melonjs`](https://www.npmjs.com/package/create-melonjs) | Scaffolding a new project — start here rather than assembling a build by hand. |

How the signals fit together

Facts from a static read of the files at the pinned commit. They describe what the code does, not why.

Some files could not be read; the facts below still hold.

Signal confidence: 43 facts

Works with

Agent compatibility, from the files in this repository
AgentStatusEvidenceInstall to
Claude CodeNativeManifest .claude-plugin/marketplace.json.claude/skills/<skill-name>/
CodexNativeManifest AGENTS.md.agents/skills/<skill-name>/
CursorCompatibleStandard SKILL.md packages/melonjs/skills/melonjs/SKILL.md.cursor/skills/<skill-name>/
Gemini CLICompatibleStandard SKILL.md packages/melonjs/skills/melonjs/SKILL.md.gemini/skills/<skill-name>/
GitHub CopilotCompatibleStandard SKILL.md packages/melonjs/skills/melonjs/SKILL.md.github/skills/<skill-name>/
OpenCodeCompatibleStandard SKILL.md packages/melonjs/skills/melonjs/SKILL.md.opencode/skills/<skill-name>/
  • Native The repository ships a manifest, folder or frontmatter field for this agent.
  • Compatible A standard SKILL.md that this agent can read.
  • Unknown No evidence either way.

Derived automatically from paths, manifests and frontmatter. Not tested on each agent.

Add this badge to your README

It updates every night and links to this page.

melonjs/melonJS: #225 on hotskills, 6.4k stars

[![melonjs/melonJS on hotskills](https://hotskills.dev/badge/melonjs-melonjs.svg)](https://hotskills.dev/skill/melonjs-melonjs/)