← Back to list

Nodi v2 early access version released

I have released the early access version of Nodi v2, a web browser-based modeling tool built on node-based programming…

Masatatsu Nakamura · 2024-11-10 06:29 · 0 claps · 5.8 min read
#3dcg #web #design-tools #modeling #nodi
Open on Medium ↗
Wiki topics: 💻 · Programming

Nodi v2 early access version released

I have released the early access version of Nodi v2, a web browser-based modeling tool built on node-based programming. https://v2.nodi3d.com

https://v2.nodi3d.com?example=WaveSpring

https://v2.nodi3d.com?example=WaveSpring

In this article, I will explain why I decided to rewrite Nodi for v2, the technical background, and the plans for its future.

What is Nodi?

Nodi is a web-based tool for procedural modeling, similar to Grasshopper, Houdini, and Geometry Nodes.

I first released the public beta of v1 around early 2020. However, it had several significant issues, and I eventually rewrote the tool from scratch over time. Although it took longer than expected because of other commitments, I’ve finally reached a point where the tool includes enough essential features to function as a basic modeling tool. That’s why I decided to release it now.

Technical Background and Improvements in v2

Two major issues identified in v1 were:

  1. Performance
  2. Weak CAD Kernel

In v1, I developed the entire core logic in TypeScript/JavaScript. This reliance on garbage collection for memory management caused performance issues over time, especially for a modeling tool handling heavy geometry data.

