A tool for LLMs (specifically Claude on claude.ai) to design 3D printable models using OpenSCAD with a visual feedback loop. The key insight: LLMs can't do spatial reasoning blind. They need to render → see → fix → repeat.
This is the core workflow that makes LLM-driven CAD actually work. Without it, you're guessing and will get geometry wrong every time.
These tools are available in Claude's container environment:
# Install OpenSCAD (may already be installed) apt-get update && apt-get install -y openscad xvfb # Verify xvfb-run -a openscad --version
1. WRITE → Create/edit a .scad file
2. RENDER → xvfb-run -a openscad -o render.png --imgsize=800,600 \
--camera=200,200,150,0,0,40 model.scad
3. VIEW → Use the `view` tool on render.png — YOU CAN SEE THE IMAGE
4. ASSESS → What's wrong? Too flat? Wrong orientation? Missing features?
5. FIX → Edit the .scad file
6. GOTO 2
The --camera flag takes: eyeX,eyeY,eyeZ,centerX,centerY,centerZ
# Isometric (best for first look) --camera=200,200,150,0,0,40 # Front view --camera=0,250,40,0,0,40 # Top down --camera=0,1,300,0,0,30 # Bottom up (to see underside) --camera=0,1,-300,0,0,20 # Orthographic projection (engineering drawings) --camera=100,0,40,0,0,40 --projection=o
If the model is too big/small in frame, adjust the eye distance (first 3 numbers).
To see internal geometry (cavities, bores, channels), cut the model in half:
difference() { // Your full model here my_model(); // Cut away half (adjust plane as needed) translate([0, -200, -10]) cube([400, 400, 200]); // removes Y > 0 }
Then render from the cut side to see inside.
Always render at least 3 views:
- Isometric — overall shape check
- Front/Side — proportions, symmetry
- Cross-section — internal geometry verification
xvfb-run -a openscad -o iso.png --imgsize=800,600 --camera=200,200,150,0,0,40 model.scad xvfb-run -a openscad -o front.png --imgsize=800,600 --camera=0,250,40,0,0,40 model.scad xvfb-run -a openscad -o section.png --imgsize=800,600 --camera=200,0,40,0,0,40 section.scad
- CLI rendering:
openscad -o file.pngworks headless with xvfb. JSCAD has no CLI renderer. - Visual feedback: The whole point is render → view → fix. JSCAD can only output geometry data, not images.
- Simpler syntax: OpenSCAD's declarative style is easier for LLMs than JSCAD's imperative JS.
- Cross-sections: OpenSCAD's
difference()with a cutting plane lets you see inside models. - Well-documented: OpenSCAD has extensive docs and examples the LLM already knows.
// Primitives cube([x, y, z]); // box cube([x, y, z], center=true); // centered box cylinder(r=10, h=20); // cylinder cylinder(r1=10, r2=5, h=20); // cone/taper sphere(r=10); // sphere // Transforms translate([x, y, z]) child(); rotate([rx, ry, rz]) child(); scale([sx, sy, sz]) child(); // Booleans (THE KEY OPERATIONS) union() { a(); b(); } // combine difference() { a(); b(); } // a minus b intersection() { a(); b(); } // overlap only // Hull (convex hull — great for organic shapes) hull() { a(); b(); } // Loops for (i = [0:4]) translate([i*10, 0, 0]) cube(5); // Modules (reusable parts) module my_part(size=10) { difference() { cube(size); sphere(r=size*0.6); } } // Resolution $fn = 64; // smooth curves (put at top of file)
-
Threads on the outside: When making internal threads (female), you SUBTRACT rings slightly larger than the bore. The grooves go INTO the wall, not out.
-
Orientation confusion: Always define your Z-axis convention upfront. For 3D printing: Z = up, build plate is Z=0.
-
Forgetting to look: ALWAYS render and view before declaring something done. You WILL get geometry wrong on the first try.
-
Camera too close: Start with eye distances of 200+. If you see a wall of color, zoom out.
-
Flat chambers: If you want a cone/pyramid shape, make sure height > base radius. Otherwise it looks like a disc.
-
Overcomplicating: Start with the simplest possible shape. A manifold is just cylinders entering a hollow cone. Don't add channels, hulls, and complex merging unless actually needed.
main.ts — HTTP server: viewer page + design API
designs/ — OpenSCAD design files
bottleSplitter.scad — 3-way bottle manifold
README.md — This file (YOU ARE HERE)
GET / → viewer page (Three.js + OpenSCAD WASM)
GET /api/designs → list all designs
GET /api/designs/:id → get design SCAD code
POST /api/designs/:id → save design { name, scad }
This approach is based on CADCodeVerify (ICLR 2025) which showed that LLMs need visual feedback loops for CAD:
- Render the model
- Vision model inspects the render
- Provides corrective feedback
- Iterate until correct
Our implementation uses OpenSCAD CLI + xvfb for rendering and Claude's view tool for visual inspection. It's the same principle, just using tools already available in the environment.
If you're picking this up:
- Install first:
apt-get install -y openscad xvfb(may already be there) - Test the loop: Write a cube.scad, render it, view it. Make sure you can see images.
- Read the designs/: See what exists already
- Iterate visually: Never commit a design you haven't rendered and viewed from multiple angles
- Cross-section complex parts: If there's internal geometry, cut it open and look inside
- Keep it simple: Most printable objects are just cylinders, cubes, and cones with boolean operations