Sparkline Sound-Off

reading length ~200 words
tagged under , ,
shorturl repc.co/a4zK1

To suit my needs, I started with Jeremy Keith’s Canvas-Sparkline and made some modifications. I won’t go into detail about how the Canvas API or the Web Audio API, but there are many resources available if you want a background.


I’m using the same CSV to generate the audio as I am the sparkline itself. Each of the values in the array represent how many posts I made per week for the most recent 26 weeks (half a year).

<canvas id="sparkline-demo-1" class="sparkline" width="160" height="24" data-values="0,0,0,0,0,0,0,0,4,0,0,4,9,1,4,5,2,4,2,6,4,6,4,6,5,0"></canvas>

Because this is really just a fun little easter egg as opposed to a perfect 1-to-1 representation of the data, I decided to map the values against a major pentatonic scale which results in music that is a bit more pleasant to listen to, rather than unharmonious noise if I used a chromatic scale. If I was going for a perfect representation of the data, I would stick to a chromatic scale so that the difference between notes matches their difference in numerical value.

I’ve chosen C4, also known as Middle C, for my starting base notes which represent values of 0 in my data.


Let’s pause and review a little background about musical notes and their relationship to the frequency of each note.

As an example, we’ll take A4, also known as the Stuttgart pitch. It’s the first A above middle C and sits at 440Hz. Unfortunately, frequencies for other notes aren’t quite as simple. We cannot increment 440 by a fixed amount for each consecutive note. In fact, each consecutive note’s frequency is exactly the 12th root of 2 times greater than the previous and vice versa.

graph showing the logarithmic relationship of frequencies in a diatonic scale

A4 = 440Hz

A♯4 / B♭4 = 440Hz × 12√2 = 466.16Hz

B4 = 466.16Hz × 12√2 = 493.88Hz

Because I want the music produced by the sparklines to be remotely pleasing to listen to, we need a way to reliably map our unitless number inputs to musical notes. As a result, and to make the music theory more familiar to myself, the data should map directly onto the standard chromatic scale that most modern Western music is based on.

Wikipedia has a great chart with a bunch of this sort of data.

We’ll use the following function to calculate the frequency for a given note on a piano, where A4 is the 49th key on a standard piano:

f(n) = (12√2)n-49 × 440Hz
let getFrequencyFromKeys = (key) => {
    return 2 ** ((key - 49) / 12) * 440;
};

So if we want to calculate the frequency of C4 (Middle C), the 40th key on the keyboard:

f(40) = (12√2)40-49 × 440Hz = 261.626Hz

Quintuplets

Now that we have established a way of translating each unitless value in our sparkline into a pitch, let’s get a bit more musical and start mapping to a Major Pentatonic scale.

Without going into gory detail, which you can read about here (even more reading if you’re interested), this type of scale is nice because between any two notes of the scale there exists harmony. This means that no matter what notes are chosen by the sparkline data, a mostly-pleasant tune will come back.

As opposed to a chromatic scale where notes are a semi-tone apart, a major pentatonic scale follows a pattern of whole-tones and semi-tones, which we’ll implement inside an array, each value representing one semi-tone step:

let keyIntervals = [2, 3, 2, 2, 3];

Given that our base note is C4, the 40th key on a keyboard, the possible keys that we’re working with are as follows:

40, 42, 45, 47, 49, 52, 54, 57, 59, 61, 64, 66, …

With these keys we can calculate their respective frequencies:

261.626, 293.665, 349.228, 391.995, 440.000, 523.251, 587.330, 698.456, 783.991, 880.000, 1046.502, 1174.659, …

In order to quell any feverish posting on my part, as unlikely as that may be, I am limiting the highest value for the sparkline arbitrarily to 12. This prevents clarity from being lost at the bottom end of the visual sparkline and limits the tunes that are generated from varying too wildly or playing notes which are unpleasant or imperceptible.

money = [mouth, mouth = money][0];

Here’s an example. First, we need the per-week data:

0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 9, 1, 4, 5, 2, 4, 2, 6, 4, 6, 4, 6, 5, 0

If we work through this array, item by item, using each value as the key to retrieve the correct frequency, we end up with the following keys and frequencies:

40, 40, 40, 40, 40, 40, 40, 40, 49, 40, 40, 49, 61, 42, 49, 52, 45, 49, 45, 54, 49, 54, 49, 54, 52, 40 becomes 261.626, 261.626, 261.626, 261.626, 261.626, 261.626, 261.626, 261.626, 440.000, 261.626, 261.626, 440.000, 880.000, 293.665, 440.000, 523.251, 349.228, 440.000, 349.228, 587.330, 440.000, 587.330, 440.000, 587.330, 523.251, 261.626

We can pump these values into the Web Audio API to create the tones in our browsers, playing each frequency in succession over four seconds:



Article from 04 March 2019 LiquidBase60