Interactive Preview & Customizer
3D preview with browser-based OpenSCAD rendering on parameter changes.
Loading preview...
The text to display on the label
Font name (ensure it is installed on your system)
Font size in mm
step 1
Letter spacing multiplier
step 0.1
Position of the keychain loop
Diameter of the hole inside the keychain loop (mm)
step 0.5
Extra outer ring margin thickness around the hole (mm)
step 0.5
Manual X-axis offset adjustment for the loop (mm)
step 1
Manual Y-axis offset adjustment for the loop (mm)
step 1
Height of the bottom-most background layer (mm)
step 0.1
Height of the middle border layer (mm)
step 0.1
Height of the top text layer (mm)
step 0.1
Outer border offset width (mm)
step 0.1
Inner border offset width (mm)
step 0.1
Choose what to render (for exporting separate STLs/3MF parts)
Resolution of the rounded offsets
step 1
WASM compiler loads on first parameter change.
Prompt
Generate 3D printable extruded textual keychain, with optional keychain loop
OpenSCAD Code
/* [Text Settings] */
// The text to display on the label
label_text = "HelloWorld";
// Font name (ensure it is installed on your system)
font_name = "Liberation Sans:style=Bold Italic"; // font
// Font size in mm
font_size = 20; // [10:1:100]
// Letter spacing multiplier
letter_spacing = 1; // [0.5:0.05:2.0]
/* [Keychain Loop Settings] */
// Position of the keychain loop
keychain_position = "left"; // [none: None, top: Top, left: Left]
// Diameter of the hole inside the keychain loop (mm)
keychain_hole_diameter = 4; // [2.0:0.5:15.0]
// Extra outer ring margin thickness around the hole (mm)
keychain_ring_thickness = 3; // [1.0:0.5:10.0]
// Manual X-axis offset adjustment for the loop (mm)
keychain_offset_x = -28; // [-100:1:100]
// Manual Y-axis offset adjustment for the loop (mm)
keychain_offset_y = 0; // [-100:1:100]
/* [Layer Thicknesses] */
// Height of the bottom-most background layer (mm)
base_height = 2; // [0.5:0.1:10.0]
// Height of the middle border layer (mm)
middle_height = 1.2; // [0.2:0.1:5.0]
// Height of the top text layer (mm)
top_height = 1.2; // [0.2:0.1:5.0]
/* [Offsets] */
// Outer border offset width (mm)
base_offset = 6; // [1.0:0.1:15.0]
// Inner border offset width (mm)
middle_offset = 3.5; // [0.5:0.1:10.0]
/* [Output Settings] */
// Choose what to render (for exporting separate STLs/3MF parts)
render_mode = "all"; // [all: All Layers, base: Base Only, middle: Middle Only, top: Top Only]
/* [Quality] */
// Resolution of the rounded offsets
$fn = 32; // [16:1:128]
/* [Hidden] */
epsilon = 0.01;
// Approximate text boundary dimensions for loop positioning
char_count = len(label_text);
approx_width = char_count * font_size * 0.45 * letter_spacing;
approx_height = font_size * 0.75;
// Determine automatic base alignment coordinates
auto_x = (keychain_position == "left") ? -(approx_width / 2 + base_offset) : 0;
auto_y = (keychain_position == "top") ? (approx_height / 2 + base_offset) : 0;
loop_x = auto_x + keychain_offset_x;
loop_y = auto_y + keychain_offset_y;
module text_shape() {
text(
text = label_text,
font = font_name,
size = font_size,
spacing = letter_spacing,
halign = "center",
valign = "center"
);
}
module base_layer() {
difference() {
linear_extrude(height = base_height) {
union() {
offset(r = base_offset) {
text_shape();
}
if (keychain_position != "none") {
translate([loop_x, loop_y]) {
circle(d = keychain_hole_diameter + 2 * keychain_ring_thickness);
}
}
}
}
if (keychain_position != "none") {
translate([loop_x, loop_y, -epsilon]) {
cylinder(h = base_height + 2 * epsilon, d = keychain_hole_diameter);
}
}
}
}
module middle_layer() {
translate([0, 0, base_height]) {
linear_extrude(height = middle_height) {
offset(r = middle_offset) {
text_shape();
}
}
}
}
module top_layer() {
translate([0, 0, base_height + middle_height]) {
linear_extrude(height = top_height) {
text_shape();
}
}
}
// Render logic
if (render_mode == "all") {
color("White") base_layer();
color("Black") middle_layer();
color("Red") top_layer();
} else if (render_mode == "base") {
base_layer();
} else if (render_mode == "middle") {
middle_layer();
} else if (render_mode == "top") {
top_layer();
}