Parametric CAD in Rust
January 27, 2026I keep designing physical parts for our robots. Motor mounts, sensor brackets, wheel hubs. Every time, the workflow is the same: open a GUI CAD program, click around for an hour, export an STL, realize the bolt pattern is 2mm off, repeat.
I wanted to write my parts the way I write firmware. In Rust. With types. With version control. With the ability to change one number and regenerate everything.
So I built vcad.
cargo add vcad
The idea
A part is just geometry with a name. You create primitives, combine them with boolean operations, and export. That's it.
use vcad::{centered_cube, centered_cylinder, bolt_pattern};
let plate = centered_cube("plate", 120.0, 80.0, 5.0);
let bore = centered_cylinder("bore", 15.0, 10.0, 64);
let bolts = bolt_pattern(6, 50.0, 4.5, 10.0, 32);
let part = plate - bore - bolts;
part.write_stl("plate.stl").unwrap();
That minus sign is a real boolean difference. + is union. & is intersection. Operator overloads make CSG feel like arithmetic.
The plate above has a center bore, four corner mounting holes, and a six-bolt circle pattern. Twelve lines of code. One STL file. Done.
What you can build
The API is small on purpose. Primitives, booleans, transforms, patterns. That's the whole language. But it composes well.
An L-bracket with mounting holes in both faces:
let base = centered_cube("base", 60.0, 40.0, 4.0);
let wall = centered_cube("wall", 60.0, 4.0, 36.0)
.translate(0.0, -18.0, 20.0);
let bracket = base + wall - base_holes - wall_holes;
A flanged hub with a bolt circle:
let hub = centered_cylinder("hub", 15.0, 20.0, 64);
let flange = centered_cylinder("flange", 30.0, 4.0, 64)
.translate(0.0, 0.0, -10.0);
let part = hub + flange - bore - bolt_pattern(6, 45.0, 3.0, 8.0, 32);
A radial vent pattern cut from a disc — one slot, repeated eight times:
let slot = centered_cube("slot", 15.0, 2.0, 10.0);
let vents = slot.circular_pattern(20.0, 8);
let panel = centered_cylinder("panel", 35.0, 3.0, 64) - vents;
Every part here is parametric. Change the bolt count, the radius, the wall thickness — one number changes and the whole part regenerates. No clicking. No undo. Just recompile.
Multi-material export
STL is fine for 3D printing, but it throws away all material information. For visualization, vcad exports glTF scenes with PBR materials defined in TOML:
let materials = Materials::parse(r#"
[materials.body]
color = [0.32, 0.72, 0.95]
metallic = 0.1
roughness = 0.5
[materials.antenna]
color = [0.95, 0.3, 0.35]
metallic = 0.3
"#).unwrap();
let mut scene = Scene::new("mascot");
scene.add(body, "body");
scene.add(antenna_ball, "antenna");
export_scene_glb(&scene, &materials, "mascot.glb").unwrap();
That's our mascot. Entirely CSG. A rounded cube intersected with a sphere for the body, spheres for eyes, cylinders for arms. Eight materials, seventeen parts, one GLB file.
Why Rust
The geometry engine is manifold, which guarantees watertight meshes from boolean operations. The Rust bindings give us zero-cost abstractions over the C++ core — the operator overloads compile down to direct manifold calls. No garbage collection pauses. No floating point surprises from a scripting layer.
But honestly, the main reason is the toolchain. cargo test runs 21 unit tests that verify volumes, surface areas, bounding boxes, and export round-trips. cargo clippy catches issues before they become parts with holes in the wrong place. Types prevent you from passing a radius where a diameter was expected.
CAD files should be code. Code has tests, reviews, diffs, and CI. An STL file has... bytes.
Built for agents
One thing I care about that most CAD tools don't: vcad is designed to be used by AI coding agents.
The README has full API tables, a cookbook with copy-pasteable recipes, and a section on Blender MCP integration. An agent can read the docs, generate a part, export it, import it into Blender, position a camera, and render a preview — all in one conversation.
Every render in this post was made that way. Claude generated the geometry with vcad, imported each STL/GLB into Blender via MCP, set up studio lighting, and rendered to PNG. No human touched Blender.
The feedback loop is: describe a part → code generates → mesh exports → render previews → iterate. All in the terminal.
Try it
cargo add vcad
- Docs: docs.rs/vcad
- Source: github.com/ecto/vcad
- Site: vcad.io
It's MIT licensed. The first version is 0.1 — there's a lot more to build. Fillets, chamfers, threads, an interactive web GUI. But the core is solid: primitives, booleans, transforms, export. Everything you need to stop clicking and start typing.
Go make something.
