Parametric Box Generation for Laser Cutting with Boxes.py: Joints, Kerf, and Custom Layouts
This site already covers the manual side of laser box-making in depth: drawing finger joints in Inkscape, building panels in Illustrator or Affinity Designer, laying out cut files in CorelDraw, importing to LightBurn, and the living-hinge box howto that walks through flexible-hinge panels drawn by hand. All of that is a drawing workflow — you place nodes, draw tabs, measure joint spacing, and redraw the whole thing if a dimension changes. Boxes.py is a different category of tool entirely. It is a parametric box generator: you do not draw a box, you describe one. Enter a length, width, height, material thickness, and a joint style, pick from dozens of purpose-built generators, and it computes the finger joints, kerf-compensated tab widths, and panel layout for you, then hands you a ready-to-cut SVG or DXF. Change your mind about the height five minutes later and you regenerate the file instead of re-drawing it. For finger-jointed enclosures, organizer trays, and anything you need to iterate on dimensionally, this is dramatically faster than the manual vector workflow covered elsewhere on this site, and it is worth understanding as a complementary tool rather than a replacement — you will still want Inkscape or LightBurn for artwork, engraving, and final layout tweaks.
What Boxes.py Is and How It Differs From Drawing Boxes by Hand
Boxes.py is an open-source parametric box-generator project started by Florian Festi. Under the hood it is a Python library with a large collection of individual "generators," each one a small program that knows how to lay out a specific category of object: a plain finger-jointed box, a hinged lid box, a tray with dividers, a gearbox, a raspberry pi case, and many more. Every generator exposes a set of parameters — outer or inner dimensions, material thickness, finger width, kerf, and generator-specific options like number of compartments or lid style — and when you submit them it runs the layout math and outputs flat, nested panels as SVG, DXF, PDF, or PS, already positioned with finger joints cut to the correct width and kerf-compensated so the joints fit as designed rather than as measured. The core difference from a manual vector workflow is that the joinery is computed, not drawn: the tool knows the physical thickness of your material and the width of your laser's cut line, and it generates tab widths that account for both. In Inkscape or Illustrator you draw a tab, cut it, test-fit it, and adjust the path if it is too tight or loose. In Boxes.py you adjust one number (the kerf value) and every joint in every future box you generate is corrected at once. This also makes it fast to prototype: generating three versions of the same box at 80mm, 90mm, and 100mm tall takes seconds each, versus redrawing tab positions three times by hand.
Installing and Running Boxes.py
There are three practical ways to use it, in increasing order of setup effort and decreasing dependence on a network connection:
- Hosted web version — boxes.hackerspace-bamberg.de runs the current release in a browser. No install, works from any machine, and is the fastest way to try a generator or produce a one-off box. Every generator has its own page with a form of parameters and a live preview.
- Self-hosted web server — the same web UI can be run locally, either from a source checkout (python3 scripts/boxesserver) or from a container image, if you want it available on your network without depending on an external site, or if you are customizing a generator and want to preview changes immediately.
- Command-line / pip install — pip install boxes.py installs the library and a set of boxes command-line scripts, one per generator (for example boxes ClosedBox). This is the version worth using for batch generation: scripting a loop that produces a whole set of storage trays in different sizes, or version-controlling the exact command that generated a given enclosure alongside a project's other files, is only practical from the CLI. Parameters are passed as flags, e.g. boxes ClosedBox --x=150 --y=100 --h=60 --thickness=3 --burn=0.15 --output=case.svg.
All three run the same generator code, so a box designed on the web version can be reproduced exactly from the CLI later by matching the parameter values shown on the web form's summary.
Key Generators Worth Knowing
Boxes.py ships well over one hundred generators. Most projects only need a handful of them:
GeneratorWhat it producesTypical use ClosedBoxA simple closed rectangular box with finger-jointed walls and a friction-fit or screw-on lidGeneral enclosures, storage boxes Box (simple finger-jointed)An open-top box with finger-jointed cornersTrays, bins, quick shop storage TypeTrayA tray subdivided into a grid of compartments with configurable wall spacingParts organizers, small hardware sorting, tool trays ElectronicsBox / ElectronicsEnclosure variantsBoxes with mounting posts, cutouts for panel connectors, and lid options sized for common PCB dimensionsESP32, Raspberry Pi, and general electronics project enclosures Gearbox / Gears / GearBoxInvolute gear profiles and complete gear-train enclosuresMechanical demos, geared mechanisms HingeBox / LidBox variantsBoxes with laser-cut living or mechanical hinges built into the joineryAn alternative to hand-drawn living-hinge panels covered in this site's living hinge box guide, when you want the hinge parametrically sized to the box rather than drawn separately DrillBox / TrayLayoutCustom compartment layouts defined by a small layout language rather than a fixed gridIrregular compartment sizes, asymmetric organizersThe full generator list is browsable from the web UI's front page, grouped by category (boxes, trays, shelves, misc mechanisms). It is worth skimming once even if you only end up using ClosedBox and TypeTray regularly — several of the more obscure generators solve problems (cable organizers, book stands, speaker enclosures) that would otherwise mean drawing from scratch.
The Parameters That Matter
Three parameters determine whether a generated box actually fits together, independent of which generator you use:
- Thickness — the actual measured thickness of your material, not its nominal thickness. "3mm" plywood is very commonly 2.7–2.9mm in practice, and Baltic birch in particular varies by supplier and even by sheet. Measure with calipers and enter the real number; a 0.2mm error here is enough to make finger joints loose or split on assembly.
- Finger joint width (finger) — the target width of each finger joint tab, in millimetres. Smaller fingers give a stronger glue joint on thin material but take longer to cut (more corners = more laser moves); larger fingers cut faster but concentrate stress on fewer teeth. 6–10mm is a reasonable default for 3–6mm plywood or acrylic; drop to 3–5mm on thinner material like 2mm acrylic where a wide finger looks disproportionate and is more prone to snapping.
- Burn / kerf compensation — the width of material the laser actually removes when cutting, which Boxes.py subtracts from tabs and adds to slots so that after cutting, the joint dimensions come out correct rather than as-drawn. This is the single most important value to get right, and it is specific to your laser, your power/speed settings, and your material — not a fixed constant.
To calibrate the burn value for your machine, cut a kerf-test file (Boxes.py includes a "TrayLayout2"-style test piece, or you can generate a small finger-jointed box specifically to test-fit) at your normal cutting settings, then measure the actual slot width against the drawn width; half the difference is roughly your burn value. Because kerf changes with focus, power, speed, and even how recently the lens was cleaned, recalibrate whenever you change material type or thickness meaningfully, and keep a running note of known-good values:
MaterialTypical kerf (burn) value, Ray5 20WNotes 3mm Baltic birch plywood0.10–0.15mmVerify per sheet; grain and glue lines shift kerf slightly 6mm MDF0.15–0.20mmWider kerf than plywood at comparable settings due to density 3mm cast acrylic0.08–0.12mmSee this site's acrylic cutting settings guide for the Ray5 20W for base speed/power before calibrating kerf 2mm acrylic0.06–0.10mmThin material narrows the kerf; retest, do not reuse the 3mm valueThese are starting points, not fixed constants — always confirm with a physical test cut before committing to a full sheet layout.
Worked Example: Generating a Parts Storage Box
As a concrete walkthrough, here is generating a small hardware-sorting tray using TypeTray on the web version:
- Open the TypeTray generator page and set outer dimensions: x=200mm, y=150mm, h=40mm (the outside footprint and wall height).
- Set thickness=3.0 to match measured plywood, and burn=0.12 based on a prior kerf test on the same sheet.
- Set the compartment layout: TypeTray takes a grid of section sizes rather than a single row/column count, so specify sx (column widths, e.g. 50:50:50:50) and sy (row depths, e.g. 75:75) to get a 4x2 grid of compartments with those internal dimensions.
- Choose a wall style for the internal dividers (through, or with finger joints where dividers meet the outer wall) and a floor/no-floor option.
- Generate, and review the SVG preview: check that panel outlines don't overlap (they should be auto-nested for the given sheet size, but verify against your actual sheet dimensions if working close to the material's edge), and that the finger count on each wall looks reasonable — very short walls sometimes generate only one or two fingers, which is structurally weak and worth increasing the finger count or reducing finger width for.
- Download the SVG.
The same box, reproduced from the CLI for a repeat run, would be: boxes TypeTray --x=200 --y=150 --h=40 --thickness=3.0 --burn=0.12 --sx=50:50:50:50 --sy=75:75 --output=parts-tray.svg.
Importing Into LightBurn and Adjusting for the Ray5 20W
Boxes.py's SVG output imports cleanly into LightBurn, but a few adjustments matter before sending it to the laser:
- On import, check LightBurn's document units and confirm the imported panel dimensions match what you specified in Boxes.py (millimetres in, millimetres out by default) — a units mismatch on import is the most common cause of a box that looks right on screen but is scaled wrong when cut.
- Boxes.py outputs every cut line as a single stroke color by default; in LightBurn, select all and assign your standard cut layer (speed/power for the material and thickness, per this site's LightBurn setup guide and the acrylic cutting settings guide for the Ray5 20W) rather than leaving lines on LightBurn's default layer.
- If a generator produced text labels or engraved reference marks (some generators optionally label parts for assembly), separate those onto an engrave layer with appropriate power/speed before running the job — they will be on the same import color otherwise and get cut through rather than engraved.
- Use LightBurn's nesting/arrange tools to fit the generated panels onto your actual sheet size if Boxes.py's output exceeds it; the panels are independent closed shapes and rearrange without affecting the joinery, since the joint math was already baked into each panel's outline.
- Do a final dry-run at low power or with the laser off to confirm the head clears the full travel of the imported layout before committing to a full-power run, especially for larger enclosures that push close to the Ray5 20W's bed limits.
Troubleshooting
SymptomLikely causeFix Joints too tight, panels crack on assemblyBurn/kerf value too low (laser removing more material than compensated for)Increase the burn value slightly and regenerate; re-run a kerf test cut Joints loose, box is wobbly or falls apartBurn/kerf value too high, or material thinner than the thickness parameter enteredDecrease burn value; re-measure material with calipers and correct the thickness parameter Some fingers are much narrower than others on the same edgeEdge length does not divide evenly by the finger width, so Boxes.py's algorithm compresses the leftover into extra fingersAdjust finger width slightly or change the box dimension by a few millimetres so it divides more evenly Box fits together but lid doesn't sit flushLid style parameter mismatched with wall height, or a hinge/lip generator variant selected unintentionallyRe-check the generator's lid/cover options; some generators default to a friction-fit lip lid rather than a flush cover Panels overlap in the exported fileGenerated layout exceeds the default sheet size assumed by the generatorSet the sheet width/height parameters (where available) or re-nest manually in LightBurn/Inkscape after import Compartment grid in TypeTray doesn't match expected internal sizesx/sy values are being read as internal compartment dimensions, not including divider thickness, and totals don't match the outer x/yRecalculate sx/sy so the sum of compartments plus (dividers × thickness) equals the outer dimension, or let outer dimensions be inferred by omitting x/y Text labels get cut instead of engravedLabel/marking layer imported on the same cut-color layerReassign label geometry to an engrave layer in LightBurn before running the jobBoxes.py is not a replacement for a vector editor — you will still reach for Inkscape or Affinity Designer for artwork, panel engraving, or one-off shapes that don't fit a generator's assumptions, and LightBurn remains the tool that actually drives the cut. What it replaces is the tedious, error-prone part of finger-jointed box design: manually computing tab counts, redrawing joints after every dimension change, and re-deriving kerf compensation by eye. Once a kerf value is calibrated for a given material and machine setting, it carries forward to every future box, and the difference between wanting a box in a new size and having a cut-ready file drops from redrawing an entire panel set to changing a few numbers and re-exporting. For anyone producing more than the occasional one-off enclosure, that alone is worth the small learning curve of picking the right generator and understanding its parameter set.