TraceNote: Learn Code Tracing by Playing Music

Code tracing is the ability to read code, understand how the program flows and variable values change, and predict program output. It is a critical skill that supports students’ ability to write code.
It makes sense – we teach students how to read natural language before we teach them to write. Why shouldn’t it be the same for code?
But there’s a big challenge to teaching code tracing: It’s tedious for students, requiring them to carefully track variable values, and repeat the process until they build fluency.
What if students could learn to trace code in a way that was fun, and made it easy to build intuitive fluency in this critical skill?
TraceNote
That’s where TraceNote comes in. TraceNote is a demo I built to explore the question of how students could learn to trace code by playing music. In TraceNote, students learn to trace a simplified programming language that represents music. Students trace the program, and when it says to play a note, students play that note, using a gamepad or keyboard. Students can only play the correct next note in the tune if they’ve correctly traced the program so far.
Students don’t have to know anything about musical notation. TraceNote uses arrows to represent notes, so playing and will play three ascending notes in the tune’s key. Here’s a very simple example (sound on!).
Students can trace the code themselves, or see a worked example as the computer plays through it. If students get lost while tracing, or make a mistake, they can retrace their steps and try again.
Iteration
We can easily introduce fundamental programming concepts, like iteration, with a small modification to the traced code.
Variables
Tracing the value of variables is a key skill for novice programmers. Trace tables, where students keep track of the value of each variable as they walk through a program’s execution, are a great way to do this… but they’re also quite tedious for the student. In TraceNote, the trace table is automatically updated, but only after a student gets the next note right. This way, the student only has to keep track of variable changes since the last note in their heads – the computer keeps track of the rest.
In TraceNote, we can indicate that a variable in incremented ⭮ or decremented ⟲ with the same rotational notation.
Functions and Lists
We can even introduce more advanced concepts like functions and lists!
In music, there are many common sequences of notes, like arpeggios, that start on a root note and then play a series of notes relative to it. Some of these “functions” are universal, and some are specific to a song.
Lists represent an important challenge for code tracing. They can also represent sequences of musical notes that can be reused or modified.
Complex Tunes
Putting it all together, we can create “programs” for complex tunes, that the student can enjoy playing once they’ve gained fluency tracing code!
Implementation
TraceNote was built in TypeScript and features its own internal interpreter. Constructing a level means building the abstract syntax tree (AST) for that level. The code to construct the Arpeggio level above to teach functions looks like this:
levels.push(new class extends Level {
name = "Many Arpeggio";
category = "Functions";
arp = 'Arpeggio'
override getMain(): Block {
return new Block()
.addCommand(new FunctionCall(this.arp, new Literal(1)))
.addCommand(new FunctionCall(this.arp, new Literal(2)))
.addCommand(new FunctionCall(this.arp, new Literal(3)))
.addCommand(new FunctionCall(this.arp, new Literal(4)))
}
override addFunctions(program: Program) {
program.functions.push(new FunctionDefinition(this.arp, [Note1Var], new Block()
.addCommand(new Pick(new GetVariable(Note1Var)))
.addCommand(new ChangeVariable(Note1Var, new Literal(2)))
.addCommand(new Pick(new GetVariable(Note1Var)))
.addCommand(new ChangeVariable(Note1Var, new Literal(2)))
.addCommand(new Pick(new GetVariable(Note1Var)))
.addCommand(new ChangeVariable(Note1Var, new Literal(3)))
.addCommand(new Pick(new GetVariable(Note1Var)))
))
}
});