Library / SDK
JOML-CI/JOML avatar
JOML-CI/JOML

JOML: a Java math library built for OpenGL-style mutating transforms

A Java math library for OpenGL rendering calculations

847 stars111 forksJavaMIT

At a glance

What is it?
JOML is an MIT licensed linear algebra library for Java 3D work, where every vector and matrix method writes its result back into the object it was called on. That mutating design removes allocations from the transform path, and it also hands the caller ownership of every temporary object.
Who is it for?
JOML suits engineers writing Java rendering code that has to hit a frame budget without garbage collection hitches, and its mutating API plus OpenGL style post-multiplication removes the usual allocation and matrix order guesswork. It does not suit teams that want immutable value types, a left-handed pipeline by default, or a library with a documented thread safety contract, since none of those are described.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Every method writes its result back into the receiver

The defining trait of JOML is that it never quietly returns a fresh object. Operations are designed to modify the object on which they are invoked, and the caller supplies every working object involved. The README states this as the reason object allocations can be eliminated entirely, since the client controls what gets created and therefore what the garbage collector later has to reclaim. That matters in a render loop, where per-frame allocation produces small hickups rather than a clean failure.

The API reads like ordinary math notation once you know the rule. Here is the example the README gives for vector arithmetic, where each call overwrites the vector it was invoked on:

Java
Vector3f v = new Vector3f(0.0f, 1.0f, 0.0f);
Vector3f a = new Vector3f(1.0f, 0.0f, 0.0f);
// v = v + a
v.add(a);
// a = a x v
a.cross(v);
// a = a/|a|
a.normalize();

After `v.add(a)`, the variable `v` holds the sum, and the object named `a` is not a separate result, it is the same instance as before. If your code assumes `add` behaves like `java.util.List.add`, the assumption breaks. Nothing prevents a caller from writing `a.add(v)` and then reading `a` expecting the original value, because the original value is gone by design.

Most methods also accept an explicit destination object, which the README presents as the way to compute a result and a derived value, such as a view-projection matrix and its inverse, without overwriting the source.

Chained matrix calls follow OpenGL post-multiplication order

Matrix operations in JOML follow the convention established by OpenGL and the GLU library: each operation post-multiplies its own result onto the matrix it was called on. The practical consequence is that a chain of calls reads in the same order as the legacy OpenGL matrix stack did, so code ported from fixed function OpenGL keeps its familiar shape. The README offers a useful mental model, treat Java's dot operator as a matrix multiplication, and each method in a chain behaves as one multiplication applied to the running result.

The fluent style works because the chaining methods return the matrix itself:

Java
Vector3f v = ...;
new Matrix4f().translate(2.0f, 0.0f, 0.0f)
              .scale(0.5f)
              .transformPosition(v);
// v is now transformed by the specified transformation

The transform here scales all axes by 0.5 and translates x by 2.0, and the result is written into `v` through `transformPosition`. Note what the chain does not do: it does not return the matrix. The transformed point comes back through the vector argument, which keeps the pattern consistent with the mutating philosophy described earlier.

A separate family of methods sets a value regardless of what the target held before. The README uses `translation` as the example, setting the matrix to a translation instead of applying one on top of the existing content:

Java
Matrix4f m = new Matrix4f();
Vector3f point = new Vector3f(1.0f, 2.0f, 3.0f);
Vector3f offset = new Vector3f(1.0f, 0.0f, 0.0f);
...
m.translation(offset).transformPosition(point);

That distinction is the one most likely to cause a subtle bug. Post-multiplying methods accumulate, set methods replace, and the two look nearly identical in a call site.

Rotating about a point takes three chained operations

Rotation about an arbitrary center is not a single call, and JOML does not pretend otherwise. The approach is to translate the world so the center sits at the origin, rotate, then translate back. The README demonstrates this with the point (0, 4, 4) rotated around the x-axis using (0, 3, 4) as the rotation center:

