Skip to content

Key

key

Musical keys with diatonic chords and harmonic analysis.

The Key namespace generates everything you need for harmonic analysis in a major or minor key: the scale, the seven diatonic chords (triads and seventh chords), their harmonic functions (T/SD/D), the chord-scale mappings for each degree, and the secondary dominants + tritone-substitute dominants used in jazz harmony.

Minor keys expose three variants — natural, harmonic, melodic — each with its own diatonic chord set.

Example

from tonal_py import Key ck = Key.major_key("C") ck.chords ('Cmaj7', 'Dm7', 'Em7', 'Fmaj7', 'G7', 'Am7', 'Bm7b5') ck.minor_relative 'A' ck.alteration 0

Source parity: @tonaljs/key

major_key

major_key(tonic: str) -> MajorKey

Build a MajorKey for the given tonic.

The returned object has everything for harmonic analysis: the scale notes, the 7 diatonic 7th chords, their harmonic functions, secondary dominants, substitute dominants, and chord-scale mappings.

Parameters:

Name Type Description Default
tonic str

The tonic note (e.g. "C", "Bb", "F#").

required

Returns:

Type Description
MajorKey

A MajorKey dataclass. Returns NO_MAJOR_KEY if the tonic is

MajorKey

invalid.

Example

from tonal_py import Key ck = Key.major_key("C") ck.chords ('Cmaj7', 'Dm7', 'Em7', 'Fmaj7', 'G7', 'Am7', 'Bm7b5') ck.chords_harmonic_function ('T', 'SD', 'T', 'SD', 'D', 'T', 'D') ck.minor_relative 'A' ck.alteration 0 Key.major_key("D").key_signature '##'

Source code in src/tonal_py/key.py
def major_key(tonic: str) -> MajorKey:
    """Build a `MajorKey` for the given tonic.

    The returned object has everything for harmonic analysis: the scale
    notes, the 7 diatonic 7th chords, their harmonic functions, secondary
    dominants, substitute dominants, and chord-scale mappings.

    Args:
        tonic: The tonic note (e.g. `"C"`, `"Bb"`, `"F#"`).

    Returns:
        A `MajorKey` dataclass. Returns `NO_MAJOR_KEY` if the tonic is
        invalid.

    Example:
        >>> from tonal_py import Key
        >>> ck = Key.major_key("C")
        >>> ck.chords
        ('Cmaj7', 'Dm7', 'Em7', 'Fmaj7', 'G7', 'Am7', 'Bm7b5')
        >>> ck.chords_harmonic_function
        ('T', 'SD', 'T', 'SD', 'D', 'T', 'D')
        >>> ck.minor_relative
        'A'
        >>> ck.alteration
        0
        >>> Key.major_key("D").key_signature
        '##'
    """
    pc = _pitch_note.note(tonic).pc
    if not pc:
        return NO_MAJOR_KEY
    ks = _MajorScale(pc)
    alteration = _dist_in_fifths("C", pc)
    return MajorKey(
        type="major",
        tonic=ks.tonic,
        alteration=alteration,
        key_signature=_pitch_note.alt_to_acc(alteration),
        minor_relative=_note_mod.transpose(pc, "-3m"),
        grades=ks.grades,
        intervals=ks.intervals,
        scale=ks.scale,
        triads=ks.triads,
        chords=ks.chords,
        chords_harmonic_function=ks.chords_harmonic_function,
        chord_scales=ks.chord_scales,
        secondary_dominants=ks.secondary_dominants,
        secondary_dominant_supertonics=ks.secondary_dominant_supertonics,
        substitute_dominants=ks.substitute_dominants,
        substitute_dominant_supertonics=ks.substitute_dominant_supertonics,
        secondary_dominants_minor_relative=ks.secondary_dominants_minor_relative,
        substitute_dominants_minor_relative=ks.substitute_dominants_minor_relative,
    )

minor_key

minor_key(tonic: str) -> MinorKey

Build a MinorKey with natural, harmonic, and melodic sub-scales.

Each sub-scale (.natural, .harmonic, .melodic) is a KeyScale with its own diatonic chords and harmonic functions.

Parameters:

Name Type Description Default
tonic str

The tonic note.

required

Returns:

Type Description
MinorKey

A MinorKey dataclass. Returns NO_MINOR_KEY if invalid.

Example

from tonal_py import Key ak = Key.minor_key("A") ak.relative_major 'C' ak.alteration 0 ak.natural.chords ('Am7', 'Bm7b5', 'Cmaj7', 'Dm7', 'Em7', 'Fmaj7', 'G7')

Source code in src/tonal_py/key.py
def minor_key(tonic: str) -> MinorKey:
    """Build a `MinorKey` with natural, harmonic, and melodic sub-scales.

    Each sub-scale (`.natural`, `.harmonic`, `.melodic`) is a `KeyScale`
    with its own diatonic chords and harmonic functions.

    Args:
        tonic: The tonic note.

    Returns:
        A `MinorKey` dataclass. Returns `NO_MINOR_KEY` if invalid.

    Example:
        >>> from tonal_py import Key
        >>> ak = Key.minor_key("A")
        >>> ak.relative_major
        'C'
        >>> ak.alteration
        0
        >>> ak.natural.chords
        ('Am7', 'Bm7b5', 'Cmaj7', 'Dm7', 'Em7', 'Fmaj7', 'G7')
    """
    pc = _pitch_note.note(tonic).pc
    if not pc:
        return NO_MINOR_KEY
    alteration = _dist_in_fifths("C", pc) - 3
    return MinorKey(
        type="minor",
        tonic=pc,
        alteration=alteration,
        key_signature=_pitch_note.alt_to_acc(alteration),
        relative_major=_note_mod.transpose(pc, "3m"),
        natural=_NaturalScale(pc),
        harmonic=_HarmonicScale(pc),
        melodic=_MelodicScale(pc),
    )

