Our engine is built using the wonderful library written by Brandon Jones. The library is consistently at the top of the benchmark charts for JavaScript linear algebra and graphics libraries. That said, there are a number of interesting things to keep in mind while optimizing use of glMatrix that may not be immediately obvious to developers who've worked with more conventional C/C++ SIMD vector libraries.
glMatrix Basics
The first thing to know about glMatrix is that it is not an object-oriented library. The API is entirely procedural. It should feel very familiar to users of C-based vector libraries or developers who primarily work in Java or certain other high-level languages that lack operator overloading, but may feel very clunky to C++ developers.
Aspiring JavaScript graphics developers should thus first get accustomed to the procedural nature of glMatrix. In order to use a glMatrix vector, you first have to create a vector to operate on. This is done using the vec3.create() function:
1 | var ray = vec3.create(); |
Fairly straight forward. At this point, you can perform operations on the vector by passing it to one of the functions in the vec3 "namespace" like so:
1 2 3 | vec3.add(ray, [1, 1, 1]); // add <1,1,1> to ray vec3.scale(ray, 4); // scale the ray by 4 vec3.negate(ray); // negate the ray (point it in the opposite direction) |
That's also pretty simple. An important point to be aware of is that the vec3 operations take an optional destination parameter for most functions. This parameter (always the last one) is a vector to store the result in. If this is not given, most functions will treat the first parameter as the destination. So the commands above could be written (equivalently) as:
1 2 3 | vec3.add(ray, [1, 1, 1], ray); // add <1,1,1> to ray vec3.scale(ray, 4, ray); // scale the ray by 4 vec3.negate(ray, ray); // negate the ray (point it in the opposite direction) |
That functionality is important for optimizing your code. In many cases you will need to take a vector and perform some operations on it to get a new vector, all without modifying the original one.
Note, however, that the destination vector must already exist. That is, you must call vec3.create() to allocate a vector before passing it to any of the other vec3 functions.
References
By far the biggest problem that most developers will run into while simply trying to use the glMatrix library is that all of the vectors are technically objects, and hence are passed and returned by reference rather than by value. So the following function will have a side-effect on its parameter:
1 2 3 4 5 6 7 | function reflect(vec, line) { return vec3.subtract(vec3.scale(line, 2 * vec3.dot(vec, line) / vec3.dot(line, line)), vec); } var a = vec3.create([1, 1, 1]); var b = vec3.create([0, 1, 0]); var c = reflect(a, b); |
The code is simply, but it will modify the line vector parameter ('b' in the example above). The result then is that reflect() modifies its second parameter to be the reflected vector. Furthermore, in the code above, b and c will reference the same vector at the end.
That's really annoying. And there's not much you can do about it. Don't think to fault glMatrix for this behavior, either; it's pretty much a given in JavaScript (and, in fact, almost every other single language in common use, including Python, Java, and Ruby). Most languages do not allow developers to create user-defined types the way that C++ does, and so all custom types end up being fully heap-allocated objects that are always passed by reference.
It's just something you need to be aware of and to deal with.
A first hack at fixing the above problem might be code like this:
1 2 3 4 5 6 7 8 9 10 | function reflect(vec, line) { var rs = vec3.create(); vec3.scale(line, 2 * vec3.dot(vec, line) / vec3.dot(line, line), rs); vec3.subtract(line, rs, rs); return rs; } var a = vec3.create([1, 1, 1]); var b = vec3.create([0, 1, 0]); var c = reflect(a, b); |
That does indeed solve the reference problem. Neither of the parameters to reflect are modified now, and the test variable c will reference a unique vector rather than being an alias of b.
Unfortunately, this introduces a second problem. Each call to reflect will now create a new vector object. This leads us into our next section on garbage collection.
Garbage Collection
Most modern languages feature automatic garbage collection of some form, and JavaScript is no exception. This little marvel is what allows developers to not have to care about memory allocated and deallocation, taking care of everything for you. Unfortunately, a slightly more cynical (and for our purposes, accurate) way of looking at garbage collection is that it does everything behind your back without your approval.
With a library like glMatrix (or any of its alternatives), each vector is a JavaScript object. That means it is allocated on the heap and needs to be freed when it is no longer in use. And that means that the garbage collector has to do all kinds of work to find and free the object. It also means that the more vectors you create, the more memory will be used until the garbage collector is triggered. And thus, a tight loop performing some heavy math can trigger a garbage collection run, and that in turn can be deadly for a game's performance if there are a lot of little wasteful objects (like vectors!) floating around. Even the best collector in the world today can and will impose a noticeable bit of latency in any kind of intense real-time application, like a game or other interactive simulation.
It turns out that you just can't escape the need for a developer to understand and maintain tight control over memory usage, allocation, and deallocation, no matter how high level of a language the developer is working in. A garbage collector in our case is hurting more than it's helping, but thankfully we can do quite a bit to work around it.
What we need is to drastically reduce the number of vectors we create. That in turn means being very, very stingy about how often our code is ever allowed to call vec3.create(), or to use any array literal constructs (like [1, 1, 1] in the code above, which creates a new Array object every time it is executed).
Our sample reflect function above has a problem. Every time it is called, it creates a new vector. If we were to be using reflect in a loop operating over a few thousand rays, we'd end up creating a few thousand short-lived garbage vectors every time the loop is run. That's not good.
We can take a page right out glMatrix's own book to fix this, however.
1 2 3 4 5 6 7 8 9 10 | function reflect(vec, line, out) { vec3.scale(line, 2 * vec3.dot(vec, line) / vec3.dot(line, line), out); vec3.subtract(line, out, out); return out; } var a = vec3.create([1, 1, 1]); var b = vec3.create([0, 1, 0]); var c = vec3.create(); reflect(a, b, c); |
What we've done there is to add a third out parameter to the reflect function. We're not responsible for creating a vector to pass in, as the example test code does. At first blush this doesn't look like much of an improvement; all we've done is move the call to vec3.create() from one place to another.
However, let's say we were calling reflect in a loop:
1 2 3 4 5 6 7 | // rays is an array of objects with properties a, b, and c, all of which are vectors function reflect_all(rays, line) { for (var i = 0, e = rays.length; i != e; ++i) { var ray = rays[i]; reflect(ray.a, ray.b, ray.c); } } |
That new reflect_all function didn't require any new allocations to made at all. Assuming your data structures are designed with this requirement in mind, you can easily write quite a bit of useful code in a similar fashion. In other cases, you won't be storing all of your calculations, and you may just need a temporary vector or two. This can be done by creating these temporaries at the top of the function, outside of any loop, and then reusing those temporaries inside the loop, overwriting their previous values each time. You can reduce your vector allocations from dozens, hundreds, or thousands down to just several for each such loop.
Extreme Ninja-like Avoidance of Allocations
Sometimes you've got to have a few temporaries in a function, and sometimes that function is itself going to be called hundreds or thousands of times. Those temporaries continue to be a problem that need to be thwarted. What to do?
One option is to make the temporary variables globals, that are allocated once when your application starts up and never again. If you're using a module system like node.js's, this isn't too hard. If you're just using plain JavaScript source files, globals will "pollute" the global namespace and can be a real problem. You might want to name your temporaries something easy and simple, like tmpv1 and tmpv2, but then you'll easily get conflicts with other source files that create their own same-named temporaries. You can't easily just share temporaries between different modules, either, because you can't be sure of the scope of a global.
The scope problem is a big issue if you use global at all, so I'm going to cover that in a bit more detail. Let's say you have a function foo() that calls a function bar(). If both of these functions try to use the same global variable as a temporary value, something bad will happen: bar() will overwrite whatever value foo() had stored in that global. What this means is that any function using any particular temporary value cannot call or be called by any other function using those same variables. So globals just aren't safe.
There is a solution, however. It's to my thinking a little bit of a hack, but others may well consider it a beautiful exposition of the power of JavaScript. The trick is to create your functions as closures that bind their own temporary values.
Here's an example:
1 2 3 4 5 6 7 8 9 | var my_function = (function() { var tmp = vec3.create(); return function(rays) { for (var i = 0, e = rays.length; i != e; ++i) { // do stuff with tmp and rays[i] } }; )(); |
That code essentially gives the my_function() function its own private "global" variable, with semantics similar to what you'd have with a statically allocated variable in a language like C++. It allows each function to have the temporaries it needs, allocated once and once only, without any risk of collision with other functions.
Strongly note that there is still a scope problem that can arise in with this trick! These functions are not re-entrant, which means that they cannot be used for recursive algorithms at all. This means that if my_function() calls itself -- or calls into another function that eventually calls into my_function() -- then the outer invocation's data stored in its tmp variable will be clobbered by the inner invocation's data. This trick can thus only be used for functions that do pure computations, and not for any functions that invoke any kind of callback or further processing which might ever call the function again. In practice, this has not been a problem for any of our own code, but it's something to be very aware of should you choose to use this performance optimization.
When Nothing Else Works
Sometimes there's just no getting around the need to create a temporary. If this is in some outer function that's called a few times at most per frame, that's not much of an issue. If it's a bit of code that's going to be called many times, though, it's a huge problem.
In these cases, you just have to accept that you can't use glMatrix at all and use hand-rolled code. In a simple example, you might have to change this code:
1 2 3 | var v3 = vec3.create(v1); var d = dot(v1, v2); vec3.scale(v3, d); |
into this code:
1 2 3 4 5 | var x = v1.x, y = v1.y, z = v1.z; d = dot(v1, v2); x *= d; y *= d; z *= d; |
It's a lot uglier, and passing those kinds of "vectors" around as parameters is painful, and you really want to avoid doing this if you can, but sometimes you just don't have a choice.
Do note that there are very few alternatives to the ugly code above when you're in this situation. In particular, do not simply try to create an array or object of your own as a temporary. Any kind of value other than a boolean, number, or null is going to create a garbage-collected object, and you'd just be doing extra work for absolutely no gain over just using glMatrix directly.
Other Considerations Besides Vectors
The last paragraph of the previous section brings up an important point: every single array and object you create will eventually be garbage collected. Possibly even worse, every array or object you create has to be scanned by the garbage collector (in most implementations) just to see if any other objects are garbage or not. You want to keep your total object count reasonably low, and not create a lot of garbage objects.
A pattern we initially used in a few too many places in SONAR was to use an object as a way of passing optional parameters to a function. These functions often had a large number of optional parameters. Without the params object parameter, their callers looked like this:
1 2 3 | foo(null, null, null, null, true); foo(null, player, "physics", null, true); foo(25, player); |
That's hard to read (impossible to read if you don't know what the parameters do, in fact) and just as hard to write. Were JavaScript to offer proper named parameter support, this problem would go away quite nicely. Unfortunately, JavaScript lacks that capability, so we are stuck improvising.
1 2 3 | foo({ log : true }); foo({ object : player, component : "physics", log : true }); foo({ interval : 25, object : player }); |
Ignoring that we don't know what foo() actually does, that is quite a bit clearer. The boolean parameters have a lot clearer meaning, and when we wanted to just pass in that final parameter we didn't have to remember to pass in exactly four nulls first.
Unfortunately, this just doesn't fly for a game in a frequently-called code. See, now each call to foo() is creating a new temporary object to pass in for its parameters. We're back to garbage collection problems.
I called out this example because they can be almost invisible to many programmers. It can be sometimes hard to remember that the object (and array!) literals syntax in JavaScript create a new object each time the relevant line of code is executed.
There aren't many work-arounds. Simply be aware of the problem, and avoid trying to make short-cuts (even ones that really make the code cleaner) in performance-sensitive code. Also, help raise awareness in the general Web community as to why having a proper named parameter facility is super important even if the language supports "easy hacks" to simulate them; HTML5 game developers will thank you!
(And while you're at it, help everyone understand why having user-defined stack-allocated pass-by-value types for things like vectors is so critically important, too!)


Would allocating a large array (bigger than all the vectors you'd use) and then passing around indexes to that array work to do an end run around the GC? Allocating would just be changing values in the existing array, deallocating would be marking that index as unused, etc.
I wonder if that would net a performance boost.
Object Pool pattern is also a general good solution to handle your alloc / dealloc (as partially described by Benjamin).
Excellent article! It's always nice to see advice given by individuals that truly, deeply understand what optimization in a language like Javascript is all about!
[...] Optimizing Vector Usage in JavaScript (and other high-level languages, too) [...]