# Drawing a piano diagram with tikz

This and this answer provide a beautiful code to draw a piano with it's range. I tried to learn them to draw a simple and plain piano diagram with key-names (A, B, C, C#) written on the keys, but the code is nested with other requirements so much that I couldn't make any sense of it. Can anybody help me to understand the code and derive a simple piano diagram out of it?

Code -

\documentclass[border=0.25cm]{standalone}
\usepackage{etex}
\usepackage{tikz}
\usepackage{musixtex}
\usetikzlibrary{fit}
\begin{document}

\begin{tikzpicture}

\def\lastnotename{origin}
\newbox\notebox
\coordinate (origin) at (0,0);
\coordinate (stave) at (origin);
\foreach \octave [evaluate={\t=int(\octave*7-7);}] in {0, ..., 5}{
\foreach \pitch [count=\c from 0, evaluate={\x=int(\octave*7+\c+1);}] in {A,...,G}{
\ifnum\t>6
\tikzset{extract anchor/.style={anchor=south west, at=(\lastnotename.south east)}}
\else
\tikzset{extract anchor/.style={anchor=north west, at=(\lastnotename.north east)}}
\fi
\edef\notename{\pitch-\octave}
\node (\notename)  [inner sep=0pt, outer sep=0pt,text width=1cm, extract anchor/.try]  {%
\begin{music}
\instrumentnumber{1}
\instrumentnumber{2}
\nostartrule
\setstaffs1{1}
\setstaffs2{1}
\ifnum\x>1
\setclefsymbol1{\empty}
\setclefsymbol2{\empty}
\fi
\setclef1{\bass}
\setclef2{\treble}
\startextract
\transpose\t
\ifnum\t>7
\ifnum\t>14
\Notes \nextinstrument \ql{\pitch} \en
\else
\Notes \nextinstrument \qu{\pitch} \en
\fi
\else
\Notes \qu{\pitch} \en
\fi
\zendextract
\end{music}};
\xdef\lastnotename{\pitch-\octave}
}}

\node (stave) [fit={(A-0) (G-5)}] {};

\newif\ifblacknote
\foreach \octave in {0,...,5}
\foreach \pitch [count=\p, evaluate={\t={"la", "si", "so", "r\e","mi", "fa", "sol"}[\p-1];}] in {A,...,G}{
\node [anchor=base] at ([xshift=0.25cm, yshift=-0.25cm]stave.south -| \pitch-\octave.south) {\t};
\draw ([xshift=0.25cm, yshift=-1cm]stave.south -| \pitch-\octave.south west) rectangle ++(1cm,-4cm);
\blacknotefalse
\ifcase\p
\or
\blacknotetrue
\or
\or
\blacknotetrue
\or
\blacknotetrue
\or
\or
\blacknotetrue
\or
\ifnum\octave<5
\blacknotetrue
\fi
\else
\fi
\ifblacknote
\fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \pitch-\octave.south east) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
\fi
}

\end{tikzpicture}
\end{document}


If I understand the requirements correctly, the musictex part can be stripped out. To simplify further, the coordinates of the keys can be calculated using a regular double for loop over 5 octaves and 7 pitches. The letters from the pitch array can be printed on the key using the calculated x coordinate, and similar for the black keys with a sharp added to the pitch letter.

What remains is some fiddling with the positioning of the keys and pitch labels. Note that, because a new key in the original code was drawn as a rectangle, the left line of the rectangle would overlap the right line of the previous key. Normally this is not a problem, however when the note name is printed in white on the black key then the next white key would be visible on top of the note name. To counter that the white keys are drawn as three lines, only top, right, and bottom. For the very first key an additional left line is drawn before the loop.

MWE:

\documentclass[border=0.25cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}

\begin{tikzpicture}

\coordinate (origin) at (0,0);
\coordinate (stave) at (origin);
% left line of first key
\draw (0.25,-1) -- (0.25,-5);

\newif\ifblacknote
\foreach \octave in {0,...,5}
\foreach \pitch [count=\p] in {A,...,G}{
% calculate x position from octave and pitch
\pgfmathparse{\octave*7+\p+0.25}
\edef\myx{\pgfmathresult}
% draw three lines for top, right, bottom of this key
\draw (\myx,-1) -- (\myx,-5);
\draw (\myx,-1) -- ($(\myx,-1)+(-1,0)$);
\draw (\myx,-5) -- ($(\myx,-5)+(-1,0)$);
% print pitch on line
\node [anchor=base,xshift=-15] at (\pgfmathresult,-4.5) {\pitch};
\blacknotefalse
\ifcase\p
\or
\blacknotetrue
\or
\or
\blacknotetrue
\or
\blacknotetrue
\or
\or
\blacknotetrue
\or
\ifnum\octave<5
\blacknotetrue
\fi
\else
\fi
\ifblacknote
% recalculate x
\pgfmathparse{\octave*7+\p}
\fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \pgfmathresult,0) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
% print pitch on black key
\node [anchor=base,xshift=0.25cm,white] at (\pgfmathresult,-2.5) {\textbf{\pitch}${}^\sharp$};
\fi
}

