Fingering.tab(), Fretboard.tab(), and the `pytheory fingering` CLI rendered tablature inverted — low E on top, high e on the bottom — because they followed the fretboard's low-to-high data orientation instead of the fixed tab convention. ASCII tab has one universal rule: the highest-pitched string goes on top. All three now render high-e-on-top regardless of the board's high_to_low setting, matching Part.to_tab() which was already correct. The data model (positions, string_names, repr) is unchanged — only the rendered tab flips. SVG chord diagrams were already correct. Same "a render path honored only part of the convention" class as the recent audio/notation fixes. Docs, README, and the guitar skill examples updated to the corrected orientation. New tests: high-e-on-top ordering, orientation-independence across high_to_low boards, and parity between Fretboard.tab()/NamedChord.tab(). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.6 KiB
name, description, license, allowed-tools
| name | description | license | allowed-tools |
|---|---|---|---|
| playing-guitar-with-pytheory | Help guitarists and string players with PyTheory — chord fingerings and shapes, ASCII tablature, chord identification, scale diagrams, SVG/PNG diagram images, alternate tunings and capo, Nashville number charts, and a real-time strobe tuner. Use whenever the user asks for a chord shape or fingering ("how do I play F#m7b5"), a tab, a chord/scale/arpeggio *image* to embed, what chord some frets/notes make, a scale diagram on the fretboard, a drop-D / DADGAD / open-G voicing, a capo position, a Nashville chart, or how to tune up — for guitar, bass, ukulele, mandolin, banjo, and ~20 other stringed instruments. | MIT | Write, Read, Bash(python3:*), Bash(uv run:*), Bash(pytheory:*) |
Playing guitar (and strings) with PyTheory
PyTheory models the fretboard: chord shapes, fingerings, tablature, scale diagrams, alternate tunings, and chord identification — for guitar and ~20 other stringed instruments — plus a real-time tuner. The fretboard tools are pure Python (run a script or one-liner); the tuner is a CLI that listens to your mic.
If PyTheory isn't installed, prefer uv when available: uv pip install pytheory
(or uv add pytheory in a uv project); otherwise pip install pytheory.
Fingerings, chord shapes & tab
from pytheory import Fretboard, Chord
fb = Fretboard.guitar()
print(fb.tab("Am")) # ASCII tablature for a chord (high e on top)
# A minor
# e|--0--
# B|--1--
# G|--2--
# D|--2--
# A|--0--
# E|--x--
shape = fb.chord("Am") # a Fingering object
shape.positions # (None, 0, 2, 2, 1, 0) — None = muted string
shape.string_names # ('E', 'A', 'D', 'G', 'B', 'e') — data model, low-to-high
print(shape.tab()) # same ASCII tab (always high-to-low display)
Fretboard.guitar()and friends build a fretboard;.chord("Cmaj7")returns aFingering,.tab("Cmaj7")returns the ASCII tab string directly..fingering(0, 2, 2, 1, 0, 0)builds aFingeringfrom explicit fret numbers (low-to-high), e.g. to check a shape you have in mind.- A
Fingeringhas.positions,.string_names,.tones,.tab(),.identify(), and.to_chord().
Charted-chord coverage:
.chord()/.tab()look the name up in a library of ~144 common voicings — every root in major, minor,5,6,7,m7,maj7,9,m9,maj9, anddim. Names outside that set (e.g.sus4,m7b5,aug,add9) raiseKeyError. For those, either build the shape yourself with.fingering(...), or work from the chord's notes viaChord.from_symbol("F#m7b5")(see "Identify a chord").CHARTS["western"].keys()lists everything charted.
Other instruments
All take the same .chord() / .tab() / .scale_diagram() API:
Fretboard.bass() # E1 A1 D2 G2 (.bass(five_string=True) for a low B)
Fretboard.ukulele() # G4 C4 E4 A4
Fretboard.mandolin() # G3 D4 A4 E5
Fretboard.banjo() # open G 5-string
Also available: mandola, octave_mandolin, mandocello, violin, viola,
cello, double_bass, harp, pedal_steel, oud, sitar, shamisen,
erhu, charango, pipa, balalaika, lute, twelve_string.
Alternate tunings & capo
Fretboard.guitar(tuning="drop d") # named tunings
Fretboard.guitar(tuning="dadgad")
Fretboard.guitar().capo(2) # capo at the 2nd fret
Named guitar tunings: standard, drop d, open g, open d, open e,
open a, dadgad, half step down. For anything else, pass a tuple of open-string
notes (low-to-high), e.g. Fretboard.guitar(tuning=("C2","G2","C3","G3","C4","E4")).
Identify a chord
From notes:
Chord.from_symbol("F#m7b5").identify() # 'F# half-diminished 7th'
From a shape on the neck (great for "what chord is x02210?"):
fb = Fretboard.guitar()
fb.fingering(None, 0, 2, 2, 1, 0).identify() # 'A minor'
Scale diagrams
from pytheory import TonedScale, Fretboard
scale = TonedScale(tonic="E2", system="western")["minor"]
print(Fretboard.guitar().scale_diagram(scale, frets=5))
# 0 1 2 3 4 5
# E| E | - | F#| G | - | A |
# A| A | - | B | C | - | D |
# ...
Pentatonic and blues scales live in the
bluessystem, notwestern:TonedScale(tonic="E2", system="blues")["minor pentatonic"](also"major pentatonic","blues","major blues").
Diagram images (SVG/PNG)
The ASCII diagrams above also render as clean SVG you can embed in a video,
slide, or worksheet (roots in red; PNG needs the optional cairosvg):
from pytheory import Fretboard, TonedScale
fb = Fretboard.guitar()
# Chord box — open/muted markers, barres auto-detected
fb.tab_image("Am", "Am.svg")
for name in ["C", "Am", "F", "G"]: # iterate a whole set
fb.tab_image(name, f"{name}.svg")
fb.chord("Am").to_svg(path="Am.svg") # same, straight from a Fingering
# Scale position shapes — e.g. the five pentatonic boxes, roots marked
pent = TonedScale(tonic="A4", system="blues")["minor pentatonic"]
for shape in fb.scale_shapes(pent): # one box per position (1-based .index)
shape.to_svg(path=f"pos{shape.index}.svg")
# Arpeggio map — every chord tone across the neck, labelled R/3/5/7…
fb.arpeggio_diagram("Am", "Am_arp.svg")
tab_image() covers the same ~144 charted chords as .tab(); an uncharted name
raises ValueError, so build the shape and call Fingering.to_svg() instead.
Pass fmt="png" (or a .png path) to rasterize via cairosvg. Each call returns
the SVG string, or the path when one is given.
Nashville number charts
from pytheory import Key
prog = Key("G", "major").nashville(1, 4, 5, 1)
[c.symbol for c in prog] # ['G', 'C', 'D', 'G']
Turn a melody into tab
Write a line as a part, print it as tablature for a guitarist:
from pytheory import Score, Duration
s = Score("4/4", bpm=120)
gtr = s.part("gtr", instrument="acoustic_guitar")
for n in ["E4", "G4", "A4", "B4", "D5", "E5"]:
gtr.add(n, Duration.QUARTER)
print(s.to_tab("gtr"))
# e|-0--3--5--7-|-10-12|
# B|------------|------|
# ...
Score.to_tab(part_name) accepts tuning= and frets= too.
Tune up — the real-time tuner
A CLI that listens to your microphone (the user runs it themselves — suggest the
! prefix so its output lands in the session):
$ pytheory tune --instrument guitar # strobe tuner locked to the 6 strings
$ pytheory tune --instrument guitar --chords # strum → it names the chord
$ pytheory tune --serve # browser strobe page + JS pitch stream
$ pytheory tune --ref 442 # different concert pitch
Instrument presets: guitar, bass, ukulele, violin, viola, cello,
mandolin, banjo. To get the target frequencies programmatically:
from pytheory.tuner import string_targets
string_targets("guitar") # [('E2', 82.4), ('A2', 110.0), ('D3', 146.8), ...]
string_targets("guitar", reference_pitch=442) # shifted concert pitch
Tips & gotchas
.chord()returns aFingering;.tab()returns the printable string. Use.tab()when you just want to show the user a chart.- Muted strings are
Nonein.positionsandxin the tab. - Tunings are low-to-high tuples of open-string notes; capo with
.capo(n). - Pentatonic/blues scales are in the
bluessystem, notwestern. - The tuner needs a microphone, so it's a CLI the user runs (
pytheory tune), not something to call from a rendering script. - Composing a full arrangement (chords, drums, synths, MIDI/WAV export) is the job of the composing-with-pytheory skill — this one is about the fretboard and tuning.