Using xmath for Zero-Allocation Math in Defold
Prerequisite: Verify xmath Dependency
Before applying any guidance from this skill, you MUST confirm that the project uses xmath. Check the game.project file for a dependency URL containing thejustinwalsh/defold-xmath (e.g. dependencies#N = https://github.com/thejustinwalsh/defold-xmath/archive/...). Alternatively, check for the presence of xmath/ in the .deps/ directory.
If neither an xmath dependency in game.project nor a local xmath module is found, do NOT apply this skill. Inform the user that the project does not use xmath and suggest adding the dependency:
[project]
dependencies#N = https://github.com/thejustinwalsh/defold-xmath/archive/refs/heads/main.zip
Core Concept: In-Place Mutation to Eliminate Heap Allocations
Standard vmath creates a new Lua object on every operation, causing constant GC pressure in hot loops:
-- BAD: vmath allocates 3 new objects every frame
function update(self, dt)
local v = self.dir * 5 * dt -- alloc #1
local pos = go.get_position() -- alloc #2
local result = pos + v -- alloc #3
go.set_position(result)
end
xmath mutates an existing variable in place — the result is written into the first argument. You allocate once, reuse forever:
-- GOOD: xmath reuses pre-allocated variables, zero allocations per frame
go.property("dir", vmath.vector3(0, 1, 0))
local v = vmath.vector3() -- allocate ONCE at module scope
function update(self, dt)
local pos = go.get_position()
xmath.mul(v, self.dir, 5 * dt) -- writes into v
xmath.add(v, pos, v) -- writes into v
go.set_position(v)
end
Key Rules
- Pre-allocate scratch variables at module scope or in
init() — never inside update() or on_message().
- The output variable is always the first argument — this is the fundamental calling convention difference from
vmath.
- Functions return nothing — you cannot chain calls. Use a scratch variable at each step.
- Use
vmath to create initial objects — vmath.vector3(), vmath.vector4(), vmath.quat(), vmath.matrix4() to allocate scratch buffers, then use xmath to operate on them.
- Type polymorphism — functions like
xmath.lerp work for vector3, vector4, and quaternion based on the output argument type.
Optimization Pattern
-- Scratch variables — allocated once
local temp_v = vmath.vector3()
local temp_q = vmath.quat()
function update(self, dt)
-- Instead of: local dir = vmath.normalize(target - pos)
xmath.sub(temp_v, self.target, self.pos)
xmath.normalize(temp_v, temp_v) -- can use same variable as both input and output
-- Instead of: local rot = vmath.quat_rotation_z(angle)
xmath.quat_rotation_z(temp_q, self.angle)
-- Instead of: local rotated = vmath.rotate(rot, dir)
xmath.rotate(temp_v, temp_q, temp_v)
end
Full API Reference
All functions write the result into the first argument. No return values.
Vector Operations (vector3 / vector4)
| Function |
Equivalent |
Description |
xmath.add(out, v1, v2) |
out = v1 + v2 |
Add two vectors |
xmath.sub(out, v1, v2) |
out = v1 - v2 |
Subtract two vectors |
xmath.mul(out, v, n) |
out = v * n |
Multiply vector by scalar |
xmath.div(out, v, n) |
out = v / n |
Divide vector by scalar |
xmath.cross(out, v1, v2) |
out = cross(v1, v2) |
Cross product (vector3 only) |
xmath.mul_per_elem(out, v1, v2) |
out.x = v1.x * v2.x, ... |
Element-wise multiplication |
xmath.normalize(out, v) |
out = normalize(v) |
Normalize vector |
xmath.rotate(out, q, v) |
out = rotate(q, v) |
Rotate vector3 by quaternion |
xmath.vector(out) |
out = (0,0,0) |
Reset to zero vector |
Interpolation (vector3 / vector4 / quaternion)
| Function |
Equivalent |
Description |
xmath.lerp(out, t, v1, v2) |
out = lerp(t, v1, v2) |
Linear interpolation |
xmath.slerp(out, t, v1, v2) |
out = slerp(t, v1, v2) |
Spherical interpolation |
Quaternion Operations
| Function |
Description |
xmath.quat(out) |
Reset to identity (0, 0, 0, 1) |
xmath.conj(out, q) |
Conjugate of quaternion |
xmath.quat_axis_angle(out, axis, angle) |
Quaternion from axis + angle |
xmath.quat_basis(out, x, y, z) |
Quaternion from 3 basis vectors (vector3) |
xmath.quat_from_to(out, v1, v2) |
Rotation quaternion from v1 to v2 |
xmath.quat_rotation_x(out, angle) |
Rotation around X axis |
xmath.quat_rotation_y(out, angle) |
Rotation around Y axis |
xmath.quat_rotation_z(out, angle) |
Rotation around Z axis |
Matrix Operations (matrix4)
| Function |
Description |
xmath.matrix(out [, m1]) |
Reset to identity or copy from m1 |
xmath.matrix_axis_angle(out, axis, angle) |
Rotation matrix from axis + angle |
xmath.matrix_from_quat(out, q) |
Matrix from quaternion |
xmath.matrix_frustum(out, left, right, bottom, top, near, far) |
Frustum projection matrix |
xmath.matrix_inv(out, m) |
Matrix inverse |
xmath.matrix_look_at(out, eye, look_at, up) |
View matrix |
xmath.matrix4_orthographic(out, left, right, bottom, top, near, far) |
Orthographic projection |
xmath.matrix_ortho_inv(out, m) |
Orthographic inverse |
xmath.matrix4_perspective(out, fov, aspect, near, far) |
Perspective projection |
xmath.matrix_rotation_x(out, angle) |
Rotation around X axis |
xmath.matrix_rotation_y(out, angle) |
Rotation around Y axis |
xmath.matrix_rotation_z(out, angle) |
Rotation around Z axis |
xmath.matrix_translation(out, position) |
Translation matrix from vector3/vector4 |
1---2name: xmath-usage3description: Provides xmath API reference and in-place math optimization patterns for Defold. Use when writing performance-critical math code, optimizing vector/quaternion/matrix operations, or when the user mentions xmath, zero-allocation math, or reducing Lua GC pressure.4---56# Using xmath for Zero-Allocation Math in Defold78## Prerequisite: Verify xmath Dependency910Before applying any guidance from this skill, you MUST confirm that the project uses xmath. Check the `game.project` file for a dependency URL containing `thejustinwalsh/defold-xmath` (e.g. `dependencies#N = https://github.com/thejustinwalsh/defold-xmath/archive/...`). Alternatively, check for the presence of `xmath/` in the `.deps/` directory.1112If neither an xmath dependency in `game.project` nor a local xmath module is found, **do NOT apply this skill**. Inform the user that the project does not use xmath and suggest adding the dependency:13```14[project]15dependencies#N = https://github.com/thejustinwalsh/defold-xmath/archive/refs/heads/main.zip16```1718---1920## Core Concept: In-Place Mutation to Eliminate Heap Allocations2122Standard `vmath` creates a **new Lua object on every operation**, causing constant GC pressure in hot loops:2324```lua25-- BAD: vmath allocates 3 new objects every frame26function update(self, dt)27 local v = self.dir * 5 * dt -- alloc #128 local pos = go.get_position() -- alloc #229 local result = pos + v -- alloc #330 go.set_position(result)31end32```3334xmath **mutates an existing variable in place** — the result is written into the first argument. You allocate once, reuse forever:3536```lua37-- GOOD: xmath reuses pre-allocated variables, zero allocations per frame38go.property("dir", vmath.vector3(0, 1, 0))3940local v = vmath.vector3() -- allocate ONCE at module scope4142function update(self, dt)43 local pos = go.get_position()44 xmath.mul(v, self.dir, 5 * dt) -- writes into v45 xmath.add(v, pos, v) -- writes into v46 go.set_position(v)47end48```4950## Key Rules51521. **Pre-allocate scratch variables at module scope or in `init()`** — never inside `update()` or `on_message()`.532. **The output variable is always the first argument** — this is the fundamental calling convention difference from `vmath`.543. **Functions return nothing** — you cannot chain calls. Use a scratch variable at each step.554. **Use `vmath` to create initial objects** — `vmath.vector3()`, `vmath.vector4()`, `vmath.quat()`, `vmath.matrix4()` to allocate scratch buffers, then use `xmath` to operate on them.565. **Type polymorphism** — functions like `xmath.lerp` work for `vector3`, `vector4`, and `quaternion` based on the output argument type.5758## Optimization Pattern5960```lua61-- Scratch variables — allocated once62local temp_v = vmath.vector3()63local temp_q = vmath.quat()6465function update(self, dt)66 -- Instead of: local dir = vmath.normalize(target - pos)67 xmath.sub(temp_v, self.target, self.pos)68 xmath.normalize(temp_v, temp_v) -- can use same variable as both input and output6970 -- Instead of: local rot = vmath.quat_rotation_z(angle)71 xmath.quat_rotation_z(temp_q, self.angle)7273 -- Instead of: local rotated = vmath.rotate(rot, dir)74 xmath.rotate(temp_v, temp_q, temp_v)75end76```7778---7980## Full API Reference8182All functions write the result into the first argument. No return values.8384### Vector Operations (vector3 / vector4)8586| Function | Equivalent | Description |87|---|---|---|88| `xmath.add(out, v1, v2)` | `out = v1 + v2` | Add two vectors |89| `xmath.sub(out, v1, v2)` | `out = v1 - v2` | Subtract two vectors |90| `xmath.mul(out, v, n)` | `out = v * n` | Multiply vector by scalar |91| `xmath.div(out, v, n)` | `out = v / n` | Divide vector by scalar |92| `xmath.cross(out, v1, v2)` | `out = cross(v1, v2)` | Cross product (vector3 only) |93| `xmath.mul_per_elem(out, v1, v2)` | `out.x = v1.x * v2.x, ...` | Element-wise multiplication |94| `xmath.normalize(out, v)` | `out = normalize(v)` | Normalize vector |95| `xmath.rotate(out, q, v)` | `out = rotate(q, v)` | Rotate vector3 by quaternion |96| `xmath.vector(out)` | `out = (0,0,0)` | Reset to zero vector |9798### Interpolation (vector3 / vector4 / quaternion)99100| Function | Equivalent | Description |101|---|---|---|102| `xmath.lerp(out, t, v1, v2)` | `out = lerp(t, v1, v2)` | Linear interpolation |103| `xmath.slerp(out, t, v1, v2)` | `out = slerp(t, v1, v2)` | Spherical interpolation |104105### Quaternion Operations106107| Function | Description |108|---|---|109| `xmath.quat(out)` | Reset to identity `(0, 0, 0, 1)` |110| `xmath.conj(out, q)` | Conjugate of quaternion |111| `xmath.quat_axis_angle(out, axis, angle)` | Quaternion from axis + angle |112| `xmath.quat_basis(out, x, y, z)` | Quaternion from 3 basis vectors (vector3) |113| `xmath.quat_from_to(out, v1, v2)` | Rotation quaternion from v1 to v2 |114| `xmath.quat_rotation_x(out, angle)` | Rotation around X axis |115| `xmath.quat_rotation_y(out, angle)` | Rotation around Y axis |116| `xmath.quat_rotation_z(out, angle)` | Rotation around Z axis |117118### Matrix Operations (matrix4)119120| Function | Description |121|---|---|122| `xmath.matrix(out [, m1])` | Reset to identity or copy from m1 |123| `xmath.matrix_axis_angle(out, axis, angle)` | Rotation matrix from axis + angle |124| `xmath.matrix_from_quat(out, q)` | Matrix from quaternion |125| `xmath.matrix_frustum(out, left, right, bottom, top, near, far)` | Frustum projection matrix |126| `xmath.matrix_inv(out, m)` | Matrix inverse |127| `xmath.matrix_look_at(out, eye, look_at, up)` | View matrix |128| `xmath.matrix4_orthographic(out, left, right, bottom, top, near, far)` | Orthographic projection |129| `xmath.matrix_ortho_inv(out, m)` | Orthographic inverse |130| `xmath.matrix4_perspective(out, fov, aspect, near, far)` | Perspective projection |131| `xmath.matrix_rotation_x(out, angle)` | Rotation around X axis |132| `xmath.matrix_rotation_y(out, angle)` | Rotation around Y axis |133| `xmath.matrix_rotation_z(out, angle)` | Rotation around Z axis |134| `xmath.matrix_translation(out, position)` | Translation matrix from vector3/vector4 |135