Skip to main content

Capistry

A Python package for parametric 3D modeling of keyboard keycaps using build123d.

Docs License: MPL 2.0 PyPI Version Python 3.13+

Rendered keycap model created with Capistry

Table of Contents

Overview

  • Parametric Design: Create custom keycaps with precise control over dimensions, angles, and styling.
  • Shapes: Rectangular, slanted, skewed, and trapezoidal keycap shapes.
  • Stems: Compatible with MX and Choc switches.
  • Advanced Geometry: Tapers, fillets, and surface modeling.
  • Batching: Generate panels of multiple keycaps for 3D printing.
  • Exporting: Export to any format supported by build123d, including STL, STEP, DXF, SVG, and more.
  • Ergogen: Export Ergogen configurations consisting of keycap positions and outlines.
  • Extensible: Designed to be extensible - you can create custom stems, cap classes, fillet strategies, and sprue designs by extending the base classes.

Installation

pip install capistry

Requirements

  • Python 3.13+
  • Dependencies:
    • build123d
    • rich
    • mashumaro
    • more-itertools
    • attrs
    • tzdata

Quick Start

from build123d import *
from capistry import *
import logging

# Initialize logging
init_logger(level=logging.INFO)

# Create a basic rectangular keycap
cap = RectangularCap(
    width=18,
    length=18,
    height=5,
    wall=1.25,
    roof=1.25,
    taper=Taper.uniform(7),
    stem=MXStem(center_at=CenterOf.GEOMETRY),
    fillet_strategy=FilletUniform()
)

# Export as STL
export_stl(cap.compound, "keycap.stl")

# Create a sprued grid-like panel for 3D printing
panel = Panel(
    items=[
        PanelItem(cap, quantity=10, mirror=True),
    ],
    sprue=SprueCylinder(),
)

# Export panel as STL
export_stl(panel.compound, "panel.stl")

Features

Keycap Shapes

As of now, all keycap shapes are quadrilaterals, i.e. four-sided polygons.

RectangularCap

Standard rectangular keycap:

cap = RectangularCap(width=18, length=18, height=5)

SlantedCap

An asymmetric keycap where two sides are angled evenly away from their respective orientations. The resulting shape has two orthogonal (90°) corners, one corner at 90 + v° and another at 90 – v°, where v is the slant angle.

cap = SlantedCap(width=18, length=18, height=5, angle=7)

SkewedCap

Keycap with a parallelogram shape:

cap = SkewedCap(width=18, length=18, height=5, skew=5, skew_width=True)

TrapezoidCap

Keycap with a trapezoidal shape:

cap = TrapezoidCap(width=18, length=18, height=5, angle=5)

Stems

Support for different stems:

# MX-style stem
stem = MXStem()

# Choc-style stem
stem = ChocStem()

cap = RectangularCap(stem=stem)

Tapering

Control the slope of keycap sides:

# Uniform taper on all sides
taper = Taper.uniform(7)

# Side-specific tapering
taper = Taper(front=10, back=7, left=4, right=4)

cap = RectangularCap(taper=taper)

Fillet Strategies

Choose how edges are rounded:

# Uniform
strat = FilletUniform()

# Front-to-back, then left-to-right
strat = FilletDepthWidth()

# Left-to-right, then front-to-back
strat = FilletWidthDepth()

# Mid-height edges, then top-perimeter
strat = FilletMiddleTop()

cap = RectangularCap(fillet_strategy=strat)

Surface Modeling

The top face of keycaps can be precisely modeled by defining a Surface. A Surface is represented by a matrix (a 2-dimensional list), specifying the offset values which will be used to model the top face. Optionally, a weights matrix may also be supplied.

surface = Surface(
        [
            [4, 4, 4, 4],
            [2, -1, -1, 2],
            [0, -1, -1, 0],
            [0, 0, 0, 0],
        ]
    )

cap = TrapezoidCap(surface=surface)

Comparisons

You can compare caps, fillets, stems, and surfaces using the capistry.Comparer class. This is useful for identifying both dimensional differences and property variations.

# Create two keycaps with different parameters
c1 = RectangularCap(width=18, length=18, height=5)
c2 = RectangularCap(width=19*2 - 1, length=18, height=5)

# Create a comparer
comparer = Comparer(c2, c2)

# Show the comparison, outputs a rich table in the console
comparer.show(show_deltas=True)

Panel Generation

Create grid-like panels of keycaps for simpler 3D printing by creating a capistry.Panel:

cap = RectangularCap()

# Create a 4x4 panel of a keycap
panel = Panel(
    items=[PanelItem(cap, quantity=16)],
    sprue=SprueCylinder(),
    cols=4
)

# Export the entire panel
export_stl(panel.compound, "keycap_panel.stl")

Ergogen Export

Export your keycaps in their current locations to an Ergogen configuration file.

# Create two rectangular keycaps
c1 = RectangularCap()
c2 = RectangularCap()

# Position the second keycap to the right of the first
c2.locate(c1.top_right)

#  Create an ergogen instance, and write the configuration to a YAML file
ergogen = Ergogen(c1, c2)
ergogen.write_yaml("config.yaml", precision=3)

Documentation

Full API documentation is available and generated using pdoc.

👉 View the full documentation here

Examples

For more detailed examples, see the examples/ directory in the repository.

Development

Building from Source

git clone https://github.com/larssont/capistry.git
cd capistry
uv sync

License

This project is licensed under the Mozilla Public License 2.0 - see the LICENSE.md file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Acknowledgments

  • build123d — CAD library powering Capistry

Metadata

Release files for capistry 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for capistry 0.2.0
File Size Uploaded
capistry-0.2.0.tar.gz 36.6 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for capistry 0.2.0
File Interpreter ABI Platform
capistry-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 36.7 MB

Release files / capistry-0.2.0.tar.gz

Download URL capistry-0.2.0.tar.gz
Size 36.6 MB
Tags Source
SHA-256 checksum
How to use checksums
5497527236e98affb027c1cc181c24be39bf44929a9f01c724d83123230fac15
BLAKE2b-256 checksum
How to use checksums
f4b5a56f791b3522ea17adcd6e6edad15d0dc573372b932323d50ff73cfa5cbc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 15, 2025.

Transparency log

Release files / capistry-0.2.0-py3-none-any.whl

Download URL capistry-0.2.0-py3-none-any.whl
Size 62.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e046f01b9a3ed673dfa62f227d86862db2028e348eb2b2ae308eaba5a101b22f
BLAKE2b-256 checksum
How to use checksums
8c20b877f4c7f1cdf85e35363bd7cf094e0a54c731e9d04e7ed5754f0e78352e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 15, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page