I have been generating 3D-printable desk pieces entirely in Python: no Blender sculpting, no CAD clicks. Each model is a signed distance function (SDF) that gets meshed with manifold3d level_set, then cleaned up with boolean cuts for the functional parts (slots, pin holes, screw keyholes).
Here are four of them and the one trick that made each one work.
The building blocks
Every organic shape below is made from three tiny functions:
import math
def smin(a, b, k):
# smooth union: blends two shapes with a fillet of size k
h = max(k - abs(a - b), 0) / k
return min(a, b) - h * h * k * 0.25
def ell(p, c, r):
# approximate ellipsoid distance
x, y, z = ((p[i] - c[i]) / r[i] for i in range(3))
return (math.sqrt(x*x + y*y + z*z) - 1) * min(r)
def rcap(p, a, b, ra, rb):
# capsule from a to b with radius ra -> rb (tapered limb, branch, bone)
pa = [p[i] - a[i] for i in range(3)]; ba = [b[i] - a[i] for i in range(3)]
h = max(0, min(1, sum(pa[i]*ba[i] for i in range(3)) / sum(v*v for v in ba)))
return math.sqrt(sum((pa[i] - ba[i]*h)**2 for i in range(3))) - (ra + (rb - ra)*h)
Manifold.level_set(f, bounds, step) wants positive values inside, so each model function returns -d.
1. Bat roost headphone stand: bark that follows the wood
The tree is a chain of tapered capsules (trunk, branch, twigs, roots) joined with smin. The first try used one bark pattern for the whole tree and it looked like a striped pole. The fix: two groove fields, one wrapping the trunk axis and one wrapping the branch axis, cross-faded by position.
def bark(x, y, z):
w = min(1, max(0, (z - 196) / 12)) * min(1, max(0, (x - 8) / 10)) # 0 on trunk, 1 on branch
gt = abs(math.sin(math.atan2(y, x) * 9 + 0.025*z + 0.6*math.sin(z*0.05)))
gb = abs(math.sin(math.atan2(y, z - 221) * 8 + 0.03*x + 0.5*math.sin(x*0.07)))
return 1.1 * ((1 - w)*gt + w*gb)
The sleeping bat hanging from the branch tip is its own SDF: a body ellipsoid wrapped in a "cloak" shell (abs(rr - 1) gives a thin shell), with a scalloped hem cut by sin(angle * 3.5) and finger bones added back as ridges.
Bat Roost Headphone Stand on Cults3D · also on Payhip
2. Cable turtle: shell plates from a Voronoi edge distance
Sea-turtle scutes are just Voronoi cells. The distance to the nearest seam is half the gap between the two nearest seed points:
SCUTES = [(0,0),(0,-20),(0,20),(-19,-10),(19,-10),(-19,10),(19,10)]
def seam(x, y):
d = sorted(math.hypot(x - a, y - b) for a, b in SCUTES)
return (d[1] - d[0]) / 2
Push the surface in near seam == 0 and you get grooves; add a small sin(seam * 2.4) and each plate gets growth rings. The useful part is boolean: four keyhole channels (5.2 mm bore, 3.4 mm mouth) cut across the shell so charging cables snap in and stop sliding off the desk.
Cable Turtle on Cults3D · also on Payhip
3. Octopus key holder: tentacles by "heading walk"
Instead of splines, each arm is a turtle-graphics walk in the wall plane: start point, heading, and a bend function bend(u) that changes the heading each step. The last few steps of the lower arms lift off the wall at about 50 degrees, so they become key hooks that still print flat-backed with no supports.
for i in range(n):
pts.append(p.copy())
h += bend(i / n)
d = np.array([math.cos(h), 0, math.sin(h)])
if i >= n - hook: # curl out of the wall
d = np.array([math.cos(h)*0.45, -0.75, 0.55 + math.sin(h)*0.2]); d /= np.linalg.norm(d)
p = p + d * step
The tube is a hull of consecutive spheres, and suckers are small spheres subtracted along each arm's underside.
Octopus Key Holder on Cults3D · also on Payhip
4. Skeleton hand phone stand: bones as hulls
Each phalanx is the convex hull of two small spheres (the shaft) plus two slightly squashed spheres at the ends (the knuckles). Fingers are chains of three bones that go up behind the phone, forward over its top edge and down in front, so the phone is held, not just leaned. The grave mound is a noisy ellipsoid SDF, and the phone slot is one tilted box intersected with a slab, so the slot depth is exact.
Grave Grab Skeleton Hand on Cults3D · also on Payhip
Lessons
- A pure-Python SDF in
level_setis slow. For large models, write the SDF in numpy over a whole grid and mesh that instead. - Always check
decompose(): a sealed cavity turns into a negative-volume component, and a groove that cuts all the way through splits the part. - Render from two or more angles before calling a model done. Most of my rejects looked fine from the front.
All four files are licensed so you can print them and sell the prints; only the files themselves stay mine. More designs: Cults3D · patreon.com/123black




Top comments (0)