This is a description of my work on
Music Blocks JavaScript Export
during Google Summer of Code 2020 with
Sugar Labs. This repository contains my
original authored files, samples of my work, and
examples. The exported code syntax guide and API is present in
original-authored-files/js-export/samples/sample.js.
Some examples are present in examples/.
The aim of Music Blocks is to teach
beginners how to program, using a snap-together block-based instructions to
create music. The purpose of my project is to develop a framework to export any Music Blocks program to an equivalent JavaScript code, and/or write Music Blocks programs using JavaScript, either of which can run independently of the blocks.
I've used JavaScript upto the ECMAScript 8 (2017) specification widely
relying on ES6 classes, let/const, arrow functions, promises, and
finally, async/await syntactical feature from ES8. In the implementation
of a new Music Blocks widget - JavaScript Editor, I required the use of
HTML and CSS, but I used the DOM manipulation features of JavaScript
to handle those.
In addition, for the actual code generation part of the project, I relied on a library named Astring (MIT License), which is a "Tiny and fast JavaScript code generator from an ESTree-compliant AST". I generated Abstact Syntax Trees (ASTs) based on the ESTree ES2017 specification, which I serialized to ES8 JavaScript code using the library.
Finally, I relied on a library named CodeJar (MIT License), which is "an embeddable code editor for the browser". I used it to create a text editor inside my widget, and a styling library named Highlight.js (BSD-3-Clause License) for syntax highlighting.
During the community bonding period I started with refactoring some of the
crucial files to ES6 standard: completed refactoring var to let and
const, implementing arrow functions, and refactoring function prototype
syntax to ES6 classes.
Even though my project was an independent feature addition, it was imperative that I refactored and reorganised the different components of Music Blocks under the hood. Therefore, I progressed in small steps splitting the task across multiple PRs. Throughout the three months of the coding period (post release v3.1), I have 51 merged Pull Requests (copy of PRs) across 240 commits.
My work, basically, consisted of four major parts: Restructuring the components, Building the JavaScript coding framework, Generating code from block stacks, and Building the JavaScript Code Editor. Since, the restructuring was not possible to do linearly, I did it in multiple turns.
When I started, logo.js was somewhat monolithic, consisting of code that was
part of different components. As I progressed, I created two new components:
Painter and Singer, in two new files turtle-painter.js and
turtle-singer.js, respectively. These two components are now subcomponents of
the Turtle component in turtle.js. Painter contains code related to the
"art" generated by the turtle (mouse), while the Singer deals with
"music" generation. The Turtles component was separated to a new file named
turtles.js, which now totally oversees alone all Turtle objects.
Below listed are the major contributions related to restructing and refactoring the said components.
-
logo.jsPR(s) Description 2295 Ported out code related to processing notes to js/note.js, encapsulating those in a newclassnamedNoteController.2298 Converted the function prototypedefinition ofLogoto ES6classand added ES6gettersandsettersfor instance variables.2356 Ported out code related to notations to js/notation.js, encapsulating those in a newclassnamedNotation.2357 Ported out utilities related to mathematical calculations to js/utils/mathutils.js, encapsulating those in a newclassnamedMathUtility.2358 Ported out some functions related to blocks to js/blocks.js.2393 Reorganized and renamed members, and cleaned up code. 2407 Ported out utility functions related to music logic to Singer(note.jswas rebranded asturtle-singer.js, andNoteControllerasSinger) inturtle-singer.js2408, 2418, 2425, 2432 Ported out variables related to music state to Turtle/Singer. -
turtle.jsPR Description 2304 Reorganized and renamed members, and cleaned up code. 2312 Set up the Model-View-Controller (MVC) framework for the Turtlecomponent.2316 Reworked variable access. -
turtles.jsPR(s) Description 2309 Separated the Turtlescomponent fromturtle.jstoturtles.jsand set up the Model-View-Controller (MVC) framework for it.2313, 2341 Removed deprecated code and reworked variable access. 2384 Improved modularity. -
turtle-painter.jsPR Description 2382 Created Turtle's subcomponentPainterinturtle-painter.js. -
turtle-singer.jsPR Description 2397 Rebranded note.jstoturtle-singer.js, andNoteControllercomponentclasstoSinger.
I created a framework to setup the JavaScript-based Music Blocks programs.
All the code related to the framework's operation is now present in the
js/js-export/ directory. The framework consists of an API of methods
corresponding to different blocks' instructions. I conceptualized a standard
syntax, based on which the JavaScript programs are to be written in.
The export.js contains two components: Mouse and MusicBlocks. Mouse is
an encapsulation for the Turtle component, while MusicBlocks deals with all
operational behavior of the framework. Since, Music Blocks (Logo component)
programs heavily rely on asynchronous events like setTimeout, it was a big
challenge to come up with a mechanism to present a synchronous appearance in the
exported code. Beneath the hood, the framework heavily relies on ES6
promises. I used the syntactic sugar of ES8 async/await to cover up
the .then() chaining of promises. In the end, the code looks quite
synchronous and clean.
Yet more refactoring was required to connect the API ends to their behavior.
Most of the block behavior was present in the block API in the js/blocks/
directory. The behavior related to "art" generation is present in the
Painter component with methods for each. The "music" behavior had to be
extracted out. I created a new directory js/turtleactions/, and separated out
the behavior code from the block code. Each of the "music" palettes now have
their own action file which deals with their behavior, a method each for each
block. Finally, I created a directory js/js-export/API/, having files
corresponding to each palette; each file contains methods that are to be
used in the exported code, which are internally linked to corresponding
behavior methods in js/turtleactions/.
Below listed are the major contributions related to the said work.
| PR | Description |
|---|---|
| 2380 | Conceptualized the "framework" and testedfor Graphics and Pen palette blocks. |
| 2497 | Ported code related to behavior ofRhythm blocks. |
| 2536 | Ported code related to behavior of Meterblocks. |
| 2454 | Ported code related to behavior of Pitchblocks. |
| 2504 | Ported code related to behavior ofIntervals blocks. |
| 2480 | Ported code related to behavior of Toneblocks. |
| 2471 | Ported code related to behavior ofOrnament blocks. |
| 2476 | Ported code related to behavior ofVolume blocks. |
| 2477 | Ported code related to behavior of Drumblocks. |
| 2508 | Created the API files. |
| 2511 | Set up the timing of events. |
| 2547 | Add argument validation. |
I used a library called Astring to generate required exported JavaScript
code. The library takes ESTree-spec Abstract Syntax Trees (ASTs) as
input. Therefore, my work was to generate the ASTs from the block stacks.
Internally, Music Blocks maintains a linear list of Block objects which are
linked amongst themselves via their indices in the list. Since, handing the
nesting of the stacks in that manner would have been a difficult process, I
generated a non-linear tree-like recursive data-structure by parsing the
start and action block stacks. The arguments to blocks can get chained
horizontally, and so, the structure had to deal with that too.
After the non-linear data-structure generation was handled, I mechanised an
algorithm to parse that recursive structure to generate a recursive JSON
object representing the Abstract Syntax Tree in the ESTree ES2017
specification. The code could then be generated by the generate() function of
the Astring library.
All the code related to the data-structure and code generation is in
generate.js, encapsulated in the JSGenerate class. All the utilities related
to generation of the ASTs are in ASTutils.js, encapsulated in the ASTUtils
class. JSInterface in interface.js contains lists and lookups of Block
names and API methods', getters', and setters' names. It also contains
the utilities related to the API's argument validation.
Below listed is the Pull Request related to the code generation.
| PR | Description |
|---|---|
| 2525 | Added the mechanism to generate the tree-like data-structure, AST from it, and the final code. |
I created a new widget named JavaScript Editor which has a syntax-highlighted
text-box and a console to print messages during execution. The widget is
generated by using the DOM manipulation features of JavaScript. The widget
deals with displaying the generated code, editing/writing new code, and running
the JavaScript-based Music Blocks programs. The styling of the editor is done
via the use of a library named Highlight.js.
Below listed are the contributions related to the JavaScript Editor Widget.
| PR | Description |
|---|---|
| 2489 | Built the initial widget design. |
| 2514 | Add robustness features and failsafes mechanisms related to multiple Mice (Mouse objects). |
| 2547 | Enhance the editor. |
The JavaScript Widget looks like:
The ? button displays the help and guide about the syntax, the ↻ button
regenerates (for changes made in block stacks) the code, and the ▶ button
executes (runs) the code. The button 💧 button changes the style. The editor
supports four styles (themes): dark low-contrast (dracula),
light high-contrast (visual studio), dark high-contrast (railcasts),
light low-contrast (github).
For an example project,
the code generated is
let action = async mouse => {
await mouse.playNote(1 / 4, async () => {
await mouse.playPitch("do", 5);
await mouse.print(mouse.NOTEVALUE);
return mouse.ENDFLOW;
});
let box1 = 0;
let box2 = 360 / mouse.MODELENGTH;
for (let i0 = 0; i0 < mouse.MODELENGTH * 2; i0++) {
await mouse.playNote(1 / 4, async () => {
if (box1 < mouse.MODELENGTH) {
await mouse.stepPitch(1);
await mouse.turnRight(box2);
} else {
await mouse.stepPitch(-1);
await mouse.turnLeft(box2);
}
await mouse.goForward(100);
return mouse.ENDFLOW;
});
box1 = box1 + 1;
}
return mouse.ENDFLOW;
};
new Mouse(async mouse => {
await mouse.clear();
await mouse.setInstrument("guitar", async () => {
await mouse.setColor(50);
await action(mouse);
return mouse.ENDFLOW;
});
return mouse.ENDMOUSE;
});
MusicBlocks.run();
which when run (by clicking on the ▶ button on the editor), plays
audio and draws

-
Add load/save mechanism in the JavaScript Editor.
-
Add support for Heap (create a
stackdata-structure), Media, Sensors, and Ensemble blocks. -
Implement a mechanism to stop the running code.
-
ADVANCEDPreprocess the generated/written code to insertasync/await,return ENDFLOW,return ENDMOUSE, so that the user-end code can be even neater. -
ADVANCEDPreprocess the generated/written code to surround theawaitstatements intry catchblocks, so thatUncaught (in promise)errors can be handled. -
ADVANCEDRedirect console behavior in the JavaScript Widget's console. -
ADVANCEDSeparate the music generation components of the Music Blocks codebase such that JavaScript-based programs can be written and executed locally from one singleHTML(or some executable) file, i.e. there would only be the canvas and the editor in that GUI.


