     {name, fullName, abbrs} = chord
     Chords[key] = chord for key in [name, fullName].concat(abbrs)
     Chords[chord.pitchClasses] = chord
+
+
+#
+# Chord Progressions
+#
+
+Chord.progression = (tonic, chords) ->
+  scale = [0, 2, 4, 5, 7, 9, 11]
+  romanNumerals = 'I II III IV V VI VII'.split(/\s+/)
+  tonic = name2midi(tonic) if typeof tonic == 'string'
+  for name in chords.split(/[\s+\-]+/)
+    cr = name.replace(/[♭67°ø\+bcd]/g, '')
+    i = romanNumerals.indexOf(cr.toUpperCase())
+    if i >= 0
+      acc = 0
+      acc = -1 if name.match /♭/
+      chordRoot = midi2name(tonic + scale[i] + acc)
+      chordType = "Major"
+      chordType = "Minor" if cr == cr.toLowerCase()
+      chordType = "aug" if name.match /\+/
+      chordType = "dim" if name.match /°/
+      chordType = "maj6" if name.match /6/
+      chordType = "dom7" if name.match /7/
+      chordType = "+7" if name.match /\+7/
+      chordType = "°7" if name.match /°7/
+      chordType = "ø7" if name.match /ø7/
+      # TODO 9, 13, sharp, natural
+      chord = Chord.find(chordType).at(chordRoot)
+      chord.inversion = 1 if name.match /b/
+      chord.inversion = 2 if name.match /c/
+      chord.inversion = 3 if name.match /d/
+      chord.pitches = chord.pitches[chord.inversion...].concat chord.pitches[...chord.inversion] if chord.inversion?
+      chord
+    else
+      name
 
 
 #

    return this

  @find: (name) ->
    match = name.match(/^([a-gA-G][#b♯♭]*)(.*)$/)
    throw new Error("#{name} is not a chord name") unless match
    [noteName, chordName] = match[1...]
    throw new Error("#{name} is not a chord name") unless Chords[chordName]
    return Chords[chordName].at(noteName)

  @fromPitches: (pitches) ->
    root = pitches[0]

  {name: 'Minor-Major 7th', abbrs: ['min/maj7', 'min(maj7)'], pitchClasses: '037e'},
  {name: '6th', abbrs: ['6', 'M6', 'M6', 'maj6'], pitchClasses: '0479'},
  {name: 'Minor 6th', abbrs: ['m6', 'min6'], pitchClasses: '0379'},
].map ({name, abbr, abbrs, pitchClasses}) ->
  fullName = name
  name = name
    .replace(/Major(?!$)/, 'Maj')
    .replace(/Minor(?!$)/, 'Min')
    .replace('Dominant', 'Dom')
    .replace('Diminished', 'Dim')
  abbrs ?= [abbr]
  abbr ?= abbrs[0]
  pitchClasses = pitchClasses.match(/./g).map (c) -> {'t':10, 'e':11}[c] ? Number(c)
  new Chord {name, fullName, abbr, abbrs, pitchClasses}

# `Chords` is also indexed by chord names and abbreviations, and by pitch classes
do ->
