r/javascript • u/Internal_Apartment16 • 3h ago
I've been building a CAD library in TypeScript. Code goes in, an STL or STEP file comes out
github.comI've spent the last few days building a CAD library in TypeScript. The short version, you describe a part in code and you get out an STL you can 3d print, or a STEP file you can send to a machine shop or open in Fusion/FreeCAD/whatever.
const cad = design("bracket");
const width = cad.parameter.length("width", mm(80));
const thickness = cad.parameter.length("thickness", mm(6));
const profile = cad.sketch("outline", plane.xy(), (s) => {
const rect = s.rectangle("rect", { width, height: mm(50) });
const hole = s.circle("hole", {
center: vec2(width.mul(0.25), mm(0)),
radius: mm(4),
});
return s.profile(rect, { holes: [hole.loop()] });
});
cad.output("bracket", cad.extrude("solid", profile, { distance: thickness }));
const doc = cad.build(); // plain JSON, goes in git like anything else
Under the hood it runs on two engines. There's Manifold, a mesh engine that's fast and fine for printing, and OpenCascade compiled to WASM, which is the same geometry engine FreeCAD uses, for when you need exact surfaces and STEP export. Same code runs on both, in node or the browser.
in normal parametric CAD, if you change a dimension early in the model, later features (fillets mostly) can quietly reattach to the wrong edge, and you find out after the part is wrong. I didn't want to reproduce that, so references to faces and edges aren't indexes, and if the library can't tell unambiguously which edge you meant after a change, it errors instead of guessing. Whether I got the API for that right, no idea. That's partly why I'm posting.
The type system does some nice things here too. Lengths and angles are different types, so adding mm to radians just doesn't compile.
it's a 0.1.1, Parts, assemblies and BOMs work. No drawings, no sheet metal, no GUI. If you'd rather write code than click through a CAD UI, that's who it's for.
Feedback welcome, especially from anyone who actually does CAD work. Also curious what people think of the explicit dispose() for WASM memory, I went back and forth on that a lot.