← Back to Blog

Why we built ModelRift on OpenSCAD

OpenSCAD's functional language makes it ideal for AI-assisted 3D modeling - better LLM generation, safer sandboxing, and fast rendering with Manifold.

openscadtechnicalai

ModelRift is an AI-enhanced 3D model editor. When we started building it, we had to pick a CAD format for the AI to generate. We chose OpenSCAD, and here’s why.

OpenSCAD in brief

OpenSCAD is a script-based 3D modeler. You write code, it outputs geometry. No mouse-driven sculpting, no direct manipulation - just text in, mesh out.

The project started in 2010, created by Clifford Wolf and Marius Kintel. They wanted to escape the “black box” nature of GUI-driven CAD tools, where the relationship between user actions and geometry is opaque. In OpenSCAD, a model is literally its source code.

The language is functional and declarative. You define shapes by combining primitives (cubes, cylinders, spheres) with boolean operations (union, difference, intersection). A basic example:

difference() {
    cube([20, 20, 10]);
    translate([10, 10, 0])
        cylinder(h=10, r=5);
}

This creates a cube with a cylindrical hole. The output is deterministic - the same code always produces the same model.

OpenSCAD gained traction with the maker movement throughout the 2010s. Thingiverse adopted it for their Customizer feature, which lets users tweak parameters on published designs without editing code. This created a feedback loop: more designs on Thingiverse meant more people learning OpenSCAD, which meant more designs.

The development pace is slow by modern standards. Major releases come every few years. Version 2021.01 added 3MF export and C-style loops. The current development snapshots include the Manifold backend, which we’ll get to. This slow cadence keeps things stable - mechanical designs shouldn’t break because of a minor update.

STL vs STEP: raster vs vector for 3D

Before getting into rendering engines, it helps to understand what OpenSCAD actually outputs.

Think of the difference between a JPEG and an SVG. A JPEG stores pixels - a grid of color values. An SVG stores shapes - “there’s a circle at position (100, 50) with radius 30.” If you scale a JPEG up, it gets blurry. If you scale an SVG up, it stays sharp because the renderer recalculates the curves at the new resolution.

STL files are like JPEGs. An STL is a list of triangles - thousands or millions of them, depending on detail level. A cylinder isn’t stored as “a cylinder with radius 5 and height 10.” It’s stored as a bunch of triangular facets that approximate the curved surface. If you zoom in far enough, you see the flat faces. The mathematical intent is gone; only the approximation remains.

OpenSCAD has a $fn variable that controls how many segments approximate a curve. Consider this simple cylinder:

cylinder(h=10, r=5, $fn=32);

With $fn=32, this exports to a 7KB STL file (124 triangles). Change it to $fn=256 and the same cylinder becomes 54KB (1,020 triangles). The geometry is identical in intent - it’s still a cylinder with radius 5 and height 10 - but the file stores every triangle explicitly. Double the resolution, roughly double the file size. A STEP file for the same cylinder would be a few hundred bytes regardless of display resolution, because it just says “cylinder, radius 5, height 10.”

STEP files are like SVGs. A STEP file stores actual geometry: “this is a cylinder, this is a fillet with radius 2mm, these two faces are tangent.” The surfaces are defined mathematically, usually as NURBS (non-uniform rational B-splines). You can scale, edit, or measure a STEP model without losing precision.

For 3D printing, STL works fine. The printer slices the mesh into layers and doesn’t care that your circle is really a 64-sided polygon. For CNC machining or injection molding, you often need STEP. CAM software needs to know that an edge is truly circular to generate proper toolpaths.

OpenSCAD produces STL (and 3MF, OFF, AMF). It cannot produce STEP natively because its internal representation is mesh-based. Once you render, the parametric information is gone.

Geometry kernels: how the math gets done

A geometry kernel is the library that actually computes boolean operations, intersections, and mesh generation. Different kernels make different tradeoffs between speed, precision, and capability.

CGAL

CGAL (Computational Geometry Algorithms Library) has been OpenSCAD’s primary kernel for over a decade. It uses Nef Polyhedra for 3D boolean operations - a mathematically rigorous representation where solids are defined as combinations of half-spaces.

CGAL’s strength is robustness. It handles degenerate cases (zero-thickness walls, coincident faces, non-manifold edges) that would crash simpler implementations. To achieve this, CGAL uses arbitrary-precision arithmetic: coordinates are stored as exact rational numbers rather than floating-point approximations. No rounding errors, no accumulated drift.

The cost is speed. Those rational numbers grow as operations compound. A union of two simple shapes might produce coordinates with hundreds of digits in the numerator and denominator. Memory usage balloons, computation slows to a crawl. A moderately complex model can take 20 minutes to render.

Manifold

Manifold is a newer geometry library that became OpenSCAD’s second rendering backend in 2024. It uses double-precision floating-point math with topological guarantees instead of exact arithmetic.

The approach is different: rather than ensuring coordinates are mathematically exact, Manifold ensures the output mesh is always valid (watertight, no self-intersections). It uses parallel algorithms, Morton code sorting for spatial locality, and avoids slow atomic operations. The code is designed for modern CPUs and can optionally use GPU acceleration.

