# Fretboard Calculator & CAD Generator (`fretboard_calc.py`)
## 📜 Overview
`fretboard_calc.py` is a **professional-grade, command-line fretboard calculation and CAD export tool** intended for **luthiers, guitar builders, CNC operators, and CAD/CAM users** who need **precise fret positions** for musical instruments — including both **traditional single-scale** and **modern multiscale (fanned fret)** designs.
Unlike many basic calculators, this script does **not** simply dump fret distances; it can:
- Compute **per-string 12-TET fret positions** accurately using the closed-form equation.
- Support **multiscale interpolation** (linear or exponential).
- Output **tabular and structured data** suitable for spreadsheets and downstream CAD workflows.
- Generate **parametric SVG or DXF drawings** of the fretboard including frets, strings, nut, bridge, and board outline.
- Apply a **datum angle rotation**, allowing the entire fretboard to be rotated around the neutral fret without changing the geometry.
The goal is to **bridge the gap between theoretical fret spacing math and practical CAD/CAM fabrication**.
---
## 🧮 Core Functionality
The script is built around **12-Tone Equal Temperament (12-TET)**:
\[
\text{pos}(n) = L - \frac{L}{2^{(n/12)}}
\]
- `L` = scale length for that string
- `n` = fret number
- `pos(n)` = distance from the nut to the `n`-th fret
This gives exact results without cumulative rounding errors.
For **multiscale instruments**, the script computes a different scale length for each string by interpolating between bass and treble scale lengths:
- **Linear mode**: even interpolation across strings.
- **Exponential mode**: interpolation weighted by a `gamma` factor, biasing toward bass or treble.
All geometry is constructed in a **neutral-fret coordinate system**, meaning the `x = 0` line runs through the chosen neutral fret. This simplifies later transformations, like rotating the entire board to a specific datum angle.
---
## 🧭 Coordinate System & Geometry
- **X-axis** = along-string direction (positive away from nut)
- **Y-axis** = string spacing direction (bass string at `y = 0`)
- All fret coordinates are stored **relative to the neutral fret** (the fret around which the board may be rotated).
### Key geometric features produced:
- Fret lines (full width across board)
- Nut and bridge edges
- Outline polygon (tapered quadrilateral)
- Optional slot kerf offsets for toolpath generation
- Optional string centerlines
---
## 🪚 Datum Angle Rotation
After the entire fretboard is computed, you can optionally **rotate everything by a given angle** around the neutral fret.
This is useful for:
- Aligning the board to your machine’s datum
- Simulating fan alignment changes
- Adjusting zeroing strategies without recomputing the entire geometry
All segments and coordinates (frets, nut, bridge, outline, slots, strings) are rotated consistently.
---
## 🧾 Output Formats
The script can emit multiple outputs in a single run:
- 📝 **Markdown (stdout)** — Readable tables of fret positions and spacings. Great for inspection and version control.
- 📊 **CSV** — Easy to load into spreadsheets or CAD/CAM tools.
- 🧱 **JSON** — Fully structured geometric and parametric data. Ideal for downstream automation.
- 🖼 **SVG** — Lightweight vector drawing with logical layers for frets, strings, outline, nut, bridge, and slot offsets.
- 📐 **DXF (ASCII)** — Minimal DXF file containing only LINE entities on named layers, suitable for import into Inkscape, Fusion 360, LightBurn, FreeCAD, etc.
---
## 🧭 Units & Measurement
- Supported units: **inches** (`in`) and **millimeters** (`mm`).
- You can specify units globally with `--unit in|mm`.
- Most length arguments accept inline suffixes:
- `25.5in` or `25.5inch`
- `648mm`
- or just a bare number, which will be interpreted in the chosen default unit.
---
## 🧰 Command Line Arguments
Run:
```
python fretboard_calc.py --help
```
to see the complete list.
Below is a breakdown of the most important options:
| Argument | Description |
|----------|-------------|
| `--frets N` | Total number of frets (required). |
| `--strings N` | Number of strings (required). |
| `--scale` | Single scale length for all strings. |
| `--bass-scale` / `--treble-scale` | Define bass and treble side scales for multiscale layouts. |
| `--scale-map` | Interpolation mode: `linear` (default) or `exp`. |
| `--scale-gamma` | Exponent for `exp` mapping (biasing). |
| `--unit` | Units: `in` or `mm`. |
| `--neutral-fret` | Index of the neutral fret (default `0`, i.e., nut). |
| `--string-spacing` | Uniform spacing between adjacent strings. |
| `--string-spacing-list` | Comma-separated list of gaps (per string). |
| `--string-spacing-file` | Path to a file containing gaps, one per line. |
| `--nut-width` / `--bridge-width` | Total fretboard width at nut and bridge. |
| `--datum-angle` | Rotation angle (degrees) applied after geometry is computed. |
| `--stroke` | Stroke width in SVG. |
| `--slot-kerf` | Saw kerf width for slot toolpaths. |
| `--emit-slot-offsets` | Emit left/right slot lines offset from fret centers. |
| `--draw-strings` | Include string centerlines in SVG/DXF. |
| `--csv` | Path to write CSV file. |
| `--json` | Path to write JSON file. |
| `--svg` | Path to write SVG drawing. |
| `--dxf` | Path to write DXF drawing. |
| `--markdown` | Force Markdown output even if files are written. |
---
## 💡 Usage Examples
### 1. Basic Single-Scale Output (Markdown Only)
```
python fretboard_calc.py \
--frets 22 --strings 6 \
--scale 25.5in --unit in
```
This will:
- Compute 6-string single-scale fret positions
- Print nicely formatted Markdown tables to stdout
- Use 25.5 inches as scale length
---
### 2. Multiscale Output with Datum Rotation and SVG
```
python fretboard_calc.py \
--frets 24 --strings 8 \
--bass-scale 27in --treble-scale 25.5in \
--neutral-fret 7 --string-spacing 0.35in \
--datum-angle 5 \
--svg board.svg --draw-strings
```
This will:
- Compute a multiscale board
- Use fret 7 as neutral
- Add 5° rotation
- Output an SVG with strings drawn
---
### 3. DXF with Kerf Slot Toolpaths
```
python fretboard_calc.py \
--frets 24 --strings 6 \
--scale 25.5in \
--nut-width 1.70in --bridge-width 2.20in \
--slot-kerf 0.023in --emit-slot-offsets \
--dxf board.dxf
```
This will:
- Generate left/right slot offsets for each fret
- Export a DXF with separate layers for OUTLINE, NUT, BRIDGE, FRETS, SLOTS, and STRINGS.
---
## 🧪 Advanced Features
### Neutral Fret Geometry
All frets are defined relative to the neutral fret, simplifying transformations and toolpath planning.
### Datum Rotation
Datum angle rotates everything about the neutral fret — useful for aligning the design to workholding setups without changing the math.
### Kerf Offsets
For sawed fret slots, the script can emit offset toolpaths corresponding to half the kerf width on either side of each fret.
### String Layout
String positions can be defined with uniform spacing or arbitrary gaps per string, enabling compound layouts.
---
## 📂 Layer Naming (DXF/SVG)
| Layer / Group | Description |
|---------------|-------------|
| `OUTLINE` | Perimeter of the fretboard |
| `NUT` | Nut edge |
| `BRIDGE` | Bridge edge |
| `FRETS` | Center fret lines |
| `SLOTS` | Left and right offset slot toolpaths (if enabled) |
| `STRINGS` | String centerlines (if enabled) |
These layer names are intentionally simple to allow **easy layer-based CAM automation**.
---
## 🛡 Recommended Workflows
- **CAD import (SVG)** — Import into Illustrator, Inkscape, Fusion 360, or FreeCAD to add additional construction features.
- **CAM import (DXF)** — Import into LightBurn or Fusion to generate toolpaths directly from named layers.
- **Spreadsheet analysis (CSV)** — Use fret positions in templates for production jigs.
- **Automation (JSON)** — Integrate with parametric CAD or custom generation pipelines.
---
## ⚠️ Common Pitfalls & Tips
- **Unrecognized arguments**: Check `--help` to make sure your CLI flags match the version of the script you're running.
- **Units**: Remember that numeric values without suffixes use the global `--unit`.
- **Neutral fret**: If you rotate around a fret other than 0, the geometry may shift visually in SVG — this is intended.
- **Datum angle precision**: A few decimal places are fine; extreme angles are allowed but will rotate the entire geometry.
- **Kerf offsets**: If `--emit-slot-offsets` is set but no `--slot-kerf` is provided, the script will exit with an error.
---
## 🧭 Development Notes
- Written in pure Python 3 — no external dependencies.
- Designed to run headless (no GUI).
- Well-structured for extension and integration with other scripts or toolchains.
- Uses minimal SVG and DXF dialects for maximum interoperability.
- Fully compatible with version control workflows (outputs are deterministic given the same input).
---
## 🧰 Example Integration
```
import json
with open('board.json') as f:
data = json.load(f)
print("Fret count:", data["frets"])
for fret in data["fret_geometry"]:
print(f"Fret {fret['fret']} angle {fret['angle_deg']} degrees")
```
## 🎸 Custom String Spacing Input
You can control the **Y-axis layout** of the strings (distance between each string centerline) using either:
- `--string-spacing` → a single uniform gap applied between all adjacent strings.
- `--string-spacing-list` → a comma-separated list of custom gaps.
- `--string-spacing-file` → a text file containing one gap per line.
---
### 🔹 1. `--string-spacing` (Uniform)
Specifies one value applied between all strings.
Example:
```bash
python3 fretboard_calc_patched.py \
--frets 24 \
--strings 6 \
--scale 25.5in \
--string-spacing 0.35in \
--svg board_uniform.svg
```
All 5 inter-string gaps are 0.35 inches.
---
### 🔹 2. `--string-spacing-list` (Inline Custom Values)
Accepts a comma-separated list of S−1 gaps (where S = number of strings).
Each value may include a unit (`in` or `mm`).
Example:
```bash
python3 fretboard_calc_patched.py \
--frets 24 \
--strings 8 \
--bass-scale 27in \
--treble-scale 25.5in \
--neutral-fret 7 \
--string-spacing-list "0.35in,0.35in,0.36in,0.36in,0.37in,0.37in,0.38in" \
--svg test_spacing_list.svg \
--stroke 0.02
```
---
### 🔹 3. `--string-spacing-file` (File-Based Custom Values)
Use a plain text file containing exactly **(strings − 1)** lines.
Each line defines the gap between one pair of adjacent strings.
Example file: `spacing.txt`
```
0.35in
0.35in
0.36in
0.36in
0.37in
0.37in
0.38in
```
Run:
```bash
python3 fretboard_calc_patched.py \
--frets 24 \
--strings 8 \
--bass-scale 27in \
--treble-scale 25.5in \
--neutral-fret 7 \
--string-spacing-file spacing.txt \
--svg test_spacing_file.svg \
--stroke 0.02
```
✅ Expected output:
```
✅ SVG written to test_spacing_file.svg
```
---
### 💡 Tips
| Behavior | Explanation |
|-----------|--------------|
| Blank lines are ignored | Helpful when commenting or spacing visually |
| Units are optional | If omitted, defaults to the unit selected by `--unit` |
| Mixed units allowed | e.g., `9mm` and `0.35in` in the same file are fine |
| Mismatched count errors | You must provide exactly S−1 lines for S strings |
| File encoding | Must be UTF-8 or plain ASCII |
---
### Example Verification
You can confirm spacing was applied correctly by checking the line:
```
**String Y positions (bass=0):** 0.000 | 0.350 | 0.700 | 1.060 | ...
```
printed in the Markdown output or within the JSON (`"string_y_positions"` field).
---
## 📜 License and Use
This script is free to use and modify for personal or professional luthiery and CAD/CAM workflows.
Attribution is appreciated but not required.
MIT License
---
## 🆘 Getting Help
- Run `python fretboard_calc.py --help` for usage.
- Check for spelling of CLI flags (many changed between versions).
- Remember to include units where appropriate.
- If you encounter geometry or import issues, inspect the SVG/DXF in a vector viewer to confirm layer contents.
---
## 🏁 Summary
`fretboard_calc.py` is designed to be a **robust, parametric, precision tool** for fretboard design:
- ✅ Exact 12-TET calculations
- ✅ Multiscale fan support
- ✅ Flexible output formats
- ✅ CNC/CAD ready geometry
- ✅ Scriptable and automatable
This makes it an excellent choice for **builders who need precision**, **automation pipelines**, and **repeatable manufacturing**.
AVR