Parametric CAD for 3D printing. Runs on Linux, Windows and macOS.
SindriCAD is a history-based solid modeler. You sketch, extrude, fillet, and pattern your way to a part, and every step stays editable in a feature tree. It is not a mesh editor, and it is not a geometry kernel of its own. It drives build123d on top of OpenCASCADE for the actual geometry, and puts a real modeling UI and a print workflow on top.
It began on Linux, where good parametric CAD has always been thin on the ground, and it runs natively on all three desktops now: Linux, Windows and macOS. It is built for 3D printing: color a multi-material model, export it as a ready-to-slice OrcaSlicer project set up for the Snapmaker U1, and send the sliced G-code to the printer over the LAN.
Named for Sindri, the dwarven smith of Norse myth.
Status: beta, in ongoing development. SindriCAD already builds real printed parts, but the feature set is still filling out and rough edges remain. Expect frequent releases, report what breaks, and keep backups of documents you care about.
Get it ·
What it does ·
Sketching ·
Textures ·
Measure ·
Import ·
Print pipeline ·
Document format
Install ·
Build and run ·
Architecture ·
Project layout ·
License ·
Support ·
Changelog
Installers for Linux, Windows and macOS are on the
latest beta release,
rebuilt automatically from every green main build. Free, no account needed, and
everything is bundled including Python and the geometry engine. The builds are
unsigned, so each platform asks you to confirm the first launch once:
see Install for the exact steps, and note that macOS is the awkward one.
If SindriCAD looks useful, starring the repo helps other people find it.
What changed in each build is in CHANGELOG.md, and the same notes appear on the release itself.
- Sketching with lines, arcs, circles, splines, rectangles, slots, polygons, and text in system fonts, plus associative patterns (bolt circles, grids, honeycomb) and a PlaneGCS constraint solver. Dimensions are entered on the canvas: type a value, press Tab to lock it, Enter to commit.
- Parameters and expressions: name a value once, use it anywhere a number goes, and write arithmetic between parameters. Change one number and the whole part follows.
- Features: Extrude (new body, join, cut, intersect, per region), Revolve, Loft, Sweep, Press/Pull (multi-face, and extrude up to a target surface), Fillet, Chamfer, Shell, Draft, Scale, Mirror, and patterns.
- Offset Face and Thicken: push selected faces along their own normals with the surrounding walls following, or give surface geometry a wall. Thicken is what turns a non-watertight mesh import, which arrives as read-only reference geometry, into a solid you can actually model with.
- Surface textures (see below).
- Direct editing: Move with a live ghost preview, Split, Combine, Delete Face with automatic healing, and a cleanup pass for messy imported geometry.
- Import STEP, BREP, STL, 3MF, OBJ, and GLB, with facet cleanup and STEP canonicalization, so imported parts come back as editable faces instead of a triangle soup. Export STEP, STL, 3MF, and GLB.
- References that survive edits: geometry is picked by queryable descriptors (an axis, a face normal, the nearest point), never by a topology index. Change an upstream parameter and a downstream fillet still lands on the right edge.
- Measure and section tools for checking a part before it prints.
- A print pipeline for the Snapmaker U1 (see below).
Press ? in the app for the full keyboard shortcut list.

A part is its history: sketch, extrude, fillet, cut, texture, and every step stays editable.
Sketches are constraint-driven. Dimensions are entered on the canvas: type a value, Tab to lock it, Enter to commit. A PlaneGCS solver keeps the rest of the profile consistent as you edit. Equal, parallel, perpendicular, concentric and the usual family of constraints render as glyphs you can click, and a reference dimension measures without driving. Conflicting and over-defined constraints are called out rather than silently ignored.
Textures turn a plain face into a tactile printed surface: knurling for grip, hexagon, rib or wave relief for looks, Voronoi and noise for organic breakup, or any grayscale image as a height map. Pick faces (or a whole body), set depth, scale, and angle, and the pattern is applied as real displaced geometry, not a shader trick: what you see is what the slicer gets.
The faceted patterns are built as exact lattices. Mesh vertices land on the pattern's own crease lines, so a knurl prints as crisp diamonds and a hexagon pattern as flat-topped cells with sharp walls, instead of the rounded mush a sampled height field gives. Patterns wrap around cylinders and cones: ribs and waves close on themselves at any angle, and the 2D lattices (knurl, hexagon) close at multiples of 90 degrees, which is geometry rather than a limitation to fix. Textures can also cut inward instead of embossing outward, and a two-tone mode prints the textured faces in a different palette color than the rest of the body.

