Turns AI-generated mesh output (Meshy, Luma, photogrammetry scans, and similar) into a watertight, manifold, correctly-scaled, multicolour-print-ready model — without losing the colour — and then into an articulated, poseable, printable figure.
Runs entirely on your PC. Nothing is uploaded anywhere, and nothing is fetched
from the internet: three.js is vendored in static/vendor/, so the 3D view
works with the network cable out.
Grab the build for your machine, unzip it anywhere, and run it. Nothing to install; everything still stays on your machine.
| Platform | You get | Notes |
|---|---|---|
| Windows 10/11 x64 | MeshDeX.exe |
one file, ~57 MB |
| macOS 11+ Apple Silicon | MeshDeX.app |
arm64 |
| macOS 11+ Intel | MeshDeX.app |
x86_64 |
It opens http://127.0.0.1:8730 in your browser. input/ and output/ are
created next to the app, so an unzipped-to-a-USB-stick copy keeps its own
work with it. If the app lives somewhere read-only — /Applications, or
Program Files — those folders go to ~/Documents/MeshDeX on macOS and
%LOCALAPPDATA%\MeshDeX on Windows instead. The path is printed in the
console window at startup.
Two first-launch warnings to expect, both because the builds are unsigned:
- Windows SmartScreen — "Windows protected your PC" → More info → Run anyway.
- macOS Gatekeeper — right-click the app → Open the first time. If it was
downloaded rather than built locally, macOS quarantines it and a plain
double-click reports it as damaged;
xattr -dr com.apple.quarantine /Applications/MeshDeX.appclears that.
Being a onefile build, Windows unpacks ~200 MB to temp on each launch, so the
first window takes a few seconds to appear. Set ONEFILE = False in
MeshDeX.spec for a folder build that starts instantly.
- Install Python 3.10+ from python.org. Tick "Add python.exe to PATH" in the installer.
- Double-click
Launch_Windows.bat(or./Launch_Mac_Linux.sh). First run builds a private environment (a few minutes, once). After that it starts in seconds and openshttp://127.0.0.1:8730in your browser.
Windows powershell -ExecutionPolicy Bypass -File build_windows.ps1
macOS ./build_macos.sh
PyInstaller cannot cross-compile. The Mac app has to be built on a Mac and
the Windows exe on Windows — there is no way around this, because manifold3d
and fast_simplification are native extensions that must be linked for the
target platform. If you do not own a Mac, push a v* tag and
.github/workflows/release.yml builds all three on GitHub's runners and
attaches them to a draft release.
Meshmixer's "Make Solid", Netfabb, Microsoft's 3D repair service and most other auto-repair tools work by rebuilding the mesh from a voxel or signed-distance field. That representation stores occupancy only — it has no colour channel. What comes out is a brand-new mesh whose vertices never existed in your original, so there is nothing left to attach colour to. Default grey, every time.
This tool never does that. It:
- repairs topology in place, so original vertices survive carrying their colour;
- when new geometry is genuinely unavoidable (decimation, hole filling), it re-samples colour from the original surface by nearest-point lookup.
That's the whole trick.
Diagnose — non-manifold edges, holes, winding defects, bowtie vertices, coincident vertices, separate shells, Euler characteristic. Before and after, side by side.
Repair, colour-safe — separates coincident vertices, deletes faces on non-manifold edges, fills the resulting holes, splits bowtie vertices into per-fan copies (each copy inherits the colour), makes winding globally consistent, forces normals outward. Verified by re-checking the result.
Decimate — quadric edge collapse to a triangle budget your slicer can actually handle, then colour transfer, then a second repair pass (decimation routinely creates non-manifold edges — this catches them).
Scale & orient — Y-up to Z-up, scale to a target height in mm, centre in X/Y, drop onto the bed. Loads into the slicer ready to go.
Print report — size, volume, filament weight, bed-contact area, overhang percentage, with warnings when adhesion or supports will be a problem.
Full Spectrum palette — enter your whole filament shelf; it searches every 4-filament combination, picks the best set for that specific model, and gives you layer-Pattern strings to paste into Snapmaker Orca's Color Mixing panel. Every recipe reports its resulting hex, so you can set the mixes up before the model is anywhere near the plate.
Pre-order recommendations — tick "also consider filaments I could order" and it searches the Panchroma catalogue too, then tells you which spools to buy. Because the shelf can now run to dozens of spools, an exhaustive search is hopeless past ~400 combinations, so it switches to greedy selection with swap refinement.
Calibration swatch — a plate of tiles, one per recipe, plus a reference sheet mapping tile position to pattern and predicted hex. Tile 1 has a corner tab so you know which way round it is. Print it, compare, then correct the shelf hex values so future predictions match your actual spools.
Articulate (print in place) — the articulated-dragon workflow: slices the model into a chain along its centreline and links each pair with a captured ball joint. One file, one print job, comes off the bed already flexible with nothing to assemble. Full description below.
Auto-rig — for the other kind of articulation, where parts print separately and you assemble them. Press Find limbs & joints and it works out where the joints go by itself. Full description below; the short version is that it finds each limb, measures where it narrows, and proposes a cut plane already perpendicular to the bone.
Paint & recolour — click any region of the model and repaint it, or shift its hue, saturation and lightness. Selection floods across the surface, not through space, so it cannot leak from an arm onto the torso it is resting against.
Plate & export — every part is turned onto its best printing face, packed onto your bed, and written as one 3MF with per-triangle colour and named objects.
Cut into parts, with joints — open the 3D preview and place a plane:
- click the model to anchor the plane at that point
- Tilt / Spin sliders orient it freely — cuts are not restricted to X/Y/Z
- Slide along normal moves it perpendicular to itself
- flat Z / X / Y buttons snap back to an axis
- Guide size scales the translucent square in the viewport. It is a visual aid only — the plane itself is mathematically infinite, and what limits the cut is confinement (below), not the size of the guide.
- Drag the coloured arrows (or the ±X/Y/Z buttons) to slide the plane in world axes.
This is the articulated-dragon workflow, and it is not the same thing as cutting a model into parts:
| Cut into parts | Print in place | |
|---|---|---|
| files | one per part | one |
| print jobs | one per part, or a plate | one |
| assembly | caps, pins, your hands | none |
| result | a kit | comes off the bed already flexible |
Open a model, go to Articulate, press Find it automatically, choose how many segments, and build. The output is a single OBJ containing many disjoint shells that interlock without touching.
Every instinct at the end of this process says to union the segments together. Unioning is exactly what turns a dragon into a paperweight. A print-in-place model is one file containing many separate solids, and the air between them is the joint. So the segments are concatenated, never unioned.
-
Find the spine. The longest medial path through the model - nose to tail-tip on a dragon. It reuses the voxel skeleton: thin voxels are expensive to travel through, so the path rides the core of the body instead of skimming the skin. You can also click the two ends yourself and get the medial path between them, which is how you articulate only a tail or only a neck.
The cost the path finder minimises is a reluctance, not a distance, so asking it for the "farthest" point returns the hardest place to reach - an ear tip. Taking two of those gave a spine running ear to ear across the top of a dog's skull. The route still comes from the weighted graph, but its length is measured afterwards in real millimetres, so farthest now means longest.
-
Station the spine at equal arc length, each with a plane perpendicular to the local tangent. Because the tangent follows the curve, slices stay square to the body even where it bends.
-
Slice, confined by connectivity. A spine curves - a dragon's tail curls, a dog's tail arches back over its own body - and a plane perpendicular to the tail, extended forever, also cuts straight through the torso. So only the piece connected to that stretch of spine is taken, and everything else the plane grazed is handed straight back.
-
Link each pair with a captured ball. The socket's mouth is deliberately narrower than the ball. On a joint you assemble that is a defect - you could never push it together - but printed in place it never has to be assembled, and the narrow mouth is the only thing stopping the chain pulling apart. Default grip is 80%: the mouth is four fifths of the ball diameter.
Clearance. Too small and the two shells fuse into one solid in the print; too large and the chain is sloppy and droops. It cannot go below roughly one extrusion width, because a slicer cannot resolve a thinner gap - a 0.1 mm gap through a 0.4 mm nozzle is not a gap, it is a solid. The panel refuses to let that pass quietly: set it under half your nozzle and it says so.
Default is 0.55 x nozzle, which is 0.22 mm on a 0.4 mm machine.
Every build is checked before you download it:
- every shell watertight;
- no two adjacent shells share any volume - a boolean intersection answers this exactly, no sampling and no tolerance, and any overlap at all means those two segments print as one lump;
- an interface too thin for a ball is merged into its neighbour rather than left as two coincident shells. Touching shells fuse on the bed, so reporting a joint there would be a lie.
Measured on a 150 mm figure, 600k triangles:
| segments asked | segments built | joints | watertight | fused | total bend | time |
|---|---|---|---|---|---|---|
| 16 | 14 | 13 | 14/14 | 0 | 111° | 15 s |
| 24 | 21 | 20 | 21/21 | 0 | 137° | 19 s |
- Bend costs material. Each joint needs its faces relieved or they grind and the chain comes off the bed rigid. The relief has to be capped against the segment length, not just the cross-section: an angle times a 25 mm torso radius asked for 8 mm per joint, and across twelve joints that quietly removed nearly 40% of the animal. It is capped now, and the panel shows the total bend you are buying.
- A chain through a quadruped still crosses its legs. Those pieces are handed back rather than cut, so the chain is intact - but a limb the chain passes through does not itself articulate. Articulate a tail or a neck on its own if that matters.
- Very thin extremities cannot hold a ball and are merged into the segment before them. A tail that tapers to a point loses its last joint or two.
- Print it as one object, and do not put supports through the joints - the gaps are meant to be bridged.
Placing every joint by hand is fine for one cut and miserable for a twelve-part figure. This finds them.
A joint belongs where a limb is narrow — wrists, ankles, necks, the base of a tail. That is a statement about the model's interior, so none of it happens on the triangles:
- Rasterise the mesh into a voxel shell, seal it, flood the outside, keep what is left. That gives a solid. (The fill is checked against the mesh's own volume; a leaking surface says so instead of quietly rigging a brick.)
- Distance transform the solid. Every interior voxel now knows how far it is from the surface — which is exactly the local limb radius.
- Root the skeleton at the thickest voxel. On any figure that lands in the torso, which is the correct trunk.
- Grow a shortest-path tree outward, weighting each step by how thin the voxel is. Paths then refuse to cut corners through skin and hug the medial axis instead — a curve skeleton, with no thinning algorithm and no mesh contraction solve.
- Repeatedly take the farthest unclaimed voxel and trace back until the path meets skeleton already found. Each trace is one limb.
- Read the radius along each branch. Local minima are constrictions, and a constriction is where a joint wants to be. The branch tangent gives the cut normal for free, so the plane arrives already perpendicular to the limb.
Why the thickness weighting matters. With uniform step cost the shortest path from a hand to the torso cuts straight through the armpit and hugs the inside of the elbow, because that genuinely is the shortest route through the solid. The skeleton then runs along the skin and every joint it proposes is off-centre. Making thin voxels expensive makes the detour through the middle of the limb cheaper than the shortcut.
Where the joint goes along the branch. Not at a fixed fraction. Every branch starts at a junction inside the body, so its first samples carry the trunk's radius rather than the limb's — put the hip at "16% along" and on a tall figure it lands in the pelvis. Instead it finds where the radius has actually fallen away from the junction value. That point is the shoulder or the hip whatever the branch's length. If the radius never falls, the branch never separated from the body and is not a limb at all.
A limb, or a detail on a limb? A branch joining the tree near the trunk is a new limb. One joining near the far END of an existing branch is a finger, a toe or an ear - a feature of that limb rather than a peer of it. Details are indented under their parent, drawn faintly in the viewport, and left unticked.
Mirror twins are levelled up. A bilaterally symmetric model that comes back with the left arm ticked and the right arm not has failed, whatever the individual scores say - the two sides are the same shape and the difference is measurement noise. So each limb is matched to its mirror partner and the weaker one is promoted to match.
It proposes, it does not cut. Every suggestion arrives with the radius it measured and a confidence, and lands in the same editable cut list you would have built by hand.
This is the one failure mode worth recognising on sight. If a model's hands rest against its hips, then at a coarse voxel size the hand and the hip merge, and that arm's skeleton takes a short cut through the contact instead of going round through the shoulder. The symptom is one full-length arm and one stunted one.
The tool detects this rather than shipping it quietly. Two limbs that end in mirrored places but start in different ones is the signature, and it produces a named warning telling you to raise Detail. The default is 150.
Mirror across symmetry reflects every cut through the detected plane and
flips _L/_R in the name. Cuts sitting on the plane — a head, a tail — are
skipped, as are ones whose twin already exists, so pressing it twice does not
double the rig. The roll angle flips with the plane; without that a mirrored
hinge bends the opposite way to its twin.
Cut distal joints first. This one is not cosmetic. Each cut takes its part off the body and carries on with what is left, so cutting a leg off at the hip and then asking for a knee finds nothing — the leg left with the hip. The symptom is a silent wrong cut somewhere else on the model. It is on by default; the checkbox exists so you can see what it is doing.
Saved rigs. A joint layout that suits one biped suits the next one, so rigs
save to output/rigs/ as JSON and reload onto a different model — positions are
remapped through the bounding boxes, while clearances are left alone, because
those are printer numbers and shrinking them with the model is how you get a
joint that fuses.
Group & assign already answers "print this cluster in that filament". It does not answer "the model gave the character a brown collar and I want a red one".
Four selection modes, because the right one depends on why the region is a region:
| mode | use it when |
|---|---|
| similar, connected | most of the time — follows the collar round the neck and stops at the fur |
| same colour anywhere | all four paws in one go |
| brush | the colour has no boundary at all — a shading ramp |
| whole connected piece | models that arrived in parts |
Flood fill runs over mesh edges, never over a distance threshold in space. Two surfaces that pass close to each other — an arm against a torso, a tail lying on a back — are near in space and far apart across the surface, and a radius-based fill leaks straight from one to the other. Tolerance is in ΔE, not RGB: 10/255 is a visible step between two near-blacks and invisible between two near-whites, so an RGB threshold grabs half the model in shadow and nothing in highlight.
Softening the edge blends across the boundary, and every blended vertex is a new colour — lovely on screen, expensive on a printer. The panel says how many colours you ended up with.
Orientation is not tidiness. FDM parts split along layer lines, so a peg printed standing up is a stack of discs glued end to end and snaps at the first layer boundary; the same peg lying down has its layers running along it.
Candidate orientations come from the convex hull's faces — a part will only sit stably on a face that is already on its hull, so that is the complete set of stable positions rather than an approximation. Each is scored on support needed (dominant), height, and bed contact. Parts are then shelf-packed onto your bed, each laid down in whichever of its two axis-aligned rotations is shorter.
3MF rather than OBJ for the final file. v x y z r g b is not in any OBJ
spec — it is a de-facto extension, and whether a slicer reads it is luck. 3MF
has an actual materials extension, so colour travels as data, and each part
arrives as a separate named object instead of one merged blob. STL is also
offered, and carries no colour at all.
A click lands on the outside of a limb, so using it directly as the anchor puts the joint against the surface instead of down the middle. With snap to the centre of the limb on, the click is sent to the server, which takes the mesh vertices in a band around the plane, keeps only the connected patch nearest your click, and returns that patch's centroid.
A plane has only two rotational degrees of freedom — its normal — and tilt and spin already cover those. The third rotation you need is the axle's angle within the plane, which is what the roll slider sets. The axle is drawn in the viewport as a yellow line so you can see the bend axis before committing.
| roll | axle runs along | the part swings |
|---|---|---|
| 0° | Y (left–right) | forward / back — a knee or a walking leg |
| 90° | X (front–back) | left / right — a hip doing the splits |
| yellow line | the axle — what the part pivots about |
| orange double arrow | the direction the part actually swings |
| cyan arrow | the side that DETACHES, and gets the ball / axle / pins / tongue |
| magenta arrow | the side that STAYS, and gets the socket cavity or fork slot |
| red / green / blue arrows | drag to slide the plane in X / Y / Z |
Confinement. A plane is infinite, so it slices everything it passes through. With "confine to the clicked piece" on (the default) only the connected component nearest your click is detached, and the rest of the model is left completely untouched. This is exact, not a bounding-box approximation.
So joints split into two families:
Assemble after printing — parts print separately, in any orientation:
| Joint | Motion | How it goes together |
|---|---|---|
| Ball + cap | 3-axis | Ball drops into an open cup, then a separate printed cap presses in and traps it. Nothing flexes. |
| Friction ball ★ | 3-axis | Identical geometry, deliberately undersized clearance. The cap squeezes the ball, and the pose stays where you put it. Grip is adjustable per cut. |
| Clevis + pin | 1 axis | Tongue slides into a two-eared fork, a separate printed pin pushes through. Strongest and most forgiving. |
| Ratchet clevis ★ | 1 axis | A clevis with radial face teeth. Clicks into 6–24 fixed angles and holds. Tooth count comes from what the nozzle can resolve at that radius. |
| Peg + bore | 1 axis | Push-fit peg, free spin. Fits where a clevis is too wide for the limb. |
| Dowel pins ★ | static | Faces mate flush on 2+ alignment pins, then glue. |
★ = holds a pose rather than swinging free.
Print already assembled — the joint is printed captured, with a clearance gap, and cannot be taken apart or put together later:
| Joint | Motion | Constraint |
|---|---|---|
| Ball & socket (PIP) | ~16° swing | Both parts must print in the same job and orientation |
| Hinge (PIP) | 1 axis | Same |
The extra parts appear in the results list as <part>_pin and <part>_cap.
Joint dimensions come from the cross-section at the cut, so the same cut works on a 40 mm figurine and a 250 mm one. The only absolute number in the joint maths is the smallest feature your nozzle prints reliably — and that is a property of your printer, not of the model.
It comes from the Printer tab: pick your machine, and minimum feature (3x nozzle), joint clearance (0.5x nozzle) and dowel clearance (0.375x nozzle) all follow. Change to a 0.6 mm nozzle and every joint on every model resizes with it.
| mechanism | example | colour regions are… |
|---|---|---|
toolchange |
Snapmaker U1 (4 heads), Prusa XL | nearly free — push the count up |
idex |
Snapmaker J1, Artisan | cheap for two colours, a swap beyond that |
purge |
Bambu + AMS, Prusa + MMU, Creality CFS | genuinely expensive — fewer, larger areas |
manual |
Ender 3, CORE One, K1 | you are standing at the machine |
Measured across scales, same cut, same leg:
| Model height | ball radius | neck | relief | retention | swing |
|---|---|---|---|---|---|
| 80 mm | 1.26 mm | 0.63 mm | 1.39 mm | 88% | 16° |
| 150 mm | 2.45 mm | 1.22 mm | 2.34 mm | 88% | 16° |
| 250 mm | 4.06 mm | 2.03 mm | 3.74 mm | 88% | 16° |
Clearance defaults to 0.20 mm (0.15 mm for dowels) and is adjustable per cut.