Gallery Gyroscope Fidget
Gyroscope Fidget
Three nested rings and a Seljuk-star centre on cone pins, printed in place as one piece; the stand prints separately. Set print_layout = True for the plate layout.
Needs a project open in ArgilCAD. The CAD engine starts when a project is open, and it builds the model from the code. It runs on a computer; phones and tablets open ArgilCAD as a viewer.
- Open ArgilCAD and create or open a project.
- Paste the code into the prompt box or the Code Editor.
- Run it — then change any dimension, or ask the AI for a change. Running code is free.
- Size
- 86 × 45.4 × 68.6 mm
- Parts
- 6
- Category
- Toys
- Printed
- Yes · 22.96 g · print-in-place
- How it was made
- Claude Code, over MCP
- Licence
- CC BY-NC-SA 4.0
Code model.py · 161 lines
# Gyroscope rings — desk fidget (ready to print)
# Frame + 3 rings + a centre with a Seljuk-star window: all in one plane,
# joined by cone pins, printed in place as ONE PIECE. The stand prints
# separately; the frame's bottom bar slides into the slot in the stand.
# print_layout = True → plate layout (the state to export)
# print_layout = False → standing on the desk, rings turned
#
# English translation of gyroscope_fidget.py for the gallery: same geometry.
from build123d import *
import math
print_layout = False
# --- Rings (mm) ---
t = 6.0 # ring and centre thickness (height when printing)
w = 4.0 # ring wall width
gap = 0.6 # radial gap between rings
R1 = 30.0 # outer radius of the outer ring
edge_round = 0.8 # edge rounding (also keeps the first layer from sticking)
# --- Cone pins ---
pin_r0, pin_r1 = 2.2, 0.7 # base / tip radius
pin_depth = 1.6 # how far a pin enters the outer part
clearance = 0.35 # print-in-place clearance
# --- Frame (printed flat together with the rings) ---
arm_w = 6.0
frame_t = 8.0 # height when printing, depth on the desk
arm_gap = 0.8 # between the outer ring and an arm
bar_h = 6.0 # bottom bar (the part that goes into the stand)
bar_gap = 1.5 # between the turning outer ring and the bottom bar
# --- Stand (printed separately) ---
stand_t = 3.0
block_h = 5.0
slot_gap = 0.2 # bar to slot (if loose, a drop of glue)
slot_wall = 3.0
# Display angles on the desk
angle1, angle2, angle3, angle4 = 20, 55, 65, 55
K_SELJUK = math.cos(math.pi / 4) / math.cos(math.pi / 8) # {8/2} star ratio
C, MIN = Align.CENTER, Align.MIN
# Radii: each ring sits inside the previous one, one gap apart
R1i = R1 - w
R2 = R1i - gap
R2i = R2 - w
R3 = R2i - gap
R3i = R3 - w
Rc = R3i - gap
def star(R, h, turn=0.0):
pts = []
for i in range(16):
a = math.radians(turn + i * 22.5)
r = R if i % 2 == 0 else R * K_SELJUK
pts.append((r * math.cos(a), r * math.sin(a)))
return extrude(Polygon(*pts, align=None), amount=h)
def ring(Ro, Ri):
r = Cylinder(Ro, t) - Cylinder(Ri, t + 2)
return fillet(r.edges().filter_by(GeomType.CIRCLE), radius=edge_round)
def cone(axis, start, direction, r0, r1, length):
"""axis 'x' or 'y'; start: position along the axis; direction: +1 / -1."""
if axis == "x":
place, rot = Pos(start, 0, 0), Rot(0, 90 * direction, 0)
else:
place, rot = Pos(0, start, 0), Rot(-90 * direction, 0, 0)
return place * rot * Cone(r0, r1, length, align=(C, C, MIN))
def joint(inner, outer, axis, r_inner, r_outer):
"""Two pins on the inner part, two cone sockets in the outer — one turning axis."""
L = (r_outer - r_inner) + 0.6 + pin_depth
for s in (1, -1):
inner += cone(axis, s * (r_inner - 0.6), s, pin_r0, pin_r1, L)
outer -= cone(axis, s * (r_inner - 0.6), s, pin_r0 + clearance, pin_r1 + clearance, L + clearance)
return inner, outer
# --- The gyroscope in the print plane (centre at the origin, bottom faces at z = -t/2) ---
ring1 = ring(R1, R1i)
ring2 = ring(R2, R2i)
ring3 = ring(R3, R3i)
centre = Cylinder(Rc, t)
centre = fillet(centre.edges().filter_by(GeomType.CIRCLE), radius=edge_round)
centre -= Pos(0, 0, -t) * star(Rc * 0.72, 2 * t, 22.5)
# Frame: two arms and a bottom bar, U-shaped
z0 = -t / 2
xa = R1 + arm_gap
xb = xa + arm_w
y_top = 4.0 # centre of an arm's end
y_bar = -(math.hypot(R1, t / 2) + bar_gap) # top edge of the bar
y_bottom = y_bar - bar_h
frame = Pos(0, (y_bar + y_bottom) / 2, z0) * Box(2 * xb, bar_h, frame_t, align=(C, C, MIN))
for s in (1, -1):
xc = s * (xa + arm_w / 2)
frame += Pos(xc, (y_bottom + y_top) / 2, z0) * Box(arm_w, y_top - y_bottom, frame_t, align=(C, C, MIN))
frame += Pos(xc, y_top, z0) * Cylinder(arm_w / 2, frame_t, align=(C, C, MIN))
inner_corners = [
e for e in frame.edges().filter_by(Axis.Z)
if abs(abs(e.center().X) - xa) < 0.01 and abs(e.center().Y - y_bar) < 0.01
]
frame = fillet(inner_corners, radius=3.0)
# Gimbal: frame–ring1 X, ring1–ring2 Y, ring2–ring3 X, ring3–centre Y
ring1, frame = joint(ring1, frame, "x", R1, xa)
ring2, ring1 = joint(ring2, ring1, "y", R2, R1i)
ring3, ring2 = joint(ring3, ring2, "x", R3, R2i)
centre, ring3 = joint(centre, ring3, "y", Rc, R3i)
# --- Stand (desk coordinates; z = 0 is the desk) ---
y_mid = -(z0 + frame_t / 2) # middle of the depth once the frame stands up
block_L = 2 * xb + 2 * slot_gap + 2 * slot_wall
block_D = frame_t + 2 * slot_gap + 2 * slot_wall
stand_L, stand_D = block_L + 6, 30.0
stand = Pos(0, y_mid, 0) * extrude(SlotCenterToCenter(stand_L - stand_D, stand_D), amount=stand_t)
stand = fillet(stand.edges().group_by(Axis.Z)[-1], radius=1.2)
block = Pos(0, y_mid, stand_t) * Box(block_L, block_D, block_h, align=(C, C, MIN))
block = fillet(block.edges().filter_by(Axis.Z), radius=2.0)
stand += block
z_slot = stand_t + block_h - bar_h
slot = Box(2 * xb + 2 * slot_gap, frame_t + 2 * slot_gap, bar_h + 1, align=(C, C, MIN))
stand -= Pos(0, y_mid, z_slot) * slot
if print_layout:
# Plate: the gyroscope lies flat (every bottom face at z = 0), the stand beside it
up = Pos(0, 0, t / 2)
frame, ring1, ring2, ring3, centre = (up * p for p in (frame, ring1, ring2, ring3, centre))
stand = Pos(0, y_bottom - 6 - stand_D / 2 - y_mid, 0) * stand
else:
# Desk: frame upright, bottom bar at the bottom of the slot; rings turned
H = z_slot - y_bottom
D = Pos(0, 0, H) * Rot(90, 0, 0)
T1 = D * Rot(angle1, 0, 0)
T2 = T1 * Rot(0, angle2, 0)
T3 = T2 * Rot(angle3, 0, 0)
T4 = T3 * Rot(0, angle4, 0)
frame = D * frame
ring1 = T1 * ring1
ring2 = T2 * ring2
ring3 = T3 * ring3
centre = T4 * centre
frame.label = "frame"
ring1.label = "ring_1"
ring2.label = "ring_2"
ring3.label = "ring_3"
centre.label = "centre"
stand.label = "stand"
result = Compound(label="gyroscope", children=[stand, frame, ring1, ring2, ring3, centre])
Dimensions you can change
| Variable | Default | Range | What it is |
|---|---|---|---|
print_layout | False | True / False | plate layout |
R1 | 30.0 | 20 – 60 mm | outer ring radius |
clearance | 0.35 | 0.2 – 0.6 mm | print-in-place clearance |
© 2026 Argil Design · CC BY-NC-SA 4.0 — share and remix with credit; no commercial use or sale; remixes under the same licence.