Additionally, v1 heavily relied on **three.js for polygon meshes and the [verb](https://github.com/pboyer/verb)** library for curves and surfaces. Some features were released in an incomplete state because they couldn’t fully achieve the necessary functionality.

Given these limitations, I started questioning whether continuing development in TypeScript/JavaScript was sustainable.

Rust and WebAssembly

To address performance and memory management concerns, I turned to WebAssembly. Its linear memory model allows for efficient memory management, avoiding issues related to garbage collection and offering improved computational performance for core geometric operations.

To utilize WebAssembly effectively, I chose Rust as the development language for v2. Rust’s strong support for WebAssembly and my prior experience with it in geometry-heavy projects made it a natural choice.

Developing a CAD Kernel

Although I chose Rust as the development language, there are no feature-rich CAD kernels in Rust that are suitable for modeling software development. (This limitation may not be specific to Rust; finding such libraries under a free license might be inherently difficult.)

There are a few pure Rust CAD kernel projects, such as truck, which is an excellent open-source library with implementations for curves and surfaces. However, none of them provide the extensive modeling operations needed for Nodi. Since modeling software requires the flexibility to easily add custom features, I concluded that developing my own solution would be necessary.

As a result, I created curvo, a library for handling curves and surfaces in Rust. https://github.com/mattatz/curvo

Lofting surfaces by curvo

Lofting surfaces by curvo

With curvo, I implemented key geometric representations like BRep, which are essential for CAD workflows in v2.

The geometric representation output switches to BRep based on the cap parameter.

The geometric representation output switches to BRep based on the cap parameter.

Zero-Copy Data Sharing with WebAssembly

For rendering geometric shapes, v2 still uses three.js. However, geometric data now resides in WebAssembly memory. To prevent performance issues caused by data copying, I used Typed Arrays for zero-copy memory sharing between WebAssembly and TypeScript.

The memory address and size of geometric data generated on the WebAssembly side are passed to the TypeScript side, enabling memory sharing via an Array Buffer. Below is a simplified excerpt of the code used to achieve zero-copy sharing of Polygon Mesh data stored on the WebAssembly side using a Typed Array.

// Interop structure for Polygon mesh that contains only memory address and size information
#[derive(serde::Serialize)]
pub struct MeshInteropHandle {
    pub count: usize,     // Number of vertices
    pub vertices: usize,  // Memory address of the vertex buffer
    pub normals: usize,   // Memory address of the normal buffer
}

// Converts a type containing the actual Polygon mesh data into a type containing only memory information
impl<'a> From<&'a MeshInterop> for MeshInteropHandle {
    fn from(value: &MeshInterop) -> Self {
        Self {
            count: value.vertices().len(),
            // Sets the memory address of the Vec type
            vertices: value.vertices().as_ptr() as usize,
            normals: value.normals().as_ptr() as usize,
        }
    }
}
// Example of creating a TypedArray to represent a mesh shape using memory information sent from WebAssembly
const {
 memory, // WebAssembly.Memory
 handle  // MeshInteropHandle
} = props;

const stride = 3; // Number of elements per vertex ([x, y, z] = 3 components)
const { 
  count,    // Number of vertices
  vertices, // Memory address of the vertex buffer
  normals   // Memory address of the normal buffer
} = handle;

// By specifying new Float32Array(WebAssembly.Memory, starting memory address, memory length),
// you can reference data on the WebAssembly ArrayBuffer without copying it
const vertexArray = new Float32Array(memory.buffer, vertices, count * stride);
const normalArray = new Float32Array(memory.buffer, normals, count * stride);

// Define shape data for rendering in three.js using TypedArray
const geometry = new BufferGeometry();
const position = new BufferAttribute(vertexArray, stride);
const normal = new BufferAttribute(normalArray, stride);
geometry.setAttribute("position", position);
geometry.setAttribute("normal", normal);

Modular as a Standalone Geometry Engine

When I was developing v1, I wanted to modularize Nodi as a geometry engine to make it easier to reuse in other projects. Specifically, the idea was to reuse node graphs created in the editor in other projects and regenerate modeling results from those node graphs.

In v2, adopting Rust and WebAssembly has made it easier to provide such modular functionality. https://github.com/Nodi3d/modular

This modular is available as an npm package, allowing it to be integrated into other projects as described above. For example, it enables the development of tools or services that partially use Nodi for geometry generation, like **Bento3D by Nakajima. (Bento3D uses v1 of Nodi, but since v1 did not provide an npm package like v2’s modular**, integration was reportedly more cumbersome.)

Bento3D https://bento3d.design/

Bento3D https://bento3d.design/

UI Updates

The UI has been completely rebuilt from scratch, resulting in updates to the overall layout and various other aspects.

Notably, an Inspector panel has been added to the right side of the screen, making it easier to preview and edit data. Within the Inspector, a new Geometry Tab has been introduced, allowing users to check the attributes of geometric data output by the selected node. (This was inspired by the Spreadsheet feature found in tools like Houdini and Blender.)

The Geometry tab allows you to view the geometric data output from nodes.

The Geometry tab allows you to view the geometric data output from nodes.

Future Features

I consider BRep and curve/surface modeling features as high-priority areas that need to be gradually developed. Beyond those, I also want to bring back FRep modeling, which was experimentally implemented in v1, to v2.

FRep Modeling

FRep modeling in v1

FRep modeling in v1

FRep represents geometric shapes using mathematical expressions called Signed Distance Functions, which require less memory for shape generation and enable smooth modeling even in browser-based applications.

An example of a modeling tool using FRep is Adobe’s **Project Neo**. However, the typical method for converting FRep into formats suitable for 3D printing involves an intermediate step of meshing. This process often incurs significant computational costs.

In v2, I aim to explore methods for directly converting FRep into 3D-printable formats or similar outputs without requiring intermediate steps like meshing.

Features as a Web Service

In the v2 early access version, a database is not yet implemented, so saving data to the cloud is not supported. (However, exporting and importing locally is possible.)

I plan to add this functionality once the modeling features are more robust and the tool is ready to be offered as a full-fledged service.

Other Features

There are countless smaller features I’d like to add, but the main ones I’m currently considering are as follows:

  • Plugins: Custom nodes and scripting support.
  • Reusable Computations: Features like Houdini’s Block Begin/End or Blender’s Simulation Nodes.
  • CAM/CAE Support.

Some of the features mentioned above involve high development costs, so their feasibility is uncertain. However, I hope to work on them as opportunities arise.

Outlook

During v1’s lifecycle, I was fortunate to collaborate with some companies and deliver customized solutions. Going forward, I aim to enhance v2’s functionality and continue building practical tools through real-world projects.

If you have any feedback, feature suggestions, or collaboration ideas, feel free to reach out.


메타데이터
post_id
fe877fe56a6c
slug
nodi-v2-early-access-version-released-fe877fe56a6c
url
https://medium.com/@masatatsu.nakamura/nodi-v2-early-access-version-released-fe877fe56a6c
canonical_url
https://medium.com/@masatatsu.nakamura/nodi-v2-early-access-version-released-fe877fe56a6c
author_url
https://medium.com/@masatatsu.nakamura
status
ok
fetched_at
2026-06-09 15:37:30