Claviature: SVG Piano Revisited

tools
web audio

July 31, 2022

My first post on this blog was about svg-piano, which is a little lib that helps creating correctly sized piano keys on the web. In this post, I want to investigate an alternative sizing that is better suited for digital applications, while keeping the character of the standard keyboard as authentic as possible.

The Standard Claviature

The standard claviature looks like this:

standard claviature

  • The black key tops (and bottoms) are all 12.7mm
  • The white key bottoms have 23.6mm
  • The white key tops have 13.95mm, 14.20mm, 15.05mm or 15.30mm
  • Let's not care about height, as it is pretty simple

The important point: The top side of the keys (black and white) have different widths.

Why is this a Problem?

In the digital domain, we might want to align a piano roll with a keyboard. If we use the standard sizing, we always have to make sure the columns of the piano roll grid are also sized irregularly, which is quite a hassle. If the tops of each key all had the same width, we would have no problems.

When it comes to the real world, the reason for the different key top sizes is probably that the white keys are lower thus enabling thick finger folks to reach them without getting stuck.

The Dilemma

Now, at the first glance, this seems pretty easy, just make the tops equal and call it a day, right? It's a little more complicated:

equal piano

  • b stands for the desired equal top width and w for the bottom width of the white keys.
  • The break between the keys E and F is predetermined

So, keys C, D and E (=3w) must have a total width of 5b:

3w=5b3w = 5b

w=53bw = \frac{5}{3}b

Also, keys F, G, A and B (=4w) must have a bottom width of 7b:

4w=7b4w = 7b

w=74bw = \frac{7}{4}b

Now we have two different solutions for w:

53b74b\frac{5}{3}b \neq \frac{7}{4}b

... which brings us to the dilemma:

If the top width of each key should be equal, the white keys cannot.

In the real world, it seems obvious to favor the equal size of white and black keys with larger gaps between black keys for the ease of playability.

In the digital domain, it might be more clever to scrap the equality of the white keys.

Generating the Equal Sized Keyboard

With only a little bit of SVG magic, we can render one octave:

export function Claviature() {
  const topWidth = 15;
  const blackHeight = 100;
  const whiteHeight = 145;
  const whiteWidth = (index) => (index > 2 ? 7 / 4 : 5 / 3) * topWidth;
  const whiteX = (index) =>
    Array.from({ length: index }, (_, i) => i).reduce(
      (sum, w) => sum + whiteWidth(w),
      0
    );
  return (
    <svg>
      {Array.from({ length: 7 }, (_, i) => i).map((index) => (
        <rect
          key={index}
          x={whiteX(index)}
          stroke="black"
          strokeWidth="1px"
          fill="white"
          y={0}
          width={whiteWidth(index)}
          height={whiteHeight}
        />
      ))}
      {[0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0].map(
        (place, index) =>
          place && (
            <rect
              key={index}
              x={index * topWidth}
              y={0}
              width={topWidth}
              height={blackHeight}
            />
          )
      )}
    </svg>
  );
}

Result:

0000000

With Options

Using the above Code as a basis, I added all the options from svg-piano:

range

<Claviature options={{ range: ['A1', 'C4'] }} />

palette

<Claviature options={{ palette: ['#F2F2EF', '#39383D'] }} />

stroke

<Claviature options={{ stroke: 'steelblue' }} />

colorize

<Claviature
  options={{
    colorize: [
      { keys: ['C3', 'E3', 'G3'], color: 'steelblue' },
      { keys: ['A3', 'C#4', 'E4'], color: 'tomato' },
    ],
  }}
/>

scaleX + scaleX

<Claviature options={{ scaleX: 0.5, scaleY: 0.5 }} />

upperHeight + lowerHeight

<Claviature options={{ upperHeight: 50, lowerHeight: 20 }} />

onClick

<Claviature
  onClick={(key) => {
    console.log('clicked', key);
    alert(`clicked ${key}`);
  }}
/>

labels

<Claviature
  options={{
    colorize: [{ keys: ['C3', 'E3', 'G3', 'Bb3'], color: 'steelblue' }],
    labels: { C3: '1', E3: '3', G3: '5', Bb3: 'b7' },
  }}
/>
113355b7b7

topLabels

<Claviature
  options={{
    topLabels: true,
    colorize: [{ keys: ['C3', 'E3', 'G3', 'Bb3'], color: 'steelblue' }],
    labels: { C3: '1', E3: '3', G3: '5', Bb3: 'b7' },
  }}
/>
113355b7b7

The library

You can use this by installing claviature from npm. Check out the claviature github repo.

Felix Roos 2023