\end{tikzpicture}
\end{document}


Result (partial screenshot):

Edit: added flats on the black keys. Code:

% recalculate x
\pgfmathparse{\octave*7+\p}
\edef\myx{\pgfmathresult}
% calculate flats
\pgfmathparse{array({"B","C","D","E","F","G","A"},\p-1)}
\edef\nextnote{\pgfmathresult}
\fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \myx,0) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
% print pitch on black key
\node [anchor=base,xshift=0.25cm,white] at (\myx,-2.5) {\textbf{\pitch}${}^\sharp$};
\node [anchor=base,xshift=0.25cm,white] at (\myx,-3.0) {\textbf{\nextnote}${}^\flat$};


Edit: one octave, different characters for note names.

\documentclass[border=0.5cm]{standalone}
\usepackage{fontspec}
\setmainfont[Script=Devanagari,Mapping=devanagarinumerals]{Shobhika}
\usepackage{tikz}
\usetikzlibrary{calc}
\usepackage{ulem}
\begin{document}

\begin{tikzpicture}

\coordinate (origin) at (0,0);
\coordinate (stave) at (origin);
% left line of first key
\draw (0.25,-1) -- (0.25,-5);

\newif\ifblacknote
\foreach \pitch [count=\p] in {सा,रे,ग,म,प,ध,नी}{
% calculate x position from octave and pitch
\pgfmathparse{\p+0.25}
\edef\myx{\pgfmathresult}
% draw three lines for top, right, bottom of this key
\draw (\myx,-1) -- (\myx,-5);
\draw (\myx,-1) -- ($(\myx,-1)+(-1,0)$);
\draw (\myx,-5) -- ($(\myx,-5)+(-1,0)$);
% print pitch on line
\node [anchor=base,xshift=-15] at (\pgfmathresult,-4.5) {\pitch};
\blacknotefalse
\ifcase\p
\or
\blacknotetrue
\or
\blacknotetrue
\or
\or
\blacknotetrue
\or
\blacknotetrue
\or
\blacknotetrue
\or
\else
\fi
\ifblacknote
\fill ([xshift=0.25cm, yshift=-1cm]stave.south -| \p,0) ++(-0.25cm,0) rectangle ++(0.5cm,-2.5cm);
% print pitch on black key
\pgfmathparse{array({"\underline{रे}","\underline{ग}",,"मऺ","\underline{ध}","\underline{नी}"},\p-1)}
\edef\nextnote{\pgfmathresult}
\node [anchor=base,xshift=0.25cm,white] at (\p,-2.5) {\nextnote};
\fi
}

\end{tikzpicture}
\end{document}