The performance difference is dramatic. On benchmark models:

  • A model that took 540 seconds with CGAL renders in 27 seconds with Manifold (20x faster)
  • A filleted design that took 1,132 seconds now takes 11 seconds (100x faster)
  • Some models show 200x improvement

For ModelRift, this matters because we render on every code change. A 20-minute render is unusable for interactive editing. A 6-second render is workable.

OpenCASCADE

OpenCASCADE (OCCT) is a different beast entirely. It’s a boundary representation (B-Rep) kernel, not a mesh-based one. OCCT is what FreeCAD, CadQuery, and many commercial CAD tools use under the hood.

In B-Rep, geometry is stored as a graph of faces, edges, and vertices, with each face defined by a mathematical surface (planes, cylinders, NURBS patches). This is the native representation for STEP files. OCCT can do things mesh kernels cannot: compute the intersection curve of two surfaces, apply a 3mm fillet to a specific edge, or query which faces are tangent.

The tradeoff is complexity. OCCT is a large library (over 1 million lines of C++) with a steep learning curve. Its API reflects decades of evolution and CAD industry conventions. Operations that seem simple (“fillet this edge”) can fail in surprising ways depending on surrounding geometry.

CadQuery wraps OCCT in a friendlier Python API. But the underlying kernel complexity still leaks through. When an LLM generates CadQuery code, it often gets the high-level operations right but misunderstands OCCT’s expectations about topology or edge selection.

Why OpenSCAD doesn’t use OCCT

It would be technically possible to build a CSG modeler on top of OCCT. You could convert primitives to B-Rep solids, perform boolean operations, and export to STEP with full fidelity.

OpenSCAD chose not to go this route. The project prioritizes simplicity: a single executable, no external dependencies, predictable behavior. OCCT would add significant build complexity and a large binary size. The STEP export benefit would come at the cost of the tool’s accessibility.

There have been proposals to add OCCT as an optional export backend - keep the mesh-based workflow but translate to B-Rep at the final step. This is technically hard because mesh-to-B-Rep conversion is lossy. A 64-segment polygon could be a circle, a 64-gon, or a spline approximation. The converter has to guess.

OpenSCAD vs CadQuery

CadQuery is the main alternative for code-based CAD. It’s a Python library built on OpenCASCADE, using B-Rep instead of CSG.

B-Rep has real advantages: you can select specific faces and edges, work with true NURBS surfaces, and export to STEP. CadQuery can do things OpenSCAD cannot, like applying fillets to specific edges or querying the topology of a shape.

But CadQuery requires Python. OpenSCAD is a standalone executable with a small, focused language.

Why ModelRift chose OpenSCAD

LLMs generate better OpenSCAD

We tested multiple code-based CAD formats with various language models. OpenSCAD consistently produced better results than CadQuery.

Part of this is training data. OpenSCAD has been around since 2010 and is the default format for parametric designs on Thingiverse. There are thousands of public .scad files the models have seen. CadQuery is newer and less represented in training corpora.

Part of it is language complexity. OpenSCAD’s functional DSL has a small surface area - a few dozen functions, no classes, no state. CadQuery gives you all of Python plus the OpenCASCADE API. More ways to write valid code means more ways to write broken code.

When we ask an LLM to generate a phone stand with cable routing, OpenSCAD output usually works on the first try. CadQuery output often has subtle bugs in the chained selector calls or misunderstands the OCCT conventions.

Sandboxing is simpler

ModelRift runs user code on our servers. With CadQuery, that means running arbitrary Python. Python sandboxing is hard. You need to restrict imports, block filesystem access, limit CPU and memory, and worry about a thousand escape vectors.

OpenSCAD’s language has no file I/O, no network access, no system calls. It can only produce geometry. We run it in a container with strict limits, but even if the sandbox failed, the worst case is a malformed mesh.

This isn’t a hypothetical concern. We want users to share and remix each other’s models. Executing untrusted code safely is the core security requirement. OpenSCAD makes this tractable.

The language limitation is a feature

OpenSCAD’s critics often point out that its language lacks modern features: no objects, no dictionaries, limited string handling. Fair enough.

For AI-assisted modeling, these constraints help. The LLM doesn’t need to decide between ten architectural patterns. It doesn’t import libraries that might not exist. It writes straightforward geometry code.

The community has built libraries like BOSL2 that add threading, attachments, and other conveniences. But for generated code, simplicity wins.

The STEP export problem

OpenSCAD cannot export STEP files natively. As discussed above, STEP requires boundary representation, and OpenSCAD works in meshes. A circle in OpenSCAD is really a polygon with 32 (or 64, or 128) segments.

For 3D printing, this doesn’t matter. For integration with professional CAD tools, it does. FreeCAD can import OpenSCAD files and convert them, but the results are sometimes unusable for complex models.

This is the main reason some users need CadQuery instead. If your workflow requires STEP output for downstream CAM or assembly modeling, OpenSCAD may not work for you.

Where this leaves us

OpenSCAD fits what we’re building. The AI generates geometry reliably, we can sandbox it safely, and Manifold makes rendering fast enough for interactive use.

We’re not claiming OpenSCAD is the best CAD tool for every use case. CadQuery is better if you need B-Rep features or Python integration. Traditional parametric CAD is better if you prefer direct manipulation. OpenSCAD is good for code-first, deterministic, AI-assisted 3D modeling - which is exactly what ModelRift does.