A spider web is mostly a 2D drawing. A 3D-printable bowl is a 3D shape. The trick in this post is to draw the web as a flat CrossSection, extrude it straight down, and subtract it from a bowl shell. The same trick works for any pattern: leaves, stars, bats. Everything uses manifold3d and plain Python.
1. A bowl from two spheres
A shallow bowl is a spherical cap. For a cap of rim radius Rb and height H, the sphere radius is Rs = (Rb^2 + H^2) / (2 H).
from manifold3d import Manifold, CrossSection as CS
Rb, H = 52.0, 30.0
Rs = (Rb * Rb + H * H) / (2 * H)
def sph(r, zc):
return Manifold.sphere(r, 96).translate([0, 0, zc])
shell = (sph(Rs, Rs) - sph(Rs - 2.8, Rs)).trim_by_plane([0, 0, -1], -H)
shell = shell + Manifold.cylinder(2.8, 16, 16, 64) # flat foot to print on
trim_by_plane keeps the side the normal points to. The normal [0, 0, -1] with offset -H keeps everything below z = H.
2. Draw the web in 2D
A thread is a thin rectangle rotated to an angle. Spokes run from the centre. Each ring is a polygon with a small inward kink on every segment, so the threads sag like a real web.
import math
def thread(a, b, w=2.6):
dx, dy = b[0] - a[0], b[1] - a[1]
ang = math.degrees(math.atan2(dy, dx))
return CS.square([math.hypot(dx, dy), w]).translate([0, -w / 2]).rotate(ang).translate(list(a))
web = CS()
n = 12
for i in range(n):
a = math.radians(i * 360 / n)
web = web + thread((0, 0), (70 * math.cos(a), 70 * math.sin(a)))
for rr in (11, 20, 29, 38):
for i in range(n):
a0, a1 = math.radians(i * 360 / n), math.radians((i + 1) * 360 / n)
am = (a0 + a1) / 2
p = (rr * math.cos(a0), rr * math.sin(a0))
q = (rr * math.cos(a1), rr * math.sin(a1))
m = (rr * 0.93 * math.cos(am), rr * 0.93 * math.sin(am)) # sag
web = web + thread(p, m) + thread(m, q)
web = web + CS.circle(7, 48) + (CS.circle(60, 128) - CS.circle(45, 128)) # hub and rim
3. Cut it through the bowl
The holes are everything inside the rim that is not thread.
holes = CS.circle(46, 128) - web
bowl = shell - holes.extrude(H + 10).translate([0, 0, -3])
bowl = max(bowl.decompose(), key=lambda m: m.volume()) # drop loose bits
Because the cut goes straight down, the threads on the steep sides look slightly flattened and thicker. That is fine for a bowl and gives the web depth.
Gotchas
- Every thread must touch the hub or the rim. A thread that touches nothing becomes a loose piece, and
decomposethrows it away. -
Manifoldhas no.intersectmethod. Use^for intersection,+for union and-for difference. - Keep thread width at 2.4 mm or more for a 0.4 mm nozzle (six perimeters is overkill, but anything under 1.6 mm snaps).
- Swap the web for 26 leaf shapes placed at the golden angle (
137.508degrees, radius6 + 40 * sqrt((i + 0.5) / n)) and you get a fall leaf bowl from the same code.
I have not test printed these yet. The renders look right and the meshes are one manifold solid. The finished spider web bowl and the leaf bowl are on Cults3D.