Patterns wrap a full turn and meet themselves at the seam.
A section plane cuts the model live, so a shelled or hollow part can be checked from the inside before it prints. Drag the arrow along the axis, or type an exact offset.
Measure reports the true shortest distance between two faces or edges, not just their centres, along with the per-axis deltas, the centre-to-centre distance, and the angle between them.
STEP, BREP, STL, 3MF, OBJ, and GLB come in; STEP, STL, 3MF, and GLB go out. A STEP import arrives as real B-rep geometry. The faces above are ordinary selections on a body that was exported to STEP and read straight back in, so imported parts can be measured, sectioned, textured, and used as sketch planes instead of arriving as a triangle soup. Mesh formats are cleaned up on the way in, and STEP is canonicalized so its faces come back in a form the selectors can address.
SindriCAD carries print prep for the Snapmaker U1 multi-material printer from model to machine, so a colored parametric part reaches a print without a manual export dance.

Sketch text, extruded and mapped to its own filament slot.
- Multi-material and multi-color 3MF: assign palette colors to bodies and export an
OrcaSlicer project 3MF with per-object extruder (tool) mapping for the U1's tool
changer (
sidecar/project3mf.py). - Slicer handoff: "Open in OrcaSlicer" binds the U1 preset with your tuned process and filament, so you land ready to slice.
- Direct device layer: a Rust Moonraker client (
src-tauri/src/printer.rs) uploads G-code to the printer over the LAN with a filament-mapping dialog, reads the palette back from the printer, and monitors the running print.
U1 support will keep growing: I add features as I come up with them and have time for them.
A .sindri file is JSON: a parameter table and an ordered list of features.
Any numeric field is either a literal (5) or the name of a parameter ("width").

