React Guitar: Performance issues

Hi people,

I’m building a React Guitar with chord database included, see a working example here:

If someone plays/marks frets on the fretboard, my Guitar component searches through all chords and displays all matches that it finds.
The chord database is a JSON object with roughly 2000 entries.
My search function is basically three nested forEach loops, and the innermost loop compares the current array of fret positions with all fret positions arrays in the data object. Which causes a considerable delay. I haven’t measured it, but it’s around 300-500 ms. You can see/hear that if you press one of the chord buttons below the fretboard. If I play the same chord on the guitar bridge above the fretboard instead, there’s almost no delay.

Here’s just the search function:

useEffect(()=>{
        const chordsDataArr = Object.values(main.chords);
        function suggestChord(currFretsArr){
            const currSuggestion = [];
            chordsDataArr.forEach(chordsArr => {
                chordsArr.forEach(chord => {
                    const positionsArr = chord.positions;
                    positionsArr.forEach(pos => {
                        const transformedFretArray = transformToFretArr(pos);
                        if (currFretsArr.every((f,index) => f === transformedFretArray[index])){
                            currSuggestion.push(`${chord.key}-${chord.suffix}`)
                        }
                    })
                })
            });
            setCurrChordSuggestion(currSuggestion);
        };
        suggestChord(currFrets)
    }, [currFrets])

Is there a better way to write such a search algorithm? What exactly is slowing it down? Would it make a difference if I extracted all the arrays from my data JSON object first, so that it’s not so deeply nested?

1 Like

First, I am a professional guitarist and I think this is a very cool, and very impressive project. good job!

As far as your question, why are you using forEach to do search? I believe that goes through every single item in the array. It seems to me, ideally you would want it to stop searching once it finds the match, and I don’t think you can bust out of a forEach loop. In this case, it seems that a good 'ol for loop would be better.

Imagine if every time you have to search through all three arrays, even though say the match is the first item of the array.

There are other ways to optimize it, but that might be a good place to start. For example, imagine a phone book and you’re looking up someone whose last name starts with “Z.” You wouldn’t want to start from “A” to do your search. Maybe you can set it up like this:
if it’s in the 2nd half of the array, only search the 2nd half.
If it’s in the 2nd half of that array, only search the 2nd half of the new array.

and so on.
I have never coded that, but I think there’s a way to do it. :slight_smile:

Awesome, I’d be absolutely interested in your feedback! :smiley:
(for example, which features would you like to have added etc).

As for the search function - the problem is, within those 2000 fret arrays, there’s about 200 that have more than one match, and I’d like to display all alternative names. I think the chords in question are rather exotic, but I can’t tell for sure. I have some musical background, but not particularly much with guitars.

Would it make sense to indeed modify my data object so it’s a plain array of objects containing only a fret array and a chord name? I’d still have to iterate over the whole array, but could leave out some of the deep comparison logic if the first fret already doesn’t match. Question is if that’s really the bottleneck.

Not really sure about the search algorithm, I basically know nothing more than FCC intermediate JS challenges :slight_smile:

As a guitarist, though, you might simplify in some of the following ways:

  1. the lowest note is the root in 90% of the cases. For example, if the lowest note is C, that means that it can only be a handful of chords–C, Cm, C7, Cm7, Cadd4, Cdim… (okay, more than a handful, but it definitely narrows it down!) You might get some speed benefits by organizing it that way.

  2. However, the lowest note is not necessarily the root. For example, once in a while you might have a C chord but the G is the lowest note (3rd fret, 6th string). The E (6th string, 0 fret) and Bb (5th string, 1st fret) are also possibilities. These are fairly exotic imo–you come across them in classical music, but usually going from one place to another.

But certain styles of pop music do make use of these chords. And sometimes other chords with bass notes that aren’t even in the chord. For example, a C with the bass of F# or some weirdness.

So one way you could handle this is have a setting where you’ll only search for basic stuff–C chords with C in the root. This will cover 90%+ of all searches.

But if they’re looking for more exotic things, then you could open it up to other possibilities.

It is true, also, that the same fingerings can equal different chord names. For example, the notes of an Amin7 chord are the same notes as a Cmaj6 chord. That does increase the complexity of the search rather significantly. It’s amazing how complex music can be, even within the scope of a 7-note scale. But then, I guess computers do a lot more with just a “two-note” scale.

All in all, if you want to display all possible matches, even alternative chords that have the same fingerings and exotic jazz or classical chords, it might not be feasible to speed things up. But if you simplify the functionality, it could be pretty quick.

The problem with covering over 90% of all searches is that it doesn’t cover 100% of searches… which is definitely what I want.

However, maybe the question I should ask myself is if what I’m doing actually makes much sense.

