Library / SDK
mcneel/rhino3dm avatar
mcneel/rhino3dm

rhino3dm puts OpenNURBS geometry in .NET, Python and JavaScript without a Rhino install

Libraries based on OpenNURBS with a RhinoCommon style

767 stars157 forksC#MIT

At a glance

What is it?
rhino3dm is McNeel's OpenNURBS based library with a RhinoCommon style API, shipped for .NET, Python and JavaScript so you can create, inspect and read write .3dm files outside Rhino. The 9.x line is still labelled a beta and the project publishes no GitHub releases, so version pinning stays manual.
Who is it for?
rhino3dm fits engineers who need to script Rhino geometry on a build server, in a browser or inside a .NET service without buying a Rhino seat, and its MIT licence keeps commercial use unencumbered. It does not fit teams that want RhinoCommon feature parity, that need the full Rhino modelling environment, or that want a supported release train with changelogs and upgrade notes.
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 3 days ago.
What is it written in?
Mainly C#, 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

OpenNURBS geometry without a Rhino install

rhino3dm is a set of libraries built on the OpenNURBS geometry library and shaped like RhinoCommon, the native scripting library that ships with Rhino. The point of the arrangement is access to geometry through .NET, Python or JavaScript applications that run independently of Rhino itself. In practice that means a service that validates a submitted .3dm file, a browser tool that lets a customer inspect a design, or a script that batch converts models does not need a licensed Rhino installation on the machine.

The object model is wider than a bare NURBS evaluator. The project describes the ability to create, interrogate and store the geometry types Rhino supports: points, point clouds, NURBS curves and surfaces, polysurfaces as boundary representations, meshes, annotations, extrusions and SubD surfaces. Alongside that sit the non-geometry classes, namely layers, object attributes, transforms and viewports. Those classes matter because a file is not only solids; attributes and layer membership drive how a model renders and how it is batch processed.

The geometry core is not Rhino itself. rhino3dm gives you the data structures and the file format, not the modelling environment, the viewport, or the analysis tools that live on top of them.

Three package pipelines over one geometry core

The same library ships three ways, and each package targets a different runtime rather than a different feature set. The Python distribution is published on PyPI as rhino3dm and is described as working on all current CPython versions from 3.8 through 3.14 across Windows, macOS and Linux. The installation command given in the project docs is a single `pip install --user rhino3dm` line.

The `--user` flag installs into the per-user site directory, which avoids needing administrator rights on a shared workstation. That suits CI runners and short lived containers where a global install is inconvenient.

The JavaScript distribution is a separate package, rhino3dm.js, paired with a web assembly binary named rhino3dm.wasm. It is published on npm and is described as running on all major browsers as well as node.js. The .NET distribution is a NuGet package called Rhino3dm, formerly shipped under the name Rhino3dmIO, and it targets standalone .NET applications rather than plugins. The repository builds macOS, Windows and Linux packages in various runtimes, and all of them are delivered through that single NuGet package. The docs for each language live in separate files, `docs/python/RHINO3DM.PY.md` and `docs/javascript/RHINO3DM.JS.md`, so behaviour is documented per binding rather than once for the whole library.

setup.py routes the Python build through CMake

The Python package is not a pure Python shim. `setup.py` at the repository root defines a custom setuptools command that shells out to CMake, and it refuses to continue when the build tool is missing:

python
        try:
            out = sp.check_output(['cmake', '--version'])
        except OSError:
            raise RuntimeError(
                "CMake must be installed to build the following extensions: " +
                ", ".join(e.name for e in self.extensions))

The same file enforces a floor on Windows, where it parses the reported version and rejects anything older:

python
            if cmake_version < '3.21.0':
                raise RuntimeError("CMake >= 3.21.0 is required on Windows")

That constraint only surfaces when you build from source. The extension class in the same file points at a source directory, which lines up with the `src/` folder in the repository listing, and the tree also carries `MANIFEST.in`, a `script/` directory and a `tools/` directory for auxiliary tasks. Building a custom Python extension from source therefore means having a compiler toolchain and CMake available, on top of a C++ geometry core that the repository pulls in through a `.gitmodules` entry. For everyday use, the published PyPI artifact is the path of least resistance.

A first sphere from the WebAssembly build

The README's JavaScript example is the shortest route to a working result, and it is worth reading closely because it exposes two practical details. The HTML page loads a module shim script from unpkg and declares an import map, so a browser without native import map support still resolves the bare module name. The import map entry pins a specific published version, and the example points at `[email protected]`.

The module body itself is only a few lines:

js
    import rhino3dm from 'rhino3dm'
    const rhino = await rhino3dm()
    const sphere = new rhino.Sphere( [1,2,3,], 12 )
    console.log(sphere.diameter)

The factory call is asynchronous, so the module has to be awaited before any geometry type is constructed. The sphere constructor takes a centre point and a radius, and the resulting object exposes a diameter property. What you should see in the browser console is the diameter value printed from a geometry object created entirely client side, with no Rhino server involved.

For node.js the same package is used without the import map and shim, since those are browser concerns. The Python binding follows the same object model, so a script that moves between the three languages sees the same classes, and its details live in the separate Python documentation file.

The JavaScript manifest still carries a beta version

