Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ node_modules/
dist/*
!.placeholder
package-lock.json
docs/
# the generated API reference, not every directory named `docs`
/packages/melonjs/docs/
.turbo
51 changes: 42 additions & 9 deletions DOC_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ loader.preload([{ name: "player", type: "image", src: "player.png" }], () => {
| Feature | Description |
|---------|-------------|
| **Rendering** | WebGPU, WebGL 2 and Canvas 2D with automatic fallback — the same feature set on every backend |
| **3D** | Perspective [Camera3d](classes/Camera3d.html), mesh instancing, ground shadows, distance fog, point and spot lights, glTF/GLB and OBJ/MTL loading |
| **3D** | Perspective {@link Camera3d | Camera3d}, mesh instancing, ground shadows, distance fog, point and spot lights, glTF/GLB and OBJ/MTL loading |
| **Tiled Maps** | First-class [Tiled](https://www.mapeditor.org/) map editor support (TMX/JSON), with GPU-accelerated tile rendering for orthogonal maps |
| **Sprites** | Texture atlas, animation, TexturePacker & Aseprite support |
| **Physics** | Built-in SAT collision with gravity and friction, shape-level collision events, and a [PhysicsAdapter](interfaces/PhysicsAdapter.html) interface for Box2D (planck) or Matter.js |
| **Physics** | Built-in SAT collision with gravity and friction, shape-level collision events, and a {@link PhysicsAdapter | PhysicsAdapter} interface for Box2D (planck) or Matter.js |
| **Audio** | Web Audio API with format fallback, plus procedural tone and noise generation |
| **Input** | Keyboard, mouse, touch, gamepad |
| **Particles** | Configurable [ParticleEmitter](classes/ParticleEmitter.html), with a reference space so particles can be measured from the emitter, the world, or any container |
| **Particles** | Configurable {@link ParticleEmitter | ParticleEmitter}, with a reference space so particles can be measured from the emitter, the world, or any container |
| **Effects** | All thirteen CSS blend modes on every renderer, tinting, masking, and camera post-processing chains |
| **Custom Shaders** | Per-sprite [ShaderEffect](classes/ShaderEffect.html) carrying both GLSL and WGSL, so one effect runs on either GPU backend |
| **Custom Shaders** | Per-sprite {@link ShaderEffect | ShaderEffect} carrying both GLSL and WGSL, so one effect runs on either GPU backend |
| **UI** | Built-in UI components (buttons, text input, containers) |

## Common Tasks
Expand All @@ -67,7 +67,7 @@ import { level } from "melonjs";
// load a level by name (must be preloaded first)
level.load("myLevel");
```
See: [`level`](functions/level.load.html), [`TMXTileMap`](classes/TMXTileMap.html)
See: {@link level.load | level}, {@link TMXTileMap | TMXTileMap}

#### Create a sprite with animations
Create a sprite from a texture atlas (e.g. exported from TexturePacker or Aseprite) and define animation sequences from named frames.
Expand All @@ -82,7 +82,7 @@ const player = new Sprite(100, 100,
atlas.getAnimationSettings(["walk01.png", "walk02.png", "walk03.png"])
);
```
See: [`Sprite`](classes/Sprite.html), [`TextureAtlas`](classes/TextureAtlas.html)
See: {@link Sprite | Sprite}, {@link TextureAtlas | TextureAtlas}

#### Handle keyboard and gamepad input
Bind physical keys or gamepad buttons to named actions, then check those actions in your game logic.
Expand All @@ -97,7 +97,7 @@ if (input.isKeyPressed("jump")) {
// make the player jump
}
```
See: [`input`](modules/input.html)
See: {@link input | input}

#### Add physics and collision to a game object
Attach a physics body with a collision shape to any renderable. The engine handles gravity, velocity, friction, and collision detection automatically.
Expand All @@ -114,7 +114,7 @@ this.body.collisionType = collision.types.PLAYER_OBJECT;
this.body.setMaxVelocity(3, 15);
this.body.setFriction(0.4, 0);
```
See: [`Body`](classes/Body.html), [`collision`](modules/collision.html)
See: {@link Body | Body}, {@link collision | collision}

#### Apply a custom shader effect to a sprite
Apply a per-sprite fragment shader using `ShaderEffect`. You only need to write the color transformation — the vertex shader and texture sampling are handled automatically. Runs on both GPU backends — write the body once and it is realized as GLSL or WGSL for the active renderer — and is silently ignored in Canvas mode.
Expand All @@ -129,7 +129,40 @@ mySprite.addPostEffect(new ShaderEffect(renderer, `
}
`));
```
See: [`ShaderEffect`](classes/ShaderEffect.html), [`addPostEffect`](classes/Renderable.html#addposteffect)
See: {@link ShaderEffect | ShaderEffect}, {@link Renderable.addPostEffect | addPostEffect}

## Using this reference with an AI assistant

Three things here are meant for assistants as much as for people.

**`llms.txt`** — [melonjs.github.io/melonJS/llms.txt](https://melonjs.github.io/melonJS/llms.txt)
indexes every exported class, function, interface and type with a one-line
summary and a link to its page, and marks the deprecated ones. It is
regenerated on every docs build, so it never drifts from the release. Point an
assistant at that single URL rather than asking it to guess an API name.

**Copy page** — the button in the header above copies the page you are reading
as Markdown, with its canonical URL attached, or hands it straight to an
assistant. Useful when you want to ask about one class without the model
fetching half the reference.

**Skills** — the engine ships guidance files that teach an assistant its
conventions, and more usefully the mistakes that fail *silently* rather than
raising an error: a custom `draw()` that ignores `this.pos`, `isKinematic`
blocking pointer events, `.z` set after `addChild`. They are versioned with the
engine, so a copy matching your exact release always ships inside the package.

Install them into whatever assistant you use, with one command:

```bash
npx skills add https://github.com/melonjs/melonJS/tree/master/packages/melonjs/skills
```

That writes each agent's own convention — `.claude/skills/`, `.agents/skills/`,
`.windsurf/skills/` and around seventy others — so there is nothing to place by
hand. It installs from `master`; swap that for a release tag in the URL to pin.

They are plain Markdown — readable by any agent, or by you.

## Links

Expand Down
34 changes: 16 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,32 +331,30 @@ melonJS ships **skills** — guidance files that teach AI coding assistants the
engine's conventions and, more usefully, the mistakes that fail silently rather
than raising an error.

They are installed with the package, at `node_modules/melonjs/skills/`, and are
versioned with the engine — so the guidance always matches the release you have.
They are versioned with the engine and ship inside the package, at
`node_modules/melonjs/skills/` — so a copy matching the exact release you are
running is always on disk.

**Claude Code** — install as a plugin :

```
/plugin marketplace add melonjs/melonJS
```

or copy the skills into a project :
Install them into whatever assistant you use, with one command :

```bash
mkdir -p .claude/skills && cp -r node_modules/melonjs/skills/melonjs* .claude/skills/
npx skills add https://github.com/melonjs/melonJS/tree/master/packages/melonjs/skills
```

**Other agents** (Codex, Cursor, Gemini CLI, …) read an `AGENTS.md` from your own
project root. One ships ready to use — copy it across :
It detects the assistants in your project and writes each one's own convention
— `.claude/skills/`, `.agents/skills/`, `.windsurf/skills/` and around seventy
others — so there is nothing to place by hand. Add `-a claude-code -a cursor`
to target specific ones.

```bash
cp node_modules/melonjs/skills/AGENTS.md ./AGENTS.md
```

It points at the shipped skills, names the three rules that produce code which
runs and is wrong, and links the API index below. If you already have an
The set includes an `AGENTS.md` for anything following that convention (Codex,
Cursor, Gemini CLI); GitHub Copilot reads the same content from
`.github/copilot-instructions.md`. It names the three rules that produce code
which runs and is wrong, and links the API index below — if you already keep an
`AGENTS.md`, paste its sections into yours.

The command installs from `master`. To pin to the release you are running,
point it at that tag instead of `master` in the URL above.

The skills are plain markdown and can be read by any agent, or by a human.

For anything the skills do not cover, the complete API is indexed for agents at
Expand Down
1 change: 1 addition & 0 deletions packages/melonjs/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# copied from repo root during dist/publish
README.md
.docs.css
5 changes: 5 additions & 0 deletions packages/melonjs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@
- Mesh: `settings.vertexColors` and `setVertexColor(index, color)` give procedural geometry a per-vertex colour, multiplied into `tint`. Both batchers already wrote a per-vertex `aColor` on WebGL and WebGPU, but the array could only ever be built internally from a multi-material OBJ — so a mesh you built yourself had no way to reach it. `tint` is per *object*, so a terrain built as one mesh could only be tinted whole; this is what lets it fade toward the sky with distance, or darken in a crease, without splitting the mesh or writing a shader. Takes packed RGBA8 (`Uint32Array`, the form the batchers read) or one `Color` per vertex; a length that does not match the vertex count throws rather than mis-colouring the tail ([#1624](https://github.com/melonjs/melonJS/issues/1624))
- Mesh: normals are generated from the geometry when a `lit` mesh is built without them. A lit mesh with no normals had nothing for the shader to light with and rendered **fullbright** — asking for lighting and silently getting flat colour — and every hand-built mesh had to write the same accumulate-and-normalize loop first. Flat versus smooth is decided by the geometry rather than a flag: face normals accumulate into their vertices weighted by area, so shared vertices average into smooth shading while a triangle soup (each face owning its three vertices) resolves to the face normal and shades flat. An explicit `settings.normals` still wins, and an unlit mesh gets none

### Added
- Docs: the API reference carries the engine's own identity — logo, brand palette and favicon — and the header links out to the site, the wiki, the repository and Discord. A **Copy page** control hands the page you are reading to an assistant: it copies the page as Markdown with its canonical URL attached, or opens it directly in a chat. The landing page also gained a short section on using the reference with an AI assistant, covering that control, the `llms.txt` index and the shipped skills

### Fixed
- Docs: a pass over the reference build itself, which was emitting 311 warnings. Thirteen links on the landing page were written as relative HTML paths that TypeDoc neither copies nor rewrites, so they 404'd for readers; 78 `@param` blocks documented parameters their signature did not declare — abstract stubs written as `drawTile()` with four documented arguments, so the published page showed none of them — and 70 legacy JSDoc tags left over from the previous toolchain were ignored with a warning apiece. Down to 156, with no reader-facing link left broken
- Docs: engine internals no longer appear in the API reference or in consumers' autocomplete. Two different leaks with the same symptom: `@ignore` hides a member from the generated documentation but leaves it in the emitted `.d.ts`, and a member with no tag at all appears in both. So the pass lifecycle, texture retirement, batcher plumbing and a long tail of one-shot warning flags were all being offered as things to use, and the genuinely public surface was buried among them. Every `@ignore`d declaration is now also `@internal`, and the declaration stripper additionally drops class members whose name starts with `_` — this codebase's own convention for "not part of the API", applied consistently so it covers the ones nobody tagged and any added later. **1454 declarations left the published types and 89 left the reference, with no change to the public API** ([#1637](https://github.com/melonjs/melonJS/issues/1637))
- Audio: a game with a sound in its preload could hang on a blank loading screen, in four different ways. A clip that failed to load never reported anything at all unless the failure was a transport error: the listener required a numeric voice id, and a decode failure, a missing codec or a no-audio-support error all carry none — so a file served as HTML by an SPA rewrite, or a corrupt one, silently stalled the whole preload. With `stopOnAudioError = false` — "ignore audio errors and carry on" — a clip that did report called the loader's *error* callback, which for a promise-based preload is the reject: `Promise.all` rejected and the completion handler never ran. `preload: false` asks the backend not to fetch, so neither callback ever fired and the manifest waited on a clip that was never coming. And `loader.setOptions({ withCredentials: true })` reached the audio backend under a pre-20.3 name it does not read, so an authenticated request went out without its cookie and failed. Unloading a clip part-way through its retries also left its retry budget behind, so reloading the same name gave up on its first failure
- Audio: `xhrWithCredentials` still works on `Sound` itself, deprecated rather than removed, since games pass it straight through; `xhr: { withCredentials }` is the current spelling and wins when both are given
- Audio: a failed load no longer throws from a timer callback. The documented "throws" could not be caught by anyone — it landed on an empty stack as an uncaught global error — so the failure is now reported to the loader (which rejects the preload, the signal a caller actually catches) and logged with `console.error`. No working code can depend on the old form, since nothing could catch it
Expand Down
7 changes: 4 additions & 3 deletions packages/melonjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@
"build": "pnpm lint && tsx scripts/build.js && pnpm types",
"dist": "pnpm clean && pnpm lint && pnpm vitest run && pnpm build && pnpm doc && cp ../../README.md .",
"dist:publish": "pnpm clean && pnpm lint && pnpm build && pnpm doc && cp ../../README.md .",
"doc": "tsx scripts/check-doc-readme.ts && typedoc src/index.ts --tsconfig tsconfig.build.json --readme ../../DOC_README.md --hideGenerator --name 'melonJS' --navigation.includeCategories true --categorizeByGroup false && tsx scripts/generate-llms-txt.ts",
"doc:watch": "typedoc src/index.ts --tsconfig tsconfig.build.json --readme ../../DOC_README.md --hideGenerator --name 'melonJS' --navigation.includeCategories true --categorizeByGroup false --watch --skipErrorChecking --preserveWatchOutput --logLevel Error",
"doc": "tsx scripts/check-doc-readme.ts && npm run doc:css && typedoc && tsx scripts/generate-llms-txt.ts",
"doc:watch": "typedoc --watch --skipErrorChecking --preserveWatchOutput --logLevel Error",
Comment on lines 78 to +81
"serve": "serve docs",
"prepublishOnly": "pnpm dist:publish",
"clean": "tsx scripts/clean.ts",
"types": "tsc --project tsconfig.build.json && tsx scripts/strip-internal.ts",
"test:types": "tsc"
"test:types": "tsc",
"doc:css": "cat scripts/docs/brand.css scripts/docs/copy-page.css > scripts/docs/.docs.css"
}
}
Loading