The current database (I got it from this repo BTW) has four alternative fingerings for most chords, and a loooong list of possible suffixes for each note. I’m not comparing notes (like ‘E-G#-B’ --> it must be Emajor or E7 or whatelse) but actual fret positions like [0,1,1,2,2,3]. How much worth is there for a guitarist to mark some frets on the fretboard in order to find out the name of the chord he’s playing?

I’ve done some research and I’ve found that guitar chords usually have quite a few different possible fingerings, some more common and widely-used than others. The database I’m using only has four alternatives for each chord. Someone could mark a totally valid Amajor chord with some unusal fingering and he’d get no result. So I’m really wondering if I should pursue this at all, or maybe rethink the whole idea.

The problem here is that spitting out all possible chords based on three notes (say ‘A-C-E’) or even four or five would give a huge number of results. As you said, it would defnitely narrow it down if I give the player an option to define the root. Or always take the lowest string as default root.

I might think about that for a while and instead start implementing other features that make more sense for now.

This is why guitar is so hard, and why most guitarist suck so much :). You can literally play E4 in six different places (you can play it on every string in different positions). Every simple chord can be played in at least 4-5 ways.

So there are a lot of possibilities. On the other hand, the strength of computers is that they can go through lots of possibilities very fast, so why not use it?

I am not sure what you mean about a guitarist marking out frets. My guess is that most beginner guitarist have no idea what the root of the chord is they are playing, but that most guitarists who are doing advanced things would know what the root of the chord is (even if the root isn’t the lowest note or is not being played on the guitar, but by the bass, for example).

Hello there,

Another guitarist here :guitar:

This will not help performance, but would improve UX a bit:

  • Instead of updating state only once all chord matches are found, you could use the suggest chord function as an asynchronous function, and call a reducer-like function within it:
function suggestChord() {
  for () {
    for() {
      if (foundAChord) {
        setMyStateNow(suggestion);
        // Keep searching
      }
    }
  }
} 
function setMyStateNow(suggestion) {
  setCurrChordSuggestion(...currChordSuggestion, suggestion);
}

You will need to find a way to clearout currChordSuggestion between each new search

potentially have the suggestChord function return a promise that, when fulfilled, clears out a nextChordSuggestion.

Otherwise, you may want to look into memo-izing your expensive operation

I know it is not much help, but hopefully you will get some ideas from it.

PS: Excellent UI, and sounds realistic. Keep it up.

1 Like

I’ll keep that in mind for later, I’ll surely need it. For now I’ve decided that my current functionality isn’t very useful. I would like to implement something that does make suggestions based on the notes entered, but that’ll require me to dive deeper into music theory. I can’t even estimate how many possible chords a working algorithm would suggest, based on 3/4/5 notes.

Thanks :love_you_gesture:
When I built that fretboard function so that the frets all have the exact realistic length, I thought I would be overdoing it a little, but I’m happy I did it.

As for the sounds, and this is also a general question:

I’ve already mentioned that I’m using a chord database from a github repo. For the sounds, I took another repo (audiosynth), I’m not using it in its entirety but took the core functions and modified them for my purpose to generate the sounds. If I’d make this a real app at some point and throw it on a website, what’s the “best practice” for crediting the resources I’ve used?


And a final question to the guitar players: If you scroll down the page, you’ll see tons of chord buttons for each suffix in the database. But how many chords does a guitar player actually use? These are the ones that are currently on the page:

major minor dim dim7 sus2 sus4 sus2sus4 7sus4
7/G alt aug 5 6 69 7 7b5 aug7 9 9b5 aug9 7b9 7#9 11 9#11 13
maj7 maj7b5 maj7#5 maj9 maj11 maj13 m6 m69 m7 m7b5 m9 m11
mmaj7 mmaj7b5 mmaj9 mmaj11 add9 madd9
/E /F /F# /G /G# /A /Bb /B /C /C# m/B m/C m/C# /D m/D /D# m/D# m/E m/F m/F# m/G m/G#

The order of that list seems to represent relevance, but I’m not really sure. Is dim more common than maj7? Is sus2sus4 more common than aug? Do those questions even make sense?
I haven’t figured out yet how to build a UI that includes all options but isn’t annoyingly overwhelming.

This depends on the licensing the author has attached to their work. Some licenses require attribution for giving you inspiration, which others say “use this content how you want, when you want…if you want, give credit, otherwise no need”.

This resource has a table with the different licenses, and a rundown of their meanings: https://choosealicense.com/appendix/

The repo you mentioned has an MIT license, which means:

  • You can use the content commercially (to make money)
  • You can modify the content as you wish
  • You cannot trademark the content as-is as your own, and must use the same license with it (others can make money off of what you have done, etc.)

Note: I am not a lawyer. So, take my interpretations with a grain of salt

Now, the author has been polite enough to include this work with such a license, and has this in their README:

If you like, feel free to share! :slight_smile: Always appreciated.

So, I highly recommend adding a With Thanks section to your page, attributing them.


The wall of chords is a bit much, regardless of how sorted it is. So, perhaps have a search-bar so the chords can be searched for?

Thanks for the license info, I’ll just contact the repo owners when in doubt.

Yeah it’s a bit non-sensical the way it is now. I’ll also dump the chord suggestions. I’ve tried to imagine what I’d find useful if I was a guitarist and came up with this instead:

Let’s say I’m playing a Gmajor and would like to transition to D, not minor but also not a standard major, so maybe something like 7 or maj7 or whatnot. So I’d like to give the player an input field or some sort of select field, where he can enter the chord he wants, and the App would then output a number of possible fingerings.
Not sure if I’ll be able to build that but it sounds like a fun challenge.