• +1 Not TeX related, but labeling black keys as necessarily denoting a sharp isn't really correct. Their label is key dependent. If the diagram is to be used as a general diagram it would be much better to have both the sharp and the corresponding flat on the black keys. (Not your problem, but something the OP might want to bear in mind.) – Alan Munn Feb 4 at 14:31
• @AlanMunn I've added flats. – Marijn Feb 4 at 14:38
• Now I feel much calmer. :) – Alan Munn Feb 4 at 14:38
• Great. I am interested in writing the pitches in a different script. So the {A,...,G} might not work :( – Niranjan Feb 4 at 14:40
• Instead of doing this with a for loop can we simply draw one octave with all the pitch labels typed manually? – Niranjan Feb 4 at 14:41

If you take a very close look at a musical keyboard (piano, organ, harpsichord, etc.), you may be surprised by what you see there: in any group of seven adjacent natural (white) keys, no two have the same shape. True, the C and E keys are mirror images of each other (as are the F and B keys), but none are interchangeable among seven adjacent natural keys. In fact, the dimensions and placement of the keys comprises an interesting and very practical (though unexpected, perhaps) example of linear programming. There are some useful discussions about this on the web: https://www.mathpages.com/home/kmath043.htm, http://www.quadibloc.com/other/cnv05.htm and http://datagenetics.com/blog/may32016/index.html.

The following comes from http://datagenetics.com/blog/may32016/index.html. The code is fairly straightforward (thank goodness for TikZ! -- my very first drawings were done using pencils and rulers with the aid of a slide rule) allowing for relatively easy editing and experimentation. There are countless other ways of accomplishing this, though this one has the advantage that all of the black keys have the same width, and all of the white (natural) keys have the same width -- there are some keyboard layouts for which this is not necessarily true.

\documentclass[tikz,border=3mm]{standalone}

\usepackage{xparse}

\usetikzlibrary{calc}

\newlength{\CtoBwd}%% Width of 7 adjacent white (natural) keys
\newlength{\Whitewd}%% Width of a natural key
\newlength{\Blackwd}%% Width of a black key
\newlength{\Whitefrontht}%% Distance from front of white key to black key
\newlength{\Backwdi}%% <<-- See below
\newlength{\Backwdii}%% <<-- See below
\newlength{\Backwdiii}%% <<-- See below
\newlength{\FronttoBack}%% Total length of a natural key
\newlength{\Blackht}%% Length of a black key

%% http://datagenetics.com/blog/may32016/index.html

\NewDocumentCommand{\drawaccidental}{mmmm}{% Lower left, sharp name, flat name, coordinate name for upper right of accidental
\draw[fill=black] (#1)
rectangle node[white,text width=\Backwdii,align=center]
{\bfseries\huge#2${}^\sharp$\\[1ex]#3${}^\flat$}
++ (\Blackwd,\Blackht)coordinate (#4);
}

\NewDocumentCommand{\drawname}{m}{% Coordinate name of lower left of key and name of key
\node at ($(#1) + (\Whitewd/2,\Whitefrontht/2)$) {\bfseries\Huge#1};
}

%% f b = \Backwdi
%% cs ds fs gs as = \Backwdii
%% d g a = \Backwdii
%% c e = \Backwdiii

\NewDocumentCommand{\drawkeyboard}{}{%
\draw (0,0)coordinate(C) -- ++(0,\FronttoBack) -- ++(\Backwdiii,0) --
++(0,-\Blackht)coordinate(CS) -| (\Whitewd,0)coordinate(D) -- cycle;
\drawaccidental{CS}{C}{D}{CS0}
\drawname{C}

\draw (D) -- ++(0,\Whitefrontht) -| (CS0) -- ++(\Backwdii,0) -- ++(0,-\Blackht)coordinate(DS)
-| (2\Whitewd,0)coordinate(E) -- cycle;
\drawaccidental{DS}{D}{E}{DS0}
\drawname{D}

\draw (E) -- ++(0,\Whitefrontht) -| (DS0) -- ++(\Backwdiii,0) -- ++(0,-\FronttoBack)coordinate(F)
-- cycle;
\drawname {E}

\draw (F) -- ++(0,\FronttoBack) -- ++(\Backwdi,0) -- ++(0,-\Blackht)coordinate(FS)
-| (4\Whitewd,0)coordinate(G) -- cycle;
\drawaccidental{FS}{F}{G}{FS0}
\drawname{F}

\draw (G) -- ++(0,\Whitefrontht) -| (FS0) -- ++(\Backwdii,0) -- ++(0,-\Blackht)coordinate(GS)
-| (5\Whitewd,0)coordinate(A) -- cycle;
\drawaccidental{GS}{G}{A}{GS0}
\drawname{G}

\draw (A) -- ++(0,\Whitefrontht) -| (GS0) -- ++(\Backwdii,0) -- ++(0,-\Blackht)coordinate(AS)
-| (6\Whitewd,0)coordinate(B) -- cycle;
\drawaccidental{AS}{A}{B}{AS0}
\drawname{A}

\draw (B) -- ++(0,\Whitefrontht) -| (AS0) -- ++(\Backwdi,0) -- ++(0,-\FronttoBack) -- cycle;
\drawname{B}
}

\begin{document}

\setlength{\CtoBwd}{6.5in} %% This varies between 160mm to 167mm depending upon manufacturer
\pgfmathsetlengthmacro{\tmpwd}{\CtoBwd/5880}\typeout{\tmpwd!!!!}
\setlength{\Whitewd}{\dimexpr \tmpwd*840\relax}
\setlength{\Blackwd}{\dimexpr \tmpwd*490\relax}
\setlength{\Blackht}{3.75in}
\setlength{\Whitefrontht}{2in}
\setlength{\FronttoBack}{\dimexpr \Whitefrontht + \Blackht\relax}
\setlength{\Backwdi}{\dimexpr\tmpwd*455\relax}
\setlength{\Backwdii}{\dimexpr\tmpwd*490\relax}
\setlength{\Backwdiii}{\dimexpr\tmpwd*525\relax}

\begin{tikzpicture}[rounded corners=4pt]
\foreach \oct in {0,...,3*}{% for 4 octaves; vary at will
\begin{scope}[xshift=\oct*\CtoBwd]
\drawkeyboard
\end{scope}
}
\end{tikzpicture}

\end{document}
`

I have number of these illustrations using other layouts which, originally, I prepared to assist my keyboard students.

• +1 Precise and exact answer! Thank you. – Paul Gaborit Feb 7 at 14:33