The npm manifest at the repository root names the package rhino3dm and gives it version `9.0.0-beta`. Its `main` entry is `rhino3dm.js` and its `types` entry is `rhino3dm.d.ts`, so TypeScript definitions ship inside the same package. The default branch of the repository is `9.x`, which matches the beta number rather than a released series.

Two details there matter for anyone planning an adoption. First, the `scripts` block in that manifest defines a single test entry whose body is a placeholder that prints an error about no test being specified and exits with a failure status. The JavaScript package therefore does not run a test suite through the npm script path, so quality assurance has to come from the repository's own CI workflows or from your own tests.

Second, the version you get from a package registry and the version you read in the repository are not the same thing. The README's import map pins `8.17.0`, while the manifest says `9.0.0-beta`. Expect to pin explicitly rather than track a moving branch.

Native bindings, local files, or the Compute server

There are three genuinely different ways to reach this geometry, and they differ in where the work happens. Building against the source tree in `src/` gives native in-process bindings, which is the fastest path but pushes the C++ core and its toolchain onto your build machine. Using the packaged libraries keeps the geometry in your own process, reading and writing .3dm files locally with no network dependency and no server to keep alive.

The third option is remote. rhino3dm is described as usable as a client to make calls into the Rhino Compute cloud server for advanced manipulation of geometry objects. The trade off is explicit: the local libraries handle geometry operations inside your process, while Compute moves that work to a hosted endpoint and adds a network round trip for each request. Compute covers manipulation that the local libraries leave out, but it also turns a self contained script into a client with an availability dependency.

The comparison that matters most is against RhinoCommon itself. A RhinoCommon plugin runs inside Rhino and inherits its full object model, its UI and its plug-in hosting model, which is exactly what rhino3dm does not provide. rhino3dm trades that reach for portability and a permissive licence.

MIT licensed with a last push on 2026-10-18

The repository is not archived and its last push was on 2026-10-18, so the code is being worked on rather than frozen. The licence is MIT, and a `LICENSE` file sits at the repository root alongside a `CONTRIBUTING.md`, a `CHANGELOG.md` and a `Current_Development_Tools.md` that lists the tooling expected by the maintainers.

There is a gap between the maintenance signal and the release signal. The repository publishes no GitHub releases, so there is no changelog entry on the release page and no tag based upgrade path to point at. The `CHANGELOG.md` file in the tree is where history would live, but the project does not publish release artefacts through GitHub. Anyone tracking the library by version has to watch package registries instead of the repository release list.

The MIT licence itself removes the usual adoption question about redistribution in a commercial product, since neither the licence nor the repository asks for a Rhino seat to use the library. Check the licence text yourself before shipping it inside a closed source product, since that is a decision for your legal team rather than for a review.

Gaps an adopter has to fill in

The obvious weak point is scope. The class list covers geometry, layers, attributes, transforms and viewports, and it stops there. There is no Rhino modelling environment, no viewport rendering, no meshing pipeline and no analysis tools. If your workflow depends on those, the library is the wrong tool and you need Rhino itself or a Compute endpoint.

A second gap is release discipline. No GitHub releases exist, the npm manifest is on a beta label, and the version referenced in the README import map differs from the manifest version. Pinning exact versions is therefore mandatory rather than optional, and you should expect to review the manifest and changelog by hand on every upgrade.

Third, the documentation is split by binding, and the JavaScript package ships a stub test script. The repository does not describe a rollback procedure for a bad version, nor does it publish a support policy for the beta line. If your pipeline needs a guarantee of a maintained release train with an upgrade path, that guarantee appears nowhere in the repository, so the licence, the CI workflows and the `src/` build are the only stability signals on offer.

Editorial conclusion

rhino3dm fits engineers who need to script Rhino geometry on a build server, in a browser or inside a .NET service without buying a Rhino seat, and its MIT licence keeps commercial use unencumbered. It does not fit teams that want RhinoCommon feature parity, that need the full Rhino modelling environment, or that want a supported release train with changelogs and upgrade notes. Before you commit, check that the geometry types you rely on are covered by the API surface, confirm your platform has a published build for your Python or runtime version, and pin an exact version rather than tracking the 9.x branch.

Frequently asked questions

What is rhino3dm and can I use it without installing Rhino?

rhino3dm is a set of libraries based on OpenNURBS with a RhinoCommon style API, available for .NET, Python and JavaScript, and it is described as working independently of Rhino. It runs on Windows, macOS and Linux.

How do I install rhino3dm for Python, JavaScript and .NET?

The Python package installs from PyPI with `pip install --user rhino3dm` and supports CPython 3.8 to 3.14. The JavaScript package rhino3dm.js is published on npm, and the .NET package Rhino3dm is published on NuGet.

Can rhino3dm read and write .3dm files?

Yes. The README states that rhino3dm can read and write the .3dm file format, along with points, point clouds, NURBS curves and surfaces, polysurfaces, meshes, annotations, extrusions and SubD surfaces.

Is rhino3dm actively maintained, and how do I get a stable version?

The repository is not archived and its last push was on 2026-10-18, but it publishes no GitHub releases and the npm manifest lists version 9.0.0-beta. The README's own import map example pins an earlier published version, 8.17.0.

Official sources

  1. Issues
  2. License: MIT
  3. mcneel/rhino3dm on GitHub
  4. README
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/mcneel-rhino3dm.svg)](https://hysenlabs.com/projects/mcneel-rhino3dm)

Was this useful?