Ever since I bought a 3d printer, I’ve wanted to dip a toe into data physicalization. I’m not very good at point-and-click 3d modelling tools, and this is kind of a casual hobby rather than serious work so I used Claude Code, an AI programming assistant, to vibe code a parametric Python program to generate the printable files. It was a real rush to find out I can just start verbally describing the object I want to an AI agent and then be holding it in my hand less than three hours later! So here’s the story of how I vibe CADed my very own 1960s-inspired Bertin reorderable matrix.
Some context up front:
When I sat down at my computer at 9pm on a Friday night with YouTube in the background after putting my kids to bed, I knew all the above but I didn’t really feel like figuring out how to use a CAD tool for the first time since engineering school 25 years ago: I just wanted to mess around with my 3d printer. So I fired up Claude Code and typed the following, just to see if it would work at all (I haven’t edited the prompts I used, just to really show what this kind of interaction feels like at the command line, typos and all since Claude doesn’t really care about formality):
I want you to write a Python program that produces a 3MF file that I can open up in Bambu Studio for multi-material printing (with AMS). I want you to define a 2cm cube out of material 1 and then on the top face of the cube I want a 1cm diameter disk that’s 0.5mm thick made out of material 2.
It mostly did! I opened up the resulting file in Bambu Studio but it wasn’t quite right. A few prompt iterations later, I just made the modification I wanted in Bambu Studio, saved my changes and asked Claude to compare the files to see what kind of output I was looking for. I still had no interest in actually looking at the 3MF files myself or the Python code that generated them, I just wanted to vaguely gesture at the problem and make it go away:
Bambu Studio isn’t really reading this as multiple materials quite right. It asks me “This file contains several objects positioned at multiple heights. Instead of considering them as multiple objects, should the file be loaded as a single object with multiple parts?”. If I say yes it does what I want and then I can manually assign the filaments. Please update the script such that this is all encoded in the file so I can open it in one click.
now it loads in one shot but it doesn’t register the cube and the disk as different materials, it’s just one merged object in one material
ok so I opened it in Bambu and assigned the second material and saved it as @modified.3mf … regen your file and compare the two to figure out what changed and then update the script to generate the same kind of thing as it saves
That worked really well: Claude is good at diffing text files and coding to a spec. At this point, about 15 minutes in, I had a pretty good sense that things were going to work great, so I dove in and told it what I actually wanted. I knew I wasn’t going to get the dimensions right the first time so I told it up front I wanted to be able to tweak parameters, thereby bumping up the level of abstraction a bit from a single specific object to a design space of objects. This is the part that I found the most exciting of this process, since I could just describe fairly naturally what I wanted and it mostly just got it, so much faster than pointing and clicking my way through a UI:
ok great, let’s work on parameterizing this thing.
Here are some parameters I want you to define in the code that we will use to build up the real object I want:
- layer thickness: 0.2mm (disk thickness is this)
- plate thickness: 2mm
- gap: 0.5mm
- square size: 2cm
- stick buffer: 3cm
- stick width: 4mm
- stick length in squares: 2
- overlay radius: 1cm
The current “cube” is going to be called a block made of material 1 and it will have a square base the size of the square. it will have an ‘overlay’ disk like we have now made of material 2 which is one layer thick and will have a radius. Our blocks will no longer be cubes but instead will be the height of 4 plates and 4 gaps because I want there to be two holes punched horizontally through the block such that I can fit a stick through with a gap all around. one hole will go front to back and one hole will go left to right.
It did a great job on its first shot, but I had to nudge it once since the “sticks” were cylinders and the holes were going to intersect (this is where you kind of hit some of Claude’s limitations in terms of how real-world objects function mechanically, but it was very easy to work through by giving it a worked example):
ok so clarifications:
- the sticks are going to be rectangular in cross section: 1 plate thick and one stick-width wide and as long N square-sides + the stick extra buffer. so please add one 2-square stick object (with current parameter values this means that it will be 2mm thick, 4mm wide and and 70mm long (2x20mm squares plus the 30mm buffer)).
- the holes also need to be rectangular. in this case the holes need to be 5mm wide and 3mm tall (because of the gaps)
- the holes themselves need to not intersect: there needs to be a plate thickness of block between them
At this point I’ve got Claude running a Python script for me that spits out a file with one block and one stick. Time to go up another level of abstraction and complexity since I want a full mechanism built out of these little blocks:
ok so what is the procedure for me to build a file? I have to write a short script that calls these utility functions? Can you suggest a way for me to define a file with a bunch of blocks and sticks?
Let’s go with python3 generate_multi_material.py –blocks 1,2,3,4 –sticks 2,2,2,2 –output myfile.3mf except for sticks I just want two parameters: number of sticks and length in blocks. also make all the parameters accessible from the CLI please so I can tweak the numbers and use argparse or something to generate a nice –help with some examples
ok so make me a file with 16 blocks (4 each of 2, 3, 4, 5mm overlays) and 8 sticks of size 4-blocks. i want the gap to be 0.3mm and the square size to be 15mm
At this point I have a fairly complete and usable Python script that I haven’t looked at even once that produces files in some format I don’t care to learn. I don’t even run the script myself: I ask Claude to run it for me and I just open up the resulting 3MF files in Bambu Studio and click around to see if what I see on the screen matches the mental model I have. Finally, a bit over 2 hours in, I send a sample 4-block/4-stick job to the printer and 30 minutes later I’m holding it in my hand. Pretty magical. The next day after a few tweaks (adding “label” blocks that are a bit longer, adding a “handle” to the sticks so they woudln’t just push all the way through) I printed the version in the video at the top of this post.
Zooming out a bit, this felt almost exactly like vibe coding some more traditional data visualization through a fairly generic pipeline:
figure object for visualization or a 3MF file for physicalization.I was really impressed at how smooth the process was of describing in my own words what I saw in my head and getting to the output I wanted, and letting Claude take care of all the intermediate technical details. Admittedly the thing I was printing was just a bunch of rectangles and cylinders, but the resulting assembly is pretty complex and works really well as a demonstration of the Bertin reorderable matrix!
PS: I did in fact crack open the Python script that Claude wrote and it looks reasonable. It’s about 1800 lines, it uses trimesh to create the primitives and then it creates the XML structure of the 3MF file verbosely. I chatted with Gemini 3 about the approach and it seemed to think this code is fairly low-level and could use some trimesh built-in 3MF export, but this is the beauty of low-stakes vibe coding: I don’t really care about the details of the process, I just wanted the output.
⁂