I wanted a real chair-height stool (42 cm seat) that folds small and that I could print on an ordinary 220 x 220 mm bed. It also had to safely hold a 250 lb adult. The whole thing is generated in Python with manifold3d, so every collision and fold check is part of the script.
Legs with knees
A 42 cm stool needs legs about 35 cm long, which no 220 mm bed can print in one piece. So each leg frame has a knee. An upper frame hinges at the seat, and a lower frame hinges on the upper one.
The knee axis sits slightly past the inner face of the frame. When you sit down, the load pushes the knee further toward its stop, not away from it, so the joint locks over-centre. A small gravity hook drops over each knee as extra protection.
Locks that work by gravity
Every lock is a part that falls into place:
- Hip pawls sit in a pocket in each hinge block. When the frame swings open, the pawl drops into a notch in the middle knuckle. The pawl pivots outboard of its tooth, so any load on the notch pushes the pawl deeper.
- Knee hooks hang on the upper frame and drop over a tooth on the lower frame.
Turn the stool upside down and all of them fall open, so folding takes about five seconds. Detent bumps on the hinge lugs click the frames into the open and folded positions.
Checking the fold in code
Manifold booleans are fast enough to sweep every joint through its full range and check the intersection volume at each step:
bad = [d for d in range(-90, int(SPL) + 1, 6)
if (S ^ U(frame_at('A', d, 0))).volume() > 3]
print('hip sweep hits', bad) # [] means nothing collides
The same pattern checks the knee sweep, the open pose and the folded stack. Folded, the stool is 22 x 21.5 x 16 cm.
A seat that doesn't dig into legs
My first seat was flat. My second version had a sculpted bowl with sit-bone pockets, but a fixed bowl only fits one body size, and its raised rim presses into the thighs of anyone larger. The version that shipped changes height only from front to back, plus rolled-down edges where legs rest:
def dish_z(x, y):
d = 6.0 * smooth((x + 105) / 70) # raised rear, flat middle
front = 9.0 * np.clip((x - 20) / 90, 0, 1) ** 2.2 # front waterfall
wf = smooth((x + 30) / 90) # sides drop more toward the front
w = 18 + 17 * wf
side = (3 + 5 * wf) * np.clip((np.abs(y) - (SY / 2 - w)) / w, 0, 1) ** 2
d += smooth_max(front, side)
return TOP - d
The function feeds a heightfield mesh. The seat plate is the space between dish_z and dish_z - 5, so the contour costs no extra filament. The ribs underneath are trimmed to the curved surface and thickened to 10 mm, which brings bending stress under a 2.2 kN point load back to about 17 MPa. That is the same as the flat seat, and well inside PETG's strength.
Optional backrest
The backrest is a bracket that slides into the rear of the seat and is held by one drop-in pin through the seat web. The back leans 12 degrees against a stop block. For carrying, it folds forward onto the seat, or comes off completely.
Files
16 STLs with a parts list, print settings (PETG, 5 walls, 40% gyroid, about 2 kg) and assembly steps:
The license lets you sell the prints; please don't share the files. If you like this kind of build, I post every new design on Patreon.

Top comments (0)