Java
Vector3f center = new Vector3f(0.0f, 3.0f, 4.0f);
Vector3f pointToRotate = new Vector3f(0.0f, 4.0f, 4.0f);
new Matrix4f().translate(center)
              .rotate((float) Math.toRadians(90.0f), 1.0f, 0.0f, 0.0f)
              .translate(center.negate())
              .transformPosition(pointToRotate);

The resulting vector is (0, 3, 5). Two details are worth copying exactly. Angles are in radians, so the README converts with `Math.toRadians`, and the axis is passed as a raw triple, here 1.0f, 0.0f, 0.0f for x. The rotate method takes the angle first and the axis second, which is the reverse of the order some other libraries use, and a swap here produces a rotation around the wrong axis rather than a compile error.

The `center.negate()` call is also a mutation. It produces a negated copy that is passed to the closing translate, and it is another reminder that the intermediate values live in objects the caller owns.

Right-handed by default, with LH variants for Direct3D style pipelines

The projection and camera builders follow the GLU conventions, and the coordinate system is part of that contract. `perspective()`, `frustum()` and `ortho()` set a projection, while `lookAt()` builds an orthonormal transformation that points a camera at a target. Like the matrix methods, these act on an existing transformation rather than replacing it:

Java
Matrix4f m = new Matrix4f()
     .perspective((float) Math.toRadians(45.0f), 1.0f, 0.01f, 100.0f)
     .lookAt(0.0f, 0.0f, 10.0f,
             0.0f, 0.0f, 0.0f,
             0.0f, 1.0f, 0.0f);
// the camera transformation is now in m

The four arguments to `perspective` are the vertical field of view, the aspect ratio, the near plane and the far plane. The result is meant to be used as a view-projection matrix in a shader, which is where the projection and modelview split, written in the README as `P * MV`, becomes relevant.

By convention `perspective()` and `lookAt()` assume a right-handed coordinate system, the convention OpenGL and GLU established. For code that follows Direct3D, methods ending in `LH` cover the left-handed case:

Java
Matrix4f m = new Matrix4f()
     .perspectiveLH((float) Math.toRadians(45.0f), 1.0f, 0.01f, 100.0f)
     .lookAtLH(0.0f, 0.0f, -10.0f,
               0.0f, 0.0f, 0.0f,
               0.0f, 1.0f, 0.0f);

The naming is predictable, any method whose name ends with `LH` is the left-handed variant, and that naming is the main safety net. Nothing in the API forces you to declare a handedness, so a mismatch between your asset pipeline and the default shows up as a mirrored scene rather than an exception.

Getting JOML into a project: Maven Central, Gradle, Android and GWT

The README does not carry an install snippet. It points to the project wiki instead, with separate setup pages for desktop via Maven, for Android via Gradle, and for GWT via Gradle. The build and publishing setup is visible in the repository as a Gradle project, with `build.gradle`, `settings.gradle`, `gradle.properties` and a `gradlew` wrapper at the top level, plus a `libs/` directory and a `src/` source tree.

The publishing target is Maven Central. The README carries the release badge and the snapshot badge, and both point at the `org.joml` group with the artifact name `joml`. The desktop artifact can therefore be pulled from the standard Maven Central repository under those coordinates, and the wiki pages hold the exact dependency block for each platform, including the Android and GWT setups where the coordinates and packaging may differ.

The README also states the design goal of posing the lowest possible requirements on the execution environment, and names two constraints that shape every decision in the library: compatibility with Java 1.4, and no use of JNI. The repository topics list Android, GWT, Java, JOGL, LWJGL, OpenGL, matrix, math and vector, which is a reasonable guide to the platforms the library is expected to run on. For a first real use, the vector example at the start of this article is enough to confirm the API shape, and it compiles on any Java version the project already supports.

The caller owns the garbage collector problem