major_key_chords

major_key_chords(tonic: str) -> list[KeyChord]

Get all chords in a major key with their harmonic role tags.

Each chord may have multiple roles (e.g. a chord that's both a diatonic V and a secondary dominant for another key center).

Parameters:

Name Type Description Default
tonic str

The tonic note.

required

Returns:

Type Description
list[KeyChord]

List of KeyChord objects, each with .name and .roles.

Example

from tonal_py import Key chords = Key.major_key_chords("C")

First chord is the tonic

chords[0].name 'Cmaj7' 'T' in chords[0].roles # tonic function True

Source code in src/tonal_py/key.py
def major_key_chords(tonic: str) -> list[KeyChord]:
    """Get all chords in a major key with their harmonic role tags.

    Each chord may have multiple roles (e.g. a chord that's both a
    diatonic V and a secondary dominant for another key center).

    Args:
        tonic: The tonic note.

    Returns:
        List of `KeyChord` objects, each with `.name` and `.roles`.

    Example:
        >>> from tonal_py import Key
        >>> chords = Key.major_key_chords("C")
        >>> # First chord is the tonic
        >>> chords[0].name
        'Cmaj7'
        >>> 'T' in chords[0].roles    # tonic function
        True
    """
    key = major_key(tonic)
    chords_dict: list[dict] = []
    # major_key inlines KeyScale fields, so we wrap it as a KeyScale-shaped object.
    key_scale = KeyScale(
        tonic=key.tonic,
        grades=key.grades,
        intervals=key.intervals,
        scale=key.scale,
        triads=key.triads,
        chords=key.chords,
        chords_harmonic_function=key.chords_harmonic_function,
        chord_scales=key.chord_scales,
        secondary_dominants=key.secondary_dominants,
        secondary_dominant_supertonics=key.secondary_dominant_supertonics,
        substitute_dominants=key.substitute_dominants,
        substitute_dominant_supertonics=key.substitute_dominant_supertonics,
        secondary_dominants_minor_relative=key.secondary_dominants_minor_relative,
        substitute_dominants_minor_relative=key.substitute_dominants_minor_relative,
    )
    _key_chords_of(key_scale, chords_dict)
    return [KeyChord(name=c["name"], roles=tuple(c["roles"])) for c in chords_dict]

minor_key_chords

minor_key_chords(tonic: str) -> list[KeyChord]

Get all chords in a minor key (combining natural/harmonic/melodic).

Parameters:

Name Type Description Default
tonic str

The tonic note.

required

Returns:

Type Description
list[KeyChord]

List of KeyChord objects with their harmonic roles. Larger

list[KeyChord]

than the major-key list because each minor sub-scale contributes

list[KeyChord]

chords.

Example

from tonal_py import Key chords = Key.minor_key_chords("A")

Has at least the 7 natural diatonic chords

len(chords) >= 7 True

Source code in src/tonal_py/key.py
def minor_key_chords(tonic: str) -> list[KeyChord]:
    """Get all chords in a minor key (combining natural/harmonic/melodic).

    Args:
        tonic: The tonic note.

    Returns:
        List of `KeyChord` objects with their harmonic roles. Larger
        than the major-key list because each minor sub-scale contributes
        chords.

    Example:
        >>> from tonal_py import Key
        >>> chords = Key.minor_key_chords("A")
        >>> # Has at least the 7 natural diatonic chords
        >>> len(chords) >= 7
        True
    """
    key = minor_key(tonic)
    chords_dict: list[dict] = []
    _key_chords_of(key.natural, chords_dict)
    _key_chords_of(key.harmonic, chords_dict)
    _key_chords_of(key.melodic, chords_dict)
    return [KeyChord(name=c["name"], roles=tuple(c["roles"])) for c in chords_dict]

major_tonic_from_key_signature

major_tonic_from_key_signature(sig: Any) -> str | None

Convert a key signature to its major-key tonic.

The signature may be either a string ("###" for 3 sharps, "bb" for 2 flats) or an integer (number of fifths from C; positive = sharps, negative = flats).

Parameters:

Name Type Description Default
sig Any

Either a sharps/flats string or an integer fifth count.

required

Returns:

Type Description
str | None

The tonic note name, or None for invalid input.

Example

from tonal_py import Key Key.major_tonic_from_key_signature("###") 'A' Key.major_tonic_from_key_signature("bb") 'Bb' Key.major_tonic_from_key_signature(2) # 2 fifths up from C 'D' Key.major_tonic_from_key_signature("garbage") is None True

Source code in src/tonal_py/key.py
def major_tonic_from_key_signature(sig: Any) -> str | None:
    """Convert a key signature to its major-key tonic.

    The signature may be either a string (`"###"` for 3 sharps, `"bb"` for
    2 flats) or an integer (number of fifths from C; positive = sharps,
    negative = flats).

    Args:
        sig: Either a sharps/flats string or an integer fifth count.

    Returns:
        The tonic note name, or `None` for invalid input.

    Example:
        >>> from tonal_py import Key
        >>> Key.major_tonic_from_key_signature("###")
        'A'
        >>> Key.major_tonic_from_key_signature("bb")
        'Bb'
        >>> Key.major_tonic_from_key_signature(2)     # 2 fifths up from C
        'D'
        >>> Key.major_tonic_from_key_signature("garbage") is None
        True
    """
    if isinstance(sig, bool):
        return None
    if isinstance(sig, int):
        return _note_mod.transpose_fifths("C", sig)
    if isinstance(sig, str) and _SIG_REGEX.match(sig):
        return _note_mod.transpose_fifths("C", _pitch_note.acc_to_alt(sig))
    return None