Mathf
Scalar math helpers — the numbers-only functions you reach for inside a frame loop. Mathf is a
plain object of functions and constants; nothing to construct. Vector, quaternion, and matrix math
live on Vec2 / Vec3, Quat, and Mat4, not here.
At a glance
class Chase extends Aspect<'chase'> {
target?: Node
follow = Mathf.smoothDamp([0, 0, 0], 0.15) // stateful damper: one per aspect, never per frame
onAttach() { this.follow.setValue([...this.node.position]) } // start from where the node is
update(dt: number) {
if (this.target) this.node.position = this.follow([...this.target.position], dt)
}
}
const scene = new Scene()
const hero = Mesh.sphere({ material: Material.lit({ color: '#4f7cff' }), position: [3, 0, -5] })
const buddy = Mesh.sphere({ material: Material.lit({ color: '#e33' }) }).aspect(Chase, { target: hero })
scene.add(hero, buddy)
scene.open()
const hp = Mathf.clamp(120, 0, 100) // 100
const fill = Mathf.lerp(0, 200, Mathf.inverseLerp(0, 50, 20)) // 80: 20 of 50 → 40 % of 200
const angle = Mathf.repeat(370, 360) // 10
const zoom = Mathf.damp(1, 2, 10, 1 / 60) // one 60 Hz frame of a frame-rate-independent easeConstants & angle conversion
Mathf.PI // Math.PI
Mathf.TAU // 2π — a full turn in radians
Mathf.EPSILON // 1e-6
Mathf.DEG2RAD // π / 180
Mathf.RAD2DEG // 180 / π
Mathf.deg2rad(deg): number
Mathf.rad2deg(rad): numberDEG2RAD / RAD2DEG also exist as bare compile-time globals (no Mathf. prefix) —
see conventions for which APIs take degrees vs radians.
Ranges & interpolation
Mathf.clamp(v, min, max): number
Mathf.clamp01(v): number // clamp into [0, 1]
Mathf.lerp(a, b, t): number // a + (b − a)·t
Mathf.inverseLerp(a, b, v): number // where v sits in [a, b] as 0..1 (0 if a === b)
Mathf.remap(v, inMin, inMax, outMin, outMax): number // map v from one range onto another
Mathf.sign(x): number // -1, 0, or 1 (Math.sign)
Mathf.repeat(t, length): number // loop t into [0, length)
Mathf.pingPong(t, length): number // bounce t back and forth in [0, length]Easing toward a value
Mathf.moveTowards(a, b, maxDelta): number // step linearly, never overshoots
Mathf.damp(a, b, lambda, dt): number // exponential approach, frame-rate independentdamp is the frame-rate-safe replacement for a = lerp(a, b, 0.1) every frame: lambda is the
rate (higher = snappier; good defaults 5..15), dt is the frame's seconds (the dt of
update(dt)). It is stateless — pass last frame's value back in.
smoothDamp — critically-damped follow (Unity SmoothDamp)
Mathf.smoothDamp(initial: number, smoothTime, maxSpeed?): Damper<number>
Mathf.smoothDamp(initial: number[], smoothTime, maxSpeed?): Damper<number[]> // vec2/vec3 arrays
const damper = Mathf.smoothDamp([0, 0], 0.15)
damper(target, dt) // call every frame → the new eased value
damper.setValue(v) // snap internal state (teleport) — kills the rubber-bandReturns a stateful damper that tracks its own velocity, so motion eases in and out without
overshoot. smoothTime is roughly the settle time in seconds; maxSpeed (default Infinity)
caps the speed — for arrays the cap applies to the vector magnitude, not per component. The
array form returns a new array every call. Chase in At a glance is the usual
shape: the damper is a field of the aspect, called from update(dt).
create the damper once — a field, not a local of update. A fresh damper every frame
resets its velocity and defeats the smoothing. On teleports call setValue so the follower
doesn't chase from the old spot.
Random
Mathf.random(min?, max?): number // float in [min, max); default 0..1
Mathf.randomInt(min, max): number // integer, INCLUSIVE of both endsSee also
- Vec2 & Vec3 — vector lerp / distance / normalize live on the vector types
- Conventions —
dtis seconds; degrees-vs-radians rules