Skills

Melonjs Math

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

Install

npx skills add melonjs/melonJS --skill melonjs-math -g -a claude-code -y
Repository
melonjs/melonJS6.4k
Category
Engineering
Updated
3d ago
License
MIT
Context cost
Mediumabout 3,000 tokens
Runs scripts
No, instructions only

What it does

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.

Vectors, matrices and maths

The one rule that explains most surprises

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

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:

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 vectormutates 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:
const cross = (a, b, out = new Vector3d()) => out.copy(a).cross(b);
Read the whole SKILL.md on GitHub

More in Engineering