# Melonjs Math

Use this skill for vector and matrix maths in melonJS, Vector2d, Vector3d, their...

- Repository: https://github.com/melonjs/melonJS (6430 stars)
- Category: Engineering
- License: MIT
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add melonjs/melonJS --skill melonjs-math -g -a claude-code -y`
Codex: `npx skills add melonjs/melonJS --skill melonjs-math -g -a codex -y`
Cursor: `npx skills add melonjs/melonJS --skill melonjs-math -g -a cursor -y`
Gemini CLI: `npx skills add melonjs/melonJS --skill melonjs-math -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add melonjs/melonJS`

## Description

Use this skill for vector and matrix maths in melonJS, Vector2d, Vector3d, their observable variants, Matrix2d, Matrix3d, and the math utility functions. Covers which methods mutate and which allocate, the vector pool and when clone() is the wrong tool, interpolation with lerp, slerp, damp and moveTowards, and the argument defaults that silently drop a component. Triggers on: Vector2d, Vector3d, ObservableVector2d, ObservableVector3d, Matrix2d, Matrix3d, vector, vector maths, dot, cross, normalize, length, distance, angle, lerp, slerp, damp, moveTowards, project, clamp, degToRad, radToDeg, clone, vector pool, vector3dPool, setMuted, setCallback, my vector is wrong, z is zero, scale ignores z., moveTowards ignores z, projectN, currentTransform, setTransform, matrix, transform matrix, column-major, val, Float32Array, basis matrix, orientation matrix, Noise, getNoise1d, flicker, wobble.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

# Vectors, matrices and maths

## The one rule that explains most surprises

**Methods mutate `this` and return it. They do not return a new vector.**

```js
const a = new Vector3d(1, 0, 0);
const b = new Vector3d(0, 1, 0);

a.cross(b);        // a IS NOW the cross product; the original a is gone
a.add(b).normalize().scale(3, 3, 3);   // chains, all in place
```

That is what makes the API allocation-free, and it is the opposite of what a
functional style expects. If you need to keep the original, copy into
something you own first:

```js
out.copy(a).cross(b);    // a untouched, nothing allocated
```

**The exceptions are named.** A handful of methods return a NEW vector, and
each has a `*Self` sibling that does not:

| allocates a new vector | mutates in place |
|---|---|
| `negate()` | `negateSelf()` |
| `floor()` | `floorSelf()` |
| `ceil()` | `ceilSelf()` |
| `clamp(low, high)` | `clampSelf(low, high)` |
| `clone()` | `copy(v)` |

So `v.negate()` in a per-frame loop allocates every frame and `v.negateSelf()`
does not. The names are the only clue; nothing in the call site shows it.

## `clone()` comes from a pool, and only pays off if you give it back

`clone()` is `vector3dPool.get(...)`. A pool is fast when instances come back:
`get()` takes a free one and resets it. Nothing comes back on its own, so a
clone that is simply dropped makes `get()` miss, construct, and write two
registry entries.

Measured, 3M operations:

| | |
|---|---|
| `out.copy(a).cross(b)` | 39ms |
| `new Vector3d(ax, ay, az).cross(b)` | 37ms |
| `a.clone().cross(b)` | **2919ms** |

So:

- **Transient value inside a function?** Use a module-level scratch vector, or
  `new Vector3d()`. Not `clone()`.
- **Keeping it, then done with it?** `clone()` and `vector3dPool.release(v)`
  when finished. Releasing is what makes the pool a pool.
- **Writing a helper that returns a vector?** Take an optional `out`:

```js
const cross = (a, b, out = new Vector3d()) => out.copy(a).cross(b);
```

The rest: https://github.com/melonjs/melonJS/blob/HEAD/packages/melonjs/skills/melonjs-math/SKILL.md
