- TypeScript 53.1%
- Python 33.5%
- CSS 7.5%
- Shell 3.6%
- Nix 1.5%
- Other 0.8%
| .agents/skills | ||
| .claude/skills/box-studio | ||
| data | ||
| docs | ||
| frontend/studio | ||
| python/fancyboxes | ||
| scripts | ||
| .envrc | ||
| .gitignore | ||
| .python-version | ||
| biome.json | ||
| flake.lock | ||
| flake.nix | ||
| justfile | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
FancyBoxes
Why you might care about this repo
- Box Studio: design round boxes with patterned walls in the browser, with a live 3D preview
- Fluted, ribbed, faceted and wavy walls, turned as spirals, double spirals, woven lattices, zigzags, arches and more
- Every pattern prints upright without supports, and the Studio shows how far each box leans
- One click exports every saved box as an STL for any slicer, all on one plate
Issues this solves
- Crossed and interleaved flutes are hard to build in CAD
- The OCCT booleans behind build123d and CadQuery return wrong solids for them
- Here the wall is a radius per angle and height, meshed directly, with no booleans
- Choosing a twist that still prints
- The twist slider is green up to the 45° limit and red beyond it
- Printing many boxes overnight
- Print: 2 lines makes the wall a 0.8 mm shell that follows the pattern: about 3 times faster than a solid wall, and not limited to one box like vase mode
- Print: 1 line makes it 0.5 mm, about a third faster again
How you want to use this repo
-
Start the Studio, and open the address it prints (http://localhost:5173):
nix run .#studio -
Press + to start a box. Set the size and the floor height first: a side view shows how deep the inside is. A 100 mm box with its floor at 70 mm holds 30 mm, on a base the slicer fills with light infill. Then pick a shape and a twist by their pictures. Patterns has 30 ready-made ones, and Surprise me picks a random pattern that prints.
-
The box names itself after its settings, such as "Toothpicks · spiral flutes", and the name changes with them. A name you type stays. ↺ beside it gives the box its settings' name again.
-
Save puts the box on the shelf,
data/config/boxes.json. -
Export to STL writes every saved box to
data/stl/, one file per box, named after it. The dialog after it downloads each file, or all of them as one zip. Use this when the browser is on a different computer than the Studio.
nix run alone exports the saved boxes without the Studio, the same as the button. Both work on any machine with Nix. A git flake reads only tracked files, so git add the repo before the first run.
To use the Studio from a phone or another laptop, start it with nix run .#studio-lan. It prints the addresses to open on the other device.
Commands
just … |
Does |
|---|---|
studio |
start Box Studio |
studio-lan |
start Box Studio for other devices on the local network |
generate |
export every saved box to data/stl/ |
check |
the gate before a commit: Biome, ruff, tsc, pyright, Vitest, pytest |
e2e |
the end-to-end tests in a headless browser |
slice-check |
slice shell walls with PrusaSlicer headless, and check they print as whole lines |
Run them inside the flake: nix develop -c just check, or nix develop once. nix run and nix run .#studio need no shell.
Patterns
Shape (wallType) |
Cross-section |
|---|---|
| Smooth | a plain circle |
| Flutes | round grooves cut into the wall |
| Alternating | deep and half-deep grooves in turn |
| Ribs | round ridges on the wall |
| Facets | a regular polygon |
| Waves | a sine curve around the box |
Twist (twistStyle) |
What the turn does |
|---|---|
| Spiral | turns one way from floor to rim |
Double spiral (knurled) |
cuts the pattern twisted both ways, for a diamond grid |
| Woven | raises the pattern twisted both ways, like wickerwork |
Arches (arched) |
crosses both ways at the floor and straightens out at the rim |
| Zigzag | turns and turns back, bands times up the wall |
| Sway | leans one way at the floor and the other way at the rim |
| Herringbone | bands planks around the box that twist one way and the other in turn |
Sizes that print well
- Diameter 40–80 mm, height 20–100 mm
- Wall: Solid (2 mm), 2 lines (0.8 mm) or 1 line (0.5 mm), for a 0.4 mm nozzle. Set
NOZZLE_DIAMETERindata/config/defaults.envfor another one. - Floor 1.5–3 mm for a deep box, 3 mm or more on tall, narrow ones so they do not tip over. A raised floor may go up to 2 mm below the rim.
- Pattern depth 1–2 mm for flutes and ribs, 2–4 mm for waves
Print faster
PrusaSlicer's estimates for six holders on one 250 × 210 mm plate, with a generic printer profile:
| 0.2 mm layers | 0.3 mm layers | Filament | |
|---|---|---|---|
| 2 mm wall, round cavity | 45 h 30 m | 31 h 44 m | 252 cm³ |
| 0.9 mm shell, two lines | 12 h 25 m | 10 h 02 m | 103 cm³ |
- Your printer's own profile is faster in absolute terms. The ratio stays about 3 to 1.
- One Ø50 × 80 holder at 0.3 mm layers: 1 h 16 m with 2 lines, 49 m with 1 line.
- Why 0.8 and 0.5 mm: PrusaSlicer prints them as exactly two and one lines on every wall type. A wall in between, such as the old 0.9 mm, gets short extra paths on crossing patterns.
just slice-checkrepeats that measurement headless. - PrusaSlicer's spiral vase mode is faster again for one box, but it prints only one object per plate. It ignores the modeled cavity, so any STL from here works for it.
Where things are
| Path | Holds |
|---|---|
python/fancyboxes/ |
the geometry engine and the Studio's backend (README) |
frontend/studio/ |
the Studio's page: React, three.js (README) |
data/config/boxes.json |
the saved boxes |
data/config/defaults.env |
every tunable: data directory, ports, host |
data/stl/ |
the exported STLs, gitignored |
data/public/sample/ |
the three boxes the e2e tests start from |
.claude/skills/box-studio/ |
how an agent runs, drives and checks the Studio |
docs/decisions.md |
one line per decision, linked to its note |