Mathsm - Extensible Math Library (Vectors, Matrices, Transformations)

mathsm

Github | 0.0.1


What is mathsm?

mathsm is a extensible, modular and powerful math library that goes far beyond what Roblox currently offers.

Whether you’re creating physics simulations, neural networks, custom rendering systems, or anything in between and beyond mathsm can help you.

Features

  • Full Vector Support
    • Dot/cross products, normalization, projection, transformations, and support for homogeneous coordinates.
  • Full Matrix Support
    • all operations, inverses, RREF, ranks, LU decomposition, determinant, transpose and more.
  • Transform Utilities
    • Create transformation matrices from position, rotation and scale.

Examples

Example 1 - Transforming a Vector

In this example, we’ll create a simple transformation matrix that translates a vector by (10, 0, 0), rotates it 90degs around the Z-axis and lastly scales it (no scalling in this example so it’s (1, 1, 1)

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mathsm = require(ReplicatedStorage.mathsm.Initializer)
local types = require(ReplicatedStorage.mathsm.Types)

type vec = types.Vec<{number}>
type matrix = types.Matrix<{number}>

-- Original vector (Vec3)
local Vec: vec = mathsm.Vec.new({2, 4, 1})

-- Position offset (translation)
local positionVec: vec = mathsm.Vec.new({10, 0, 0})

-- Rotation 90° around Z-axis
local rotationMatrix: matrix = mathsm.Matrix.new({
    { 0, -1, 0, 0 },
    { 1,  0, 0, 0 },
    { 0,  0, 1, 0 },
    { 0,  0, 0, 1 }
})

-- Scaling (1,1,1 means no scale change)
local scaleVec: vec = mathsm.Vec.new({1, 1, 1}, "Scalevec")
-- Create a transformation matrix
local transform = mathsm.Matrix.fromTransform(positionVec, rotationMatrix, scaleVec)

-- Convert Vec3 to homogeneous coordinates, then transform it
local transformed = transform * Vec:toHomogeneous()

-- Print result: should now be moved + rotated
print(transformed) -- {6, 2, 1, 1} - in homogeneous 

Example 2 - Finding the Closest Point on a Line

Let’s say you have a moving player and a ray beam defined by two points a and b.
You want to find the closest point on that beam to the player’s position.

This could be useful for detecting if a player is near a path, AI navigation and etc..

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local mathsm = require(ReplicatedStorage.mathsm.Initializer)
local types = require(ReplicatedStorage.mathsm.Types)

type vec = types.Vec<{number}>

-- Ray segment from point A to B
local A: vec = mathsm.Vec.new({0, 0, 0}) -- player
local B: vec = mathsm.Vec.new({10, 0, 0}) -- ray

-- Player position (moving around)
local playerPos: vec = mathsm.Vec.new({7, 3, 0})

-- Vector from A to B
local AB = B - A

-- Vector from A to player
local AP = playerPos - A

-- Project AP onto AB to find closest point
local projection = AP:project(AB)

-- Closest point = A + projection
local closestPoint = A + projection

-- Print result
print("Closest Point:", closestPoint:value()) -- (7, 0, 0)

Benchmarks

mathsm did well through all the tests

Benchmark Total Time (s) Avg Time (s) Runs
Vec Dot 1.137325 0.00000114 1,000,000
Vec Mul 4.649543 0.00000465 1,000,000
Vec Div 29.306327 0.00002931 1,000,000
Vec Sub 9.767209 0.00000977 1,000,000
Vec Add 9.815857 0.00000982 1,000,000
Vec3 Cross 30.207469 0.00003021 1,000,000
Vec Lerp 13.671228 0.00001367 1,000,000
Vec Project 17.218875 0.00001722 1,000,000
Vec Distance 14.720008 0.00001472 1,000,000
Vec Angle-Between 10.104772 0.00001010 1,000,000
Vec IsParallel 42.740762 0.00004274 1,000,000
Vec Equals 14.320398 0.00001432 1,000,000
Vec ToHomogeneous 9.155959 0.00000916 1,000,000
Matrix Mul 4x4 (Vec Bench) 43.355540 0.00004336 1,000,000
Matrix Addition 5.012368 0.00005012 100,000
Matrix Subtraction 1.072155 0.00001072 100,000
Matrix Multiplication (4x4) 0.188731 0.00001887 10,000
Matrix Scalar Division 0.120677 0.00001207 10,000
Matrix Determinant 0.167449 0.00003349 5,000
Matrix Inverse 1.375827 0.00068791 2,000
Matrix RREF 0.057053 0.00002853 2,000
LU Decompose 0.067873 0.00003394 2,000
Forward Substitution 0.038110 0.00000381 10,000
Backward Substitution 0.039185 0.00000392 10,000
Matrix Transpose 0.069743 0.00000697 10,000
Matrix Rotate 90 0.401704 0.00004017 10,000
Matrix Clone 0.741801 0.00000742 100,000
Matrix Trace 0.016283 0.00000163 10,000
Matrix Fill 0.015259 0.00000153 10,000
Matrix Is Zero 0.055135 0.00000055 100,000
Matrix Flatten 0.050093 0.00000501 10,000
Matrix Get Rank 0.058860 0.00002943 2,000
Matrix Shape 0.205024 0.00000205 100,000
Matrix Is Square 0.246277 0.00000246 100,000

All benchmarks were done without utilizing parallel luau as 0.0.1 does not support it.

Installation

Currently the quickest and easiest way to get your hands on mathsm is by installing the latest release 0.0.1 from the github releases here

Developer notes

  • Solo Project. This is a one-person project, maintained entirely by me.
  • Driven by Passion. mathsm was created out of pure interest in math, programming, and Roblox development.
  • Ongoing Development. I actively improve and expand mathsm whenever I have time, so expect updates, optimizations, and new features in future releases.
17 Likes

Oh sick I was actually looking for something like this lol

3 Likes

Bless your heart, I was lookin for somethin like this last week!!!

1 Like

I believe the GitHub repository isn’t up to date. I’ll work on fixing the bugs and adding a few more utilities. All coming in 0.0.2.

Just a quick note: theres a module called RuntimeDiagnostics under the folder Debugging which currently isn’t required or being used. This is due to that I made it later on when I had finished most of the code so I had no time to port it into development, however I might be using it for the next update for better and easier error messages.

edit: Thank you so much for all the positive feedbacks.

Luaus new builtin vector type and vector library is wayyyy faster and more memory efficient than this, but the matrix library is still pretty useful. Would be nice of you replaced all usages of tables with varargs/just parameters & luau vectors, should make the performance much better

1 Like

Sure, Luau’s Vector2/3 are fast since they’re implemented in C++ under the hood. But they are immutable and limited compared to mathsm vectors.

Not to mention, I haven’t implement parallel luau code yet which should further improve performance.

1 Like

Immutable doesn’t really matter because each vector is 1 TValue and all xyz values are stored on the stack, which means luau doesn’t have to allocate and use slow heap memory for it. Also, vector.create(x, y, z?) is actually way faster than most if not all of mathsm’s vector operations, because

  1. Your library functions aren’t fastcalled so they have to be fetched from the module everytime you call 1 of the functions, this doesn’t matter for most libraries but with vectors you want the most speed possible
  2. Quite a few of your functions use tables (which allocate heap memory) instead of just normal parameters/varargs
  3. You use function metamethods for implementing arithmetic operations, whereas since vectors are native datatypes they have the operations for them (*, +, -, etc) defined for them by the interpreter, which makes them much faster.
  4. vectors are uniquely optimized when lowered into native, this again makes vectors orders of magnitudes faster than mathsm vecs
  5. It uses simd, can’t get any faster than that :V

Overall I’m not saying your library is useless, just that it could be alot faster if you used vectors. Stuff like mathsm.Vec.project() could be implemented with vectors to make it faster, same with mathsm.Vec.lerp() too since vectors have *, + and - defined for them

I don’t think you really understand how parallel luau works, not to be rude. You have to switch contexts and the speed tradeoff is only worth it when an algorithm can be hugely optimized when working in parallel, that’s stuff like terrain gen, map gen, etc, not vector operations.

And to drive the point home, heres a benchmark comparing i think every single mathsm vector function vs the luau vector library


local mathsm = require(script.mathsm.Initializer)
return {
	Functions = {
		["vectorlib"] = function(p: any)
			p.profilebegin("vector.create * 2")
			local v1, v2 = vector.create(100, 200, 300), vector.create(1020302030, 222, 210)
			p.profileend()

			p.profilebegin("vector.add")
			local a = v1 + v2
			p.profileend()

			p.profilebegin("vector.sub")
			local s = v1 - v2
			p.profileend()

			p.profilebegin("vector.mul")
			local m = v1 * 2.5
			p.profileend()

			p.profilebegin("vector.div")
			local d = v1 / 3
			p.profileend()

			p.profilebegin("vector.dot")
			local dot = vector.dot(v1, v2)
			p.profileend()

			p.profilebegin("vector.cross")
			local cross = vector.cross(v1, v2)
			p.profileend()

			p.profilebegin("vector.magnitude")
			local mag = vector.magnitude(v1)
			p.profileend()
			
			p.profilebegin("vector.lerp (0.12)")
			local lerp = v1 + (v2 - v1) * 0.12
			p.profileend()

			p.profilebegin("vector.angle")
			local angle = vector.angle(v1, v2)
			p.profileend()

			p.profilebegin("vector.normalize")
			local norm = vector.normalize(v1)
			p.profileend()

			p.profilebegin("vector.distance")
			local dist = vector.magnitude(v2 - v1)
			p.profileend()
		end,

		["mathsm.Vec"] = function(p: any)
			p.profilebegin("Vec.new * 2")
			local v1, v2 = mathsm.Vec.new({100, 200, 300}), mathsm.Vec.new({1020302030, 222, 210})
			p.profileend()

			p.profilebegin("Vec.add")
			local a = v1:add(v2)
			p.profileend()

			p.profilebegin("Vec.sub")
			local s = v1:subtract(v2)
			p.profileend()

			p.profilebegin("Vec.mul")
			local m = v1:multiply(2.5)
			p.profileend()

			p.profilebegin("Vec.div")
			local d = v1:divide(3)
			p.profileend()

			p.profilebegin("Vec.dot")
			local dot = v1:dot(v2)
			p.profileend()

			p.profilebegin("Vec.cross")
			local cross = v1:cross(v2)
			p.profileend()

			p.profilebegin("Vec.magnitude")
			local mag = v1:magnitude()
			p.profileend()

			p.profilebegin("Vec.normalize")
			local norm = v1:normalize()
			p.profileend()

			p.profilebegin("Vec.lerp")
			local lerp = v1:lerp(v2, 0.5)
			p.profileend()
			
			p.profilebegin("Vec.distance")
			local dist = v1:distance(v2)
			p.profileend()
		end,
	}
}

(5000 repeats)

Also, ontop of the performance benefits, since vectors are again the size of 1 TValue and uses no heap memory, this means they are insanely memory efficient.

I do get your point. But I do not believe you can do all the functionality built-in mathsm with just raw Luau’s vectors. it’s a bit low on speed but gives you an edge over normal Vectors.

What functionality does mathsm.Vec provide that vector doesn’t? I can’t find it, no offense.

athar is right, especially about the parallel lua stuff. Parallel lua has pretty niche uses still. While the main idea of it is that it can speed up code, you don’t use parallel lua on things that are already fast. Its not some magical concept that makes code go faster, you treat it like a GPU. GPUs are terrible at doing long, complex calculations but are great at doing tons and tons of simple independent calculations in parallel, independent meaning calculations done on one thread are not affected by or required by any other thread. Using parallel lua on certain things can actually make them perform worse because of the overhead incurred from dispatching parallel threads, especially if each parallel thread must pass results back to the dispatching thread.

Still I think this module can be very helpful to people. If extreme performance isn’t a main focus then you’re fine not using roblox vectors, but I do agree with athar it could benefit from it.

1 Like

does it provide projection? compatibility with matrices? does it provide a reflect function? and more on

  1. Yes vectors indeed provide the operations needed to perform projection
  2. Yes indeed it is compatible with matrixes, aka CFrames in the context of Roblox
  3. Yes you can reflect vectors, it’s called the unary negative operator
  4. And for stuff like lerp you can again implement that using the base operations of vector, which is why i said your library isn’t made obsolete by vectors, i’m saying you can gain alot of speed by using them and making mathsm.Vec simply provide additional operations like lerp and project.

Well actually I was looking forward to reading more about parallel Luau but as far as I have read It’s all about dividing calculations over multiple threads.

It is that yes, but to do that you have to switch the context of your code from serial to parallel and then back if you want results. This takes up a huuuuuge amount of time considering all Roblox has is a messaging api and a slow SharedTableRegistry service which makes messaging between luau vms incredibly slow, which is why it is only used for heavy algorithms. Also, you are capped to like 3 cores on the client, and 1 on the server on Roblox, so it’s not like you can just keep offloading processing power to more cores.

This project as a whole was a challenge I did for myself. Making something pure mathematical and from scratch in Luau. In other words it’s more of a hobby project other than anything. So I haven’t relied on Luau’s built-in because I wanted to do better than them.

You quite literally cannot be faster than luau vectors, the operations for them are quite literally what amounts to a cpu context switch then a single instruction. You can make your library multiple orders of magnitude faster by using them.

I do understand I can not make them “faster”. I did try making them “better” even if it was on speed.

also I did mention they were built in C++ under the hood so they are pretty optimized

This exactly. Parallel lua is great for operations like terrain generation because each chunk generating thread only needs two inputs: the random seed and the coordinates of the chunk it is generating. Each thread can then chug along on its own calculating the resulting terrain with no further input from the dispatching thread.

1 Like

Also Do you all think this library is worth it? worth updating? is anyone going to use it?