The allocation story is a trade, and the trade has a cost. JOML removes allocations from inside the library, but the README is explicit that the client is responsible for allocating the needed working objects. Nothing in the library pools vectors or matrices for you. A render loop that writes `new Vector3f(...)` inside the update step will still allocate, and JOML's mutating design will not save it.

That is a fair arrangement for a project that knows its frame budget, and a poor one for a codebase where vector objects are created wherever they are first needed. The same applies to the destination object overloads. Writing results into a caller supplied target keeps the original intact and lets you reuse a scratch matrix, but only if the code actually holds on to that scratch object. A fresh target per call buys nothing.

The second limitation is documentation depth on the concurrency question. The README describes the mutating semantics and the coordinate conventions in detail, and it points to a separate wiki page for the design rationale, but it does not state whether vector and matrix instances are safe to share across threads. Since every method mutates its receiver, sharing one instance between two threads without synchronisation is a data race on plain fields. Treat instances as thread confined unless the project states otherwise.

A third case where JOML is the wrong tool: software that needs immutable value semantics. There is no immutable variant, no persistent vector, and no way to pass a matrix to code that assumes it cannot be changed. For a scene graph or a config-driven tool where values are shared and compared, a different library fits better.

Release cadence and license terms

The repository is not archived, and the last push was on 2026-10-10. Recent releases are version 1.10.9 on 2026-06-07, 1.10.8 on 2024-07-28, and 1.10.7 on 2024-07-12. The gap between the two older releases and the current one is wide, so a version line can sit unchanged for a long stretch and then move again, and a team pinning a specific version should not assume new patches arrive on a schedule.

The license is MIT, which is permissive and allows the library to be vendored, redistributed and used in commercial and closed source products without a copyleft obligation. It carries the usual notice requirement, and it does not come with a warranty. Neither the README nor the release list carries a stated support policy, so migration help for a specific version has to come from the issue tracker rather than from a promised maintenance window.

For an upgrade cost estimate, the release numbering in the 1.10 line suggests incremental changes rather than a yearly major rewrite, but the README does not document a deprecation policy, and the wiki design page referenced from it is the place to check what changed between two pinned versions before a large migration.

Editorial conclusion

JOML suits engineers writing Java rendering code that has to hit a frame budget without garbage collection hitches, and its mutating API plus OpenGL style post-multiplication removes the usual allocation and matrix order guesswork. It does not suit teams that want immutable value types, a left-handed pipeline by default, or a library with a documented thread safety contract, since none of those are described. Before adopting, verify the coordinate convention of your own pipeline against the right-handed default, check the setup page for your target platform in the project wiki, and confirm that your render loop reuses working objects rather than allocating new ones per frame.

Frequently asked questions

How do I add JOML to a Maven or Gradle project?

The README does not include a dependency block, it links to wiki pages for Maven setup on desktop and Gradle setup for Android and for GWT. The build publishes to Maven Central under the org.joml group with the artifact name joml, so the coordinates can be resolved from the standard Central repository.

Does JOML use a right-handed or left-handed coordinate system?

The methods perspective() and lookAt() assume a right-handed coordinate system, following the OpenGL and GLU convention. Left-handed setups use the variants whose names end with LH, such as perspectiveLH() and lookAtLH(), which match the Direct3D style matrix library.

How do I avoid allocations when updating vectors every frame?

Instance methods write the computation result back into the object they are called on, and most methods also let you pass another destination object, so a scratch vector or matrix can be reused. Working objects themselves are allocated by the client, so they have to be created once and reused rather than constructed inside the frame loop.

Is JOML still being maintained?

The repository is not archived and the last push was on 2026-10-10. The recent releases listed are 1.10.9 on 2026-06-07, 1.10.8 on 2024-07-28, and 1.10.7 on 2024-07-12, so releases can be separated by long gaps.

Official sources

  1. Issues
  2. JOML-CI/JOML on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/joml-ci-joml.svg)](https://hysenlabs.com/projects/joml-ci-joml)

Was this useful?