Guitar Chord Transitions with Vue.js

A basic Vue.js component for rendering guitar chord diagrams, and for animating chord transitions.
Vue.js
Author

Chris Hansen

Published

August 29, 2022

Overview

I’m not great with front-end stuff, but I quite like the idea of Vue.js components, and I dabble from time-to-time. One thing I thought I’d attempt is a component that could be used to render a guitar chord diagram. There are probably already decent examples–a quick Google turned up vue-chords, for example. However, I thought it would be interesting to create a component that showed the transition between two chords quite explicitly in order to help me visualise the change. So, here goes…

A Basic Vue.js Component

Throughout this post we’ll use a small component library called chordvuer, the source of which can be found on GitHub. This contains two basic components: GuitarChord (or guitar-chord) and GuitarChordTransitions (or guitar-chord-transitions).

GuitarChord

<GuitarChord 
  id="chord1" 
  :chord='{
    name: "G",
    fingers: [
      {finger: 1, fret: 2, strings: [5]},
      {finger: 2, fret: 3, strings: [6]},
      {finger: 3, fret: 3, strings: [2]},
      {finger: 4, fret: 3, strings: [1]}
    ],
    open: [3, 4],
    closed: []
  }'
>

GuitarChordTransitions

<GuitarChordTransitions
  id="chord2" 
  ::chords="[this.chords['C'], this.chords['G'], this.chords['Am'], this.chords['F']]"
  :duration="2000"
>

Note that chords are rendered as SVG, and much can be modified via CSS, including string colour, finger colours, fret colours, and so on. For example, the fretboard colour could be changed for a chord with id foo:

#foo .fretboard {
  fill: #eee;
}

Storing Chords

The chord representation should be easy enough to understand, though entering them by hand would still get pretty tedious. To make things a little easier, a small set of common chords are provided with the library in an object called chordvuer.chords. Common fifths are stored in an object called chordvuer.powerchords.

Choosing A Chord

As noted, the main goal was to show transitions between chords. One simple example would just be to provide a set of chords via a drop-down box. For example:


<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.36/vue.global.min.js"></script>
<script src="./assets/js/chordvuer.umd.js"></script>
<link rel="stylesheet" href="./assets/css/style.css">

<div id="app">
  <div>
  <label for="chord-name">Chord: </label>
  <select v-model="selected">
    <option v-for="(v, k) in chords" :value="k" :key="k" :selected="selected" >
      {{ v.name }}
    </option>
  </select>
  <hr>
  <guitar-chord 
    id="chord" 
    :chord="chords[this.selected]"
  ></guitar-chord>
  </div> 
</div>

<script>
  const app = Vue.createApp({
    data () {
      return {
        selected: "A"
      }
    }
  })

  app.config.globalProperties.chords = window.chordvuer.chords;
  app.component('guitar-chord', chordvuer.GuitarChord)
  app.mount('#app')
</script>

Note the source is written to use the UMD version of the component library. And please excuse the duplicates due to the separate entries for flats and sharps for the same chords.

Transitions on a Loop

Let’s do something similar to the above, but this time, let’s allow users to build up a sequence of chord transitions by selecting things from a drop-down list (for example, select C, G, A Minor, then F):



Selections: {{ selectionStr }}

<div id="app">
  <div class=controls>
    <label for="chord-name">Chord: </label>
    <select v-model="selected" >
      <option v-for="(v, k) in chords" :value="k" :key="k" >
        {{ v.name }}
      </option>
    </select>
    <br>
    <label for="dur">Transition speed (seconds): </label>
    <input type="number" min="1" max="5" v-model="speed">
    <br>
    Selections: {{ selectionStr }}
    <br>
    <button @click="reset()" >clear selections</button>
  </div>
  <hr>
  <guitar-chord-transitions 
    id="chord" 
    :duration="this.speed * 1000" 
    :chords="selections"
  ></guitar-chord-transitions>
</div>

<script>
  const app = Vue.createApp({
    data () {
      return {
        selected: null,
        selections: [],
        speed: 1
      }
    },
    watch: {
      selected() {
        this.selections.push(this.chords[this.selected])
      }
    },
    computed: {
      selectionStr() {
        if (this.selections.length === 0) return ""
        return this.selections.map((x) => x.name).join(", ")
      }
    },
    methods: {
      reset() {
        this.selections = []
      }
    }
  })

  app.config.globalProperties.chords = window.chordvuer.chords;
  app.component('guitar-chord-transitions', chordvuer.GuitarChordTransitions)
  app.mount('#app')
</script>