Designing 3D Printed Board Game Inserts with Python

Mark Lavin
The Startup
Published in
8 min readJan 18, 2020
My finished insert for Burgle Bros

I love playing board games. I play at least once a week with a regular group and much more than that with my wife after the kids are in bed. More of my most recent hobbies have revolved around gaming, like miniature painting or dabbling back into Magic the Gathering (don’t judge me). When I received a 3D printer as a Christmas gift, I knew exactly what I wanted to do with it: make board game pieces and inserts. Having a great insert can really help with the setup/tear-down time for a game, and that means more time for playing.

I had made a few inserts before out of foam core, and they turned out pretty good. But the problem with foam core is that cutting it felt imprecise. Maybe I’m just bad at measuring, or at using an EXACTO knife. Also the 5 mm foam core I was using started to cut into the amount of space I had to work with, particularly if I wanted to make lots of small holds. I was hopeful that 3D printing would allow me better precision to make more complex designs than I could do with foam core.

Setup game of Burgle Bros

That was the dream, but I needed a place to start. One of the first foam inserts I made was for a game called Burgle Bros by Tim Fowers. It’s a fantastic cooperative game when you work together as a gang of thieves robbing a bank. The art is amazing and it comes in this really cute box. By cute I mean both that it’s great looking but also it’s tiny. We would always joke that trying to get all the pieces back into the box is the game after the game. In any case I had a design for an existing insert I had previously made for this game. Now all I had to do was turn that design into something I could print.

3D Printing Primer

Many articles have been written about how 3D printers work so let me skip to the most relevant parts. My Ender3 printer and most FDM printers use g-code to program their movements. Where the nozzle should be, how hot should the nozzle or the bed be, and when and how much filament to extract is all controlled by g-code. I know very little about g-code, and thankfully you don’t need to either if you want to print something. The way most prints are made is by running a 3D model, like an STL file, through a program called a slicer to output the necessary g-code to print. I use a free slicer called Cura which I’ve configured for my printer and the filament I’m using. That process itself took a lot of trial and error, but that’s not the topic for today.

With all that we get the heart of the problem. If we want to 3D print something then we need a model of it. There are plenty of models available on sites like Thingiverse but someone has to be the one to start to create these designs. If you are capable with 3D modeling software then you should feel right at home. I did some early experiments with TinkerCAD, but I quickly became frustrated with repetitiveness of what I wanted to do. I just wanted to make some boxes and cut holes out of them. Using a GUI interface felt so imprecise, so I started looking for a better way.

Generating STL Files with Python

When I first looked into 3D printing with Python, I thought that I could go straight to generating the necessary g-code. I quickly abandoned that approach. There weren’t a lot of good resources and ultimately g-code isn’t a great format to share because it contains machine and filament specific configuration build into it. When researching how I could programmatically create STL files, I came across OpenSCAD and later SolidPython. OpenSCAD has a scripting language built into it that can be used to generate solid shapes by building them up as the composition and intersection of other shapes. SolidPython is a Python library which allows you to write Python which can be converted into OpenSCAD, which can generate the STL files.

While I could have used the OpenSCAD language directly, using Python to generate it instead allowed me to get working quickly without having to learn a new language or program on top of the things I was already learning about using the slicer and my printer. At this point I was looking for anything to lower the barrier to entry. With any project, I like to see incremental progress and once I have something working, I can go back, reevaluate, and refine what I’ve done.

Making Our First Box

Let’s go ahead and make our first open box. That’s going to be a fundamental building block for all of our board game inserts. This is where my previous experiment with TinkerCAD helped me to visualize what I needed to do. An open box can be created out of two shapes: the outer rectangular solid which forms the bottom and walls and the inner rectangular solid which is the negative space which forms the inside of the box. The inner solid needs to be slightly smaller than the outer and that difference will determine the thickness of the walls. We can adjust the thickness of the bottom by how we position the box vertically inside. Here I decided to make both the walls and the bottom 3 mm thick.

# box.py
import solid
# Size for our box
length, width, height = 100, 150, 50
# Wall thickness
wall = 3
box = solid.cube(size=[length, width, height])
# Inner box needs to be smaller based on the wall size
inside = solid.cube(
size=[length - 2 * wall, width - 2 * wall, height - wall])
# Offset inner box
inside = solid.translate([wall, wall, wall])(inside)
# Remove inner box from the outer to create the empty space
box += solid.hole()(inside)
solid.scad_render_to_file(box, "box.scad", include_orig_code=False)

Running this script will output a box.scad file which we can convert to an STL file using OpenSCAD.

# Make sure you have SolidPython installed
$ python -m pip install solidpython
# Run the above script saved as box.py
$ python box.py
# Convert scad to stl
$ openscad -o box.stl box.scad

Now this STL file is ready for our slicer. We can make this a little fancier by rounding the corners. I’m not sure why this makes it feel fancier to me. Maybe I’m showing my age as a web developer back to when rounded corners were all the rage. In any case, to round each of the corners we’ll add a cylinder to each of the corners. Shout out to William Adams on Thingiverse for his SCAD example post for the basis of this logic.

# box.py (continued)def roundbox(size, radius):
"""Box with rounded edges."""
x, y, z = size
# Adjust the box size to maintain the original dimensions
x = x - radius * 2
y = y - radius * 2
z = z - 1
return solid.minkowski()(
solid.cube(size=[x, y, z]),
solid.cylinder(r=radius),
)

To make use of this for the example box, you would replace the calls solid.cube() calls with roundbox. We’ll be making use of this function for our insert.

Creating the Burgle Bros Insert

Once we have the logic to create an open box, we aren’t too far from what we want for our insert. As I mentioned, I had previously created this insert in foam core so I had a plan from the start. My insert was made from two thin boxes, one with four equal compartments for the tokens and another with three compartments for the dice, guard pawns, and player pawns. As the foam core is about about 5 mm thick and the printed walls were going to be only 3 mm thick, I had to adjust the sizes slightly. That gave me more room to work with in this already cramped box. Both boxes are 171 mm x 60 mm x 15 mm, but have different sized wells inside of them to hold the different pieces.

# box.py (continued)wall = 3
# Tokens holder
x, y, z = 171, 60, 15
box = roundbox([x, y, z], radius=2)
# Make 4 equal size wells
wells = 4
size = (x - (wells + 1) * wall) / wells
well = roundbox([size, y - wall * 2, z - wall], radius=2)
for i in range(wells):
box += solid.hole()(solid.translate(
[wall * (i + 1) + size * i, wall, wall])(well))
# Output box to SCAD file
solid.scad_render_to_file(
box, "tokens.scad", include_orig_code=False)
# Character, guards and dice holder
x, y, z = 171, 60, 15
box = roundbox([x, y, z], radius=2)
# Character holder (77 mm)
characters = roundbox([77, y - wall * 2, z - wall], radius=2)
box += solid.hole()(solid.translate([wall, wall, wall])(characters))
# Guard holder (52 mm)
guards = roundbox([52, y - wall * 2, z - wall], radius=2)
box += solid.hole()(solid.translate(
[wall * 2 + 77, wall, wall])(guards))
# Dice holder (30 mm)
dice = roundbox([30, y - wall * 2, z - wall], radius=2)
box += solid.hole()(solid.translate(
[wall * 3 + 77 + 52, wall, wall])(dice))
# Output box to SCAD file
solid.scad_render_to_file(
box, "characters.scad", include_orig_code=False)

Combining this with our previous code and running it will generate the SCAD files which can again be converted into STL files using OpenSCADs command line interface.

# Run the above script saved as box.py
$ python box.py
# Convert scad to stl
$ openscad -o tokens.stl tokens.scad
$ openscad -o characters.stl characters.scad

Now we have two STL files that we can load into our slicer of choice. The print settings that you use will depend on your printer, but I printed mine at 0.2 mm layer thickness which gave enough accuracy for these simple shapes. The shapes don’t have any vertical overhang so they don’t require any supports. You may want to include a skirt or brim for better bed adhesion. Some of my early tests had problems with the bottom layers curling. That was addressed by lowering the nozzle temperature, which will depend on the material you are using.

They turned out great. Even though the end to end time is probably longer than it would have taken me to make it out of foam core, it’s much less labor intensive and the sizing is much more precise then I could get with foam core. The Python here is pretty rough but a good first pass and hopefully clear enough to be a starting point for you if you want to start making your own creations.

It’s great when multiple hobbies or interests can come together. Having a 3D printer to make these inserts is a complete luxury. With a little math, some planning, and patience, you can make well fitting inserts. For me, programming the logic in Python made it much easier for me to understand and get it done. It’s a fun challenge if you like math and puzzles like me. I’ll be following up with some of my more complex insert designs in future posts, so if you enjoyed this one, be on the look out for those.

--

--

Mark Lavin
The Startup

Python/web developer with too many random hobbies. I work for NVIDIA but my opinions are my own.