Change a parameter, the whole part follows, and the corner fillets stay on their edges.
Everything is bundled, including Python and the geometry engine, so there is nothing else to install. The builds are unsigned for now, so each platform asks you to confirm the first launch once. Open the section for your platform:
Windows (.exe or .msi, self-updating)
- Download
SindriCAD_<version>_x64-setup.exe(or the.msi) from the latest beta. - The build is unsigned, so SmartScreen will warn "Windows protected your PC". Click "More info", then "Run anyway".
SindriCAD needs Microsoft Edge WebView2, which Windows 10 and 11 already ship; the setup exe fetches it automatically if it is missing. Once installed, SindriCAD updates itself: it checks the beta release at startup and offers a one-click restart-and-update.
Linux (.AppImage, .deb or .rpm)
Grab the .AppImage (chmod +x, runs on any distro, updates itself in place), or
the .deb / .rpm (sudo dpkg -i / sudo rpm -i; updates come from your package
manager workflow, not in-app).
macOS (.dmg, needs a right-click to open)
The .dmg is unsigned: right-click the app, choose Open, then confirm. Apple code
signing is planned.
3Dconnexion SpaceMouse (optional, one udev rule on Linux)
SindriCAD reads a SpaceMouse natively for 6DOF camera navigation, with the two buttons mapped to Fit and Home/ISO. Plug it in: no driver or configuration needed. Sensitivity and axis inversion live in Settings.
On Linux the device node (/dev/hidrawN) is root-only until a udev rule
grants the logged-in user access. The .deb and .rpm packages install that
rule for you and reload udev, so a packaged install just works.
The AppImage cannot install system files, so it needs the rule once, by hand:
sudo curl -fsSL https://raw.githubusercontent.com/MakerViking/sindricad/main/packaging/99-spacemouse.rules -o /usr/lib/udev/rules.d/99-sindricad-spacemouse.rules
sudo udevadm control --reload && sudo udevadm trigger --subsystem-match=hidrawBuilding from a clone? Run sudo sh packaging/setup-spacemouse.sh instead. It
installs the same rule and applies it to an already-connected device.
If SindriCAD can see the device but can't open it, it says so in a notification
rather than failing silently. Two usual causes: the rule above is missing, or
spacenavd / the official 3Dconnexion driver is already holding the device.
Stop that service to let SindriCAD read it directly.
Prerequisites: Node, a Rust toolchain, Python 3.12, uv,
and WebKitGTK. See docs/PACKAGING.md for per-OS package names and
known-good versions. A system OpenCASCADE install is not needed for the default
build: the geometry sidecar ships its own OCCT inside its Python wheels. OCCT is only
needed for the opt-in rust-geom Cargo feature (see
docs/PACKAGING.md).
# 1. geometry sidecar (Python 3.12 via uv, locked versions from uv.lock)
cd sidecar
uv sync
uv run python test_smoke.py # backend sanity (rebuild/export/error naming)
uv run python test_ws.py # WebSocket transport sanity
# 2. the app, from the repo root. Tauri starts Vite and the sidecar for you.
npm install
npm run tauri devFor frontend-only iteration you can run the two halves separately:
cd sidecar && uv run python server.py # ws://127.0.0.1:8765
npm run dev # http://localhost:5173Note: on Linux a standalone
python server.pyarms PR_SET_PDEATHSIG and dies with the shell that started it. To keep one alive across shells (or on other platforms), run it in a terminal you keep open, and kill it by hand when done or it will hold port 8765.
┌─ Tauri shell (Rust) ──────────────────────────────────────┐
│ • native window, file dialogs │
│ • spawns and supervises the Python geometry sidecar │
│ • kills the sidecar on app exit (process-group + PDEATHSIG)│
│ │
│ ┌─ Frontend (TypeScript, in the webview) ──────────────┐ │
│ │ • Three.js viewport (orbit/pan/zoom, ViewCube, │ │
│ │ picking, Z-up) │ │
│ │ • UI: browser tree, timeline, parameters, toolbar │ │
│ │ • owns the DOCUMENT (feature tree + parameters) │ │
│ └──────────────────┬───────────────────────────────────┘ │
└─────────────────────┼──────────────────────────────────────┘
│ JSON over localhost WebSocket (ws://127.0.0.1:8765)
▼
┌─ Geometry sidecar (Python + build123d + OCCT) ────────────┐
│ • rebuild(document) -> mesh + per-triangle faceIds + edges │
│ • export(document, format, path) -> STEP / STL / 3MF │
│ • selector resolution (topological-naming mitigation) │
└────────────────────────────────────────────────────────────┘
Design decisions worth knowing up front:
- Geometry lives only in Python on the shipping path. Rust never touches it there.
There is an experimental opt-in Rust geometry path on OpenCASCADE, gated behind
VITE_GEOM=rust, but a 2026 feasibility study found a Rust kernel could not beat OCCT on robustness or speed, so the Python build123d sidecar stays the default and the source of truth. - Full rebuild on every change. The frontend sends the whole document, the sidecar rebuilds from scratch and returns a fresh mesh. There is no server-side state.
- The parametric engine is the build123d tree, re-run. Nothing more exotic.
- Selectors, not indices. Geometry is referenced by queryable descriptors so references survive edits that renumber the underlying topology.
See docs/ARCHITECTURE.md for the full invariant list and the rebuild pipeline, and docs/PROTOCOL.md for the sidecar's wire protocol.
sidecar/ build123d geometry service (builder, geom_select, tessellate, exporters, server)
src/ frontend: viewport/, ui/, document/, geometry/, input/, io/, print/
src-tauri/ Rust shell: lib.rs (entry), sidecar.rs (lifecycle), printer.rs (U1 device layer)
SindriCAD is licensed under the GNU Affero General Public License v3.0
(AGPL-3.0-only), see LICENSE. AGPL's network copyleft means any fork, or
any modified version offered over a network, has to publish its source under the same
terms.
Contributions are welcome under CONTRIBUTING.md, which includes a short contributor agreement so the project can stay open under the AGPL while the maintainer can also offer commercial terms to those who need them. Third-party components and their licenses are listed in NOTICE.md.
SindriCAD is free and open source. If it earns a place in your workflow, you can back development on Patreon (MuninWorks). Patronage covers the servers, domains, and tooling behind this and my other projects.






{ "parameters": { "width": 40, "height": 20, "thickness": 5 }, "features": [ { "id": "f1", "type": "sketch", "plane": "XY", "entities": [{ "type": "rectangle", "width": "width", "height": "height" }] }, { "id": "f2", "type": "extrude", "sketch": "f1", "distance": "thickness", "operation": "new" }, { "id": "f3", "type": "fillet", "edges": { "kind": "edge", "by": "axis", "axis": "Z" }, "radius": 2 } ] }