martini: real-time RTIN terrain meshes for the browser
A JavaScript library for real-time RTIN terrain mesh generation
At a glance
- What is it?
- Martini is a small JavaScript library that turns a regular height grid into a hierarchy of right-triangulated irregular networks in milliseconds, so a terrain renderer can pick the level of detail it needs per frame. It is two files, one dependency free core, and the author calls it a work in progress with the last tagged release in 2020.
- Who is it for?
- Adopt martini if you are rendering terrain in the browser and need to adapt mesh detail to a screen-space error budget per frame, since that is the thing it does and does in milliseconds. Do not adopt it as a general mesh library or a terrain data pipeline, because it takes an already loaded height grid and produces indices, and it has not had a tagged release since 2020 with the author describing it as experimental.
- Can I use it commercially?
- Yes. ISC 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 121 days ago.
- What is it written in?
- Mainly JavaScript, 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
One height grid in, a detail hierarchy out
Martini is an experimental JavaScript library for real-time terrain mesh generation from height data. Given a grid of (2^k + 1) by (2^k + 1) terrain, it generates a hierarchy of triangular meshes of varying level of detail in milliseconds.
That is the whole product, and the constraint in it is the interesting part. The input is not an arbitrary heightmap, it is a grid whose side length is a power of two plus one, which is what makes the recursive subdivision possible. A 257 by 257 grid is a 2^8 + 1 grid, and the example in the readme uses exactly that number.
The output is a hierarchy rather than a single mesh, and that is the point of the whole design. At any moment a renderer can ask for the mesh at a given error tolerance and get back the smallest amount of geometry that stays under that tolerance, which is what lets a terrain be drawn at a consistent screen-space quality while the camera moves.
The name expands to Mapbox's Awesome Right-Triangulated Irregular Networks, Improved, and the algorithm is based on the paper Right-Triangulated Irregular Networks by Will Evans and others from 1997. So this is a browser implementation of a published geometric construction rather than an invention, with the improvement implied by the name.
There is an interactive Observable notebook linked from the readme, by the author, where the algorithm is shown in action and explained, and the readme carries a martini.gif of a terrain demo.
Three calls, and the error parameter does the work
The API is short enough to read in one pass, which is typical of a focused library:
import Martini from '@mapbox/martini';
// set up mesh generator for a certain 2^k+1 grid size
const martini = new Martini(257);
// generate RTIN hierarchy from terrain data (an array of size^2 length)
const tile = martini.createTile(terrain);
// get a mesh (vertices and triangles indices) for a 10m error
const mesh = tile.getMesh(10);The three steps map to three ideas. The constructor takes the grid size once, and the instance is the mesh generator for that size, so a renderer that handles many tiles of the same resolution creates one generator and reuses it. The createTile call takes the actual terrain data, an array of size squared length, and produces a tile holding the RTIN hierarchy. The getMesh call then takes an error value in metres and returns the mesh, described as vertices and triangles indices, that stays within that error.
The error parameter is the user facing control. Ten metres in the example. Larger error means fewer triangles and a faster frame, smaller error means a more faithful surface. A renderer that wants to adapt detail to zoom level maps zoom to an error budget and asks for a mesh per frame, which is the real-time use the library is named for.
Because getMesh returns indices rather than a vertex buffer, the library is agnostic about where the vertex data lives. The vertices come from the height grid plus the x and y coordinates, and the triangles reference them, so the renderer can build the buffers it needs once and reindex them per frame.
Two files, no dependencies, and an ISC licence
The package is unusually small for something with a graphics pedigree, and the manifest explains why.
The published files list contains exactly two entries, index.js and index.d.ts. There is no build step, no bundler configuration and no compiled output in the tree, because the source is shipped as is. The type field is module, so it is an ES module, and the exports map points at the same file, with types pointing at the declaration file.
The dependencies block does not exist. There is nothing to install alongside it, which for a graphics library is unusual and means there is no transitive dependency risk and nothing to keep in step with.
The devDependencies are the small set a project needs to work on itself. eslint at version 9, the author's own eslint-config-mourner, and pngjs, which is there for the benchmark or test code to read and write PNG images. The eslintConfig block in the manifest extends the mourner config and disables the no-use-before-define rule, which is a hint about the style of the implementation, since a recursive subdivision function naturally refers to itself before its definition.
The scripts are minimal too. The test script runs node test/test.js, the bench script runs node bench.js, and there is a pretest that runs eslint over index.js, bench.js and the test directory, so linting is part of the test path. The prepublishOnly script runs the test again before publishing.
The licence is ISC, with the author listed as Vladimir Agafonkin, and the package is published as @mapbox/martini. The keywords are terrain, rtin, mesh, 3d and webgl, which describes the intended use exactly.
A Python port exists, and the paper is the real documentation
Two things sit outside the JavaScript package and both matter to anyone evaluating it.
The first is pymartini, a Python port maintained by a different author, linked from the readme under ports to other languages. That is worth knowing for two reasons. A Python implementation is useful if your terrain pipeline is Python and you want the hierarchy offline rather than in the browser, for instance to precompute a mesh cache. And an independent port is a form of evidence about the algorithm being portable and about the reference implementation being clear enough to reimplement, which is not a trivial property of a 1997 paper with a terse description.
The second is the paper itself. The readme credits Right-Triangulated Irregular Networks by Will Evans and others, and links to the PDF. The construction is a geometric one: given a regular grid, the algorithm places vertices on a perturbed pattern, and the triangulation is chosen so that triangles stay roughly equilateral while deviating from the regular grid, which is what makes the mesh adaptive rather than merely subdivided.
That paper is the actual specification. The readme is two paragraphs and one code example, so anyone who wants to understand the algorithm, or to trust that it will behave as claimed on a particular terrain, has to go to the paper or the Observable notebook.
The Observable notebook is the more practical of the two for an implementer. It shows the algorithm running with visuals, which for a geometric construction is a faster route to intuition than a formal description, and it is by the author of the library.
What the project state means for a dependency
The last tagged release is v0.2.0 from 2020-01-31, with v0.1.0 before it from 2019-08-19. The last push to the repository was on 2026-06-24, so the code is being touched even though the version tags stopped years ago.
The readme labels the library a work in progress, and calls it experimental. Taken with the 0.x version, that is an honest signal about what to expect: this is code that works, that is small enough to read in an afternoon, and that is not going to grow a stable API or a compatibility promise.
For a rendering library that is often the right trade. The algorithm is a paper, the interface is three methods, and there is no configuration surface to break. The risk of depending on it is not that it changes underneath you in a way you cannot see, because the API is too small for that, it is that a real edge case in the geometry might never be fixed because nobody is obliged to.
There is one more practical point. The library does not load or fetch terrain data, and it does not produce a renderable buffer. It takes a height array you already have and produces indices for it. So the surrounding questions of where the height data comes from, how it is fetched, and how the tile grid is managed are all still yours, and they are usually the larger part of a terrain renderer.
Editorial conclusion
Adopt martini if you are rendering terrain in the browser and need to adapt mesh detail to a screen-space error budget per frame, since that is the thing it does and does in milliseconds. Do not adopt it as a general mesh library or a terrain data pipeline, because it takes an already loaded height grid and produces indices, and it has not had a tagged release since 2020 with the author describing it as experimental. Before you commit, check the grid size constraint, since it is a power of two plus one rather than an arbitrary dimension, and read the Observable notebook the readme links, which is where the algorithm is explained in more depth than the readme is.
Frequently asked questions
What does the martini library do?
It generates real-time terrain meshes from height data. Given a terrain grid of (2^k + 1) by (2^k + 1) points, it creates a hierarchy of right-triangulated irregular networks of varying level of detail in milliseconds, so a renderer can request the mesh that fits its error budget.
How do I use martini to generate a mesh?
Create a Martini instance with the grid size, for example new Martini(257), then call createTile with the terrain data as an array of size squared length, and finally call getMesh with the error tolerance in metres, which returns the vertices and triangle indices.
How do I install martini?
With npm install @mapbox/martini. The published package contains only index.js and index.d.ts, has no runtime dependencies and is an ES module with type definitions included.
Is there a Python version of martini?
Yes, pymartini is a Python port linked from the readme under ports to other languages, maintained separately from the JavaScript package.
What algorithm does martini implement?
It implements Right-Triangulated Irregular Networks, based on the 1997 paper by Will Evans and others. The readme also links an interactive Observable notebook by the author where the algorithm is shown running and explained.
Official sources
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.
[](https://hysenlabs.com/projects/mapbox-martini)Was this useful?