Tag: p5

  • Developing a  JavaScript guitar chord diagramming app using p5.js

    Developing a JavaScript guitar chord diagramming app using p5.js

    part 1

    Here is my process for developing a guitar chord diagramming app, from drawing the initial sketch using the p5.js library to enabling CRUD operations for other users to be able to create and save their own guitar chord voicings and progressions.

    Background

    A short time before my daughter was born, I took a one-off lesson with jazz guitar legend Pat Martino, who was at home in South Philadelphia between tours. It was my birthday, and my wife said, “We will never have money again after the baby is born.” How right she was. It was good value, though, because four years later, I’m still trying to figure out what he taught me.

    I always say it was like meeting Gandalf, but maybe that is disrespectful to say. Pat was a very kind and patient teacher, and he took me seriously and wanted to share his knowledge of the guitar, which is vast and philosophical. I remember he also showed me some new gear he had just received, a reverb device the size of a toaster oven that made his guitar sound like storm clouds over the ocean.

    One of the things I continue to study from Pat’s lesson book Fundamentals of Guitar is how chords are related by augmented parental forms and what that means in practical terms of smooth voice leading from one to the next. I thought an app might help me understand that concept better, especially with these lovely open sounding inversions.

    The project code

    The p5.js library provides an easy-to-use sandbox environment and some custom functions to make programming for web apps more approachable for artists, musicians, teachers and others.

    In this first draft, I just want to draw one guitar chord diagram nicely, using a Mel Bay chord book as a model. Note the use of for loops to draw lines and iterate through an array. The next step will be to construct a class and instantiate additional chord objects.

    /* first, initiate global variables
        so they will be accessible within
        the setup() and draw() functions
    */
    let root;
    let third;
    let fifth;
    let mute;
    
    function setup() {
      createCanvas(106, 145);
      /* next, add data in setup() function,
        key value pairs within curly braces
        creates an easily readable set of
        variables accessible by dot
        and bracket notation
    */
    
      fifth = {
        note: "G",
        x: 10,
        y: 85,
      };
      root = {
        note: "C",
        x: 64,
        y: 133,
      };
      third = {
        note: "E",
        x: 46,
        y: 61,
      };
      /* also input values for the fingering pattern
        above the nut here. But this seems cumbersome to input.
        Maybe a function should calculate this instead?
    */
      mute = ["o", "x", "o", "o", "x", "x"];
    }
    
    function draw() {
      // print chord name centered
      textSize(12);
      textAlign(CENTER, BASELINE);
      fill("black");
      text("C 2nd Inv", width / 2, 13);
      // print fingering above nut, 18px apart, same as strings
      for (let i = 0, x = 10; i < mute.length, x < width; i += 1, x += 18) {
        textSize(9);
        textAlign(CENTER, BASELINE);
        text(mute[i], x, 21);
      }
      /* print left-hand position
        here it is at the nut, so position "1"
      */
      textSize(9);
      textAlign(LEFT, BASELINE);
      text("1", 0, 30);
    
      //draw nut with a thicker line and square caps
      strokeCap(SQUARE);
      strokeWeight(4);
      line(9, 24, 100, 24);
    
      //draw frets 24 px apart
      for (let y = 48; y < 145; y += 24) {
        strokeWeight(1);
        line(10, y, 100, y);
      }
    
      /* draw strings 18 px apart without exceeding the
          width of the canvas, starting at 10 to allow
          for left-hand position label number in the margin
      */
      for (let x = 10; x < width; x += 18) {
        strokeWeight(1);
        line(x, 24, x, 144);
      }
      /* draw a black dot with note letter name inside.
       Trial & error results in a 14px ellipse and size 12 text
       seems comfortably readable to me
       add +1 to y value of text to make it appear centered.
       
       */
    
      fill("black");
      ellipse(root.x, root.y, 14, 14);
      fill("white");
      textSize(12);
      textAlign(CENTER, CENTER);
      text(root.note, root.x, root.y + 1);
    
      fill("black");
      ellipse(third.x, third.y, 14, 14);
      fill("white");
    
      text(third.note, third.x, third.y + 1);
    
      fill("black");
      ellipse(fifth.x, fifth.y, 14, 14);
      fill("white");
    
      text(fifth.note, fifth.x, fifth.y + 1);
    }

    Be sure to look for Part 2 in which I turn into a werewolf and convert this simple sketch into slaughterhouse of JavaScript classes and objects.

    By Chris Mohnacky.