Creating the CAD models
The process of creating our CAD models is relatively straightforward, but could require more advanced skills for more complex layouts. We need a keyboard layout which we’ll then convert into CAD models. After the models have been created, we will clean them up a bit and convert them to a format that the laser cutter can handle.
Keyboard Layout
To begin, we’ll need to decide on a layout. I used the very handy Keyboard Layout Editor tool, which allows users to create, share, and save keyboard layouts, and even has a number of examples. Let’s grab the default numpad layout, and save it as a .json.

While we’re at it, let’s create a project folder to organize everything under, including our new layout file. We’re now ready to create the CAD files, but first we’re going to need the software.
Required CAD Software
To generate CAD files, we’ll use the following pieces of software:
- OpenSCAD
- CAD software that focuses on programmatic design
- InkScape
- Also requires this extension
- Reads in OpenSCAD output to make simple modifications
- Exports a format for the laser cutter
- BoardBuilder
- Python module which does the dirty work of converting a layout .json to SCAD
- Setting up and installing a Python environment is out of this guide’s scope
- Sorry! I have to stop with the details somewhere
Now that we have our software installed, and a Python environment ready with the BoardBuilder module, we can get started on generating our CAD files – the top (or faceplate), the middle (where the wires and microcontroller will go), and the bottom, which keeps everything together.
Creating the OpenSCAD files
We’ll use a fairly straightforward Python script to generate the CAD models using the BoardBuilder module. Here is the full python source code, which is also available (and possibly updated) in the GitHub for the project linked previously:
#Set up some parameters
#Most are in mm
import os
from BoardBuilder.BoardBuilder import BoardBuilder#Get the current directory and files
file_directory = os.path.dirname(os.path.realpath(__file__))
layout_name = “numpad-layout.json”
layout_path = os.path.join(file_directory, layout_name)
output_dir = ‘./out/’#Set up the parameters
stabilizers = “costar”
horizontal_padding = ’12’
vertical_padding = ‘35,12’
corner_radius = 6
screw_count = 6
screw_diameter = 5
screw_padding = 6
screw_side_count = 6#Build the board
board = BoardBuilder(layout_path, horizontal_padding, vertical_padding, corner_radius, screw_count, screw_diameter, False, stabilizers, screw_padding, screw_side_count)
board.render_mid_layers(output_dir)#The top and bottom have different holes for the screws
screw_diameter_face_bottom = 3
screw_side_count_face_bottom = -1
board = BoardBuilder(layout_path, horizontal_padding, vertical_padding, corner_radius, screw_count, screw_diameter_face_bottom, False, stabilizers, screw_padding, screw_side_count_face_bottom)
board.render_top_plate(output_dir)
board.render_bottom_plate(output_dir)
Whew! That’s a lot to look at all at once. It’s OK, I’ll break it down into pieces.
Imports
#Set up some parameters #Most are in mm import os from BoardBuilder.BoardBuilder import BoardBuilder
This is very simple. We import our os library, which is useful for file paths. We also import the BoardBuilder module, which is located in the BoardBuilder folder/file.
File Paths
#Get the current directory and files file_directory = os.path.dirname(os.path.realpath(__file__)) layout_name = "numpad-layout.json" layout_path = os.path.join(file_directory, layout_name) output_dir = './out/'
Now we get all of our file paths sorted out. We get the current directory we’re running the script in, and build the paths to the layout file which is in the same directory. We also specify that the output should be in a folder named ‘out’.
Board Parameters
#Set up the parameters stabilizers = "costar" horizontal_padding = '12' vertical_padding = '35,12' corner_radius = 6 screw_count = 6 screw_diameter = 5 screw_padding = 6 screw_side_count = 6
This is where we define the parameters used to generate the CAD. You’ll notice simple things like the corner radius and how many screws we want, but also more advanced parameters such as the padding on each side, as well as how many sides the ‘screws’ have. You can see that 6 sides are specified, which will produce a 6-sided (hexagonal) hole. You’ll see why later.
The Middle Layer
#Build the board board = BoardBuilder(layout_path, horizontal_padding, vertical_padding, corner_radius, screw_count, screw_diameter, False, stabilizers, screw_padding, screw_side_count) board.render_mid_layers(output_dir)
Here we create our board builder with the above parameters, and have it render the middle layer to our output directory. This creates our OpenSCAD file for the middle layer.
The Top and Bottom Layers
#The top and bottom have different holes for the screws screw_diameter_face_bottom = 3 screw_side_count_face_bottom = -1 board = BoardBuilder(layout_path, horizontal_padding, vertical_padding, corner_radius, screw_count, screw_diameter_face_bottom, False, stabilizers, screw_padding, screw_side_count_face_bottom) board.render_top_plate(output_dir) board.render_bottom_plate(output_dir)
Finally we make the faceplate and the bottom. However, we want the screw diameters to be smaller, so we can countersink the screws into the layers, and circular holes. And that’s it – the Python script sets things up by importing modules and setting variables, and then creates a middle layer with larger hex holes, the faceplate and bottom layers with circular smaller holes we’ll countersink later.
Let’s open each SCAD file to take a look:

Converting OpenSCAD to Vector
You now have your very own CAD files, however these are not something that a laser cutter can use yet. We’ll need to convert them into a format that your laser cutter can actually do something with. Now, the exact format will depend on the machine’s firmware, but in my case I need a .lyz file. To create this, we’ll output our CAD files as a vector format that InkScape can read.
To do this in OpenSCAD, export the model as a .dfx.

You’re done with OpenSCAD. Now, if you’re CAD savvy you can use these models and do your own custom modifications, but this will work for the case of this tutorial.
Converting with InkScape
We’ve got our .dfx files, which InkScape can utilize. However, this particular laser cutter requires a .lyx file. Since OpenSCAD couldn’t do this automatically we’re going to use InkScape to handle it. Before we export, we’re going to make a slight modification: a hole for the cable! When we generated the CAD model, it didn’t have a spot for the USB cable to exit – we could have added this into the model but we’re going to do it now! You can use the files in GitHub for guidance.

We have our layers ready to go – you shouldn’t have to touch the faceplate and the bottom but it doesn’t hurt to take a look! For my case, the lines needed to be red to mark a cut – blue and black lines implied a raster fill and were not what I wanted, so double check any color requirements. To export, hit File->Save As, and change the output type to .lyz.

And we’re ready to cut! At this point, we’ve taken all the required steps for generating the CAD models; now we can get to the fun part!