This codelab will teach you how to modify a simple web app to include the Blockly visual programming library.

What is Blockly?

Blockly is a library for building block programming apps. Block programming allows users to create scripts and programs by using visual blocks, even if they do not know any programming language. Blockly includes everything you need for defining and rendering blocks in a drag-n-drop editor. Each block represents a chunk of code that can be easily stacked and translated into code. It can be used to let users customize components and add behaviors to parts of the app.

What you will build

MusicMaker, a web app where you can program buttons to play different sounds, using Blockly.

What you'll learn

What you'll need

This codelab is focused on Blockly. The app structure, non-relevant concepts and code are glossed over and are provided for you to simply copy and paste.

Download the sample code

You can get the sample code for this code by either downloading the zip here:

Download zip

or by cloning this git repo:

git clone https://github.com/google/blockly.git

If you downloaded the source as a zip, unpacking it should give you a root folder blockly-master.

Install and verify web server

While you're free to use your own web server, this codelab is designed to work well with the Chrome Web Server. If you don't have that app installed yet, you can install it from the Chrome Web Store.

Install Web Server for Chrome

After installing the Web Server for Chrome app, click on the Apps shortcut on the bookmarks bar:

In the ensuing window, click on the Web Server icon:

You'll see this dialog next, which allows you to configure your local web server:

Click the choose folder button, and select the blockly root folder. This will enable you to serve your work in progress via the URL highlighted in the web server dialog (in the Web Server URL(s) section).

Under Options, check the box next to "Automatically show index.html", as shown below:

Then stop and restart the server by sliding the toggle labeled "Web Server: STARTED" to the left and then back to the right.

Now visit your site in your web browser (by clicking on the highlighted Web Server URL). In the browser, navigate to the demos/codelab/app directory. You should see a page that looks like this:

Play Mode

By default, the app launches in "Play Mode". In this mode, you can see 9 buttons. None of them can do anything yet. The idea is to let user define custom behaviors for each button, using Blockly.

Edit mode

By tapping the EDIT button, you can switch to edit mode. In this mode, tapping a button will display an editor, which is where you can "code" how sounds should play for that button. For now, the editor screen is empty.

You can go back to the play mode by tapping SAVE and then DONE buttons.

Now that you know what you'll be building, let's add Blockly dependencies to the app.

  1. Navigate to demos/codelab/app/.
  2. Open the index.html file in a text editor.

Add Blockly javascript libraries

At the end of index.html file, you can see two scripts being imported:

Add Blockly scripts just before these two scripts. The order is important, because we will use Blockly objects later in main.js.

First, include the core Blockly script and the core blocks set.

<script src="../../../blockly_compressed.js"></script>
<script src="../../../blocks_compressed.js"></script>

Then include the messages the blocks are using for the user's language (in this case English):

<script src="../../../msg/js/en.js"></script>

In index.html file, in the <main> section, you can see a div with the id "blockly-div":

<div id="blockly-div" style="height: 480px; width: 400px;"></div>

This is where we will insert Blockly editor. The div has a defined width and height, because we will insert a fixed size workspace into it.

A Blockly workspace consists of two parts: an area where the blocks to be converted to code are assembled and a toolbox containing all blocks that are available to user.

Define Blockly toolbox

Let's define the toolbox. Add the structure of the toolbox just after the blockly-div:

<xml id="toolbox" style="display: none">
  <category name="Loops" colour="120">
    <block type="controls_repeat_ext">
      <value name="TIMES">
        <shadow type="math_number">
          <field name="NUM">5</field>
        </shadow>
      </value>
    </block>
  </category>
</xml>

This XML defines a toolbox with a single "repeat loop" block inside a single category named "Loops".

We set the display style of the toolbox to none, because we do not intend to display the XML structure on our web page - it will be just used to construct the toolbox programmatically.

Add Blockly workspace to the edit view

Now open the scripts/main.js file. Just before the end of the main function, add code to inject Blockly editor:

Blockly.inject('blockly-div', {
  media: '../../../media/',
  toolbox: document.getElementById('toolbox'),
  toolboxPosition: 'end',
  horizontalLayout: true,
  scrollbars: false
});

Let's look at the options we used to initiate blockly editor:

Now refresh the page. Select the EDIT mode, then tap on one of the buttons. You should see a Blockly editor:

Tap on the Loops category. You should be able to drag and drop the available loop block to the workspace and back.

Next, we want to add a custom "play sound" block to our workspace:

Create sound block script file.

To define a custom block in Blockly, it must be specified inside a js file. Create a JS file to define a new "play sound" block:

  1. Add sound_blocks.js file in the scripts directory.
  2. Add the following code to sound_blocks.js:
Blockly.defineBlocksWithJsonArray([
  {
    "type": "play_sound",
    "message0": "Play %1",
    "args0": [
      {
        "type": "field_dropdown",
        "name": "VALUE",
        "options": [
          ["C4", "sounds/c4.m4a"],
          ["D4", "sounds/d4.m4a"],
          ["E4", "sounds/e4.m4a"],
          ["F4", "sounds/f4.m4a"],
          ["G4", "sounds/g4.m4a"],
          ["A5", "sounds/a5.m4a"],
          ["B5", "sounds/b5.m4a"],
          ["C5", "sounds/c5.m4a"]
        ]
      }
    ],
    "previousStatement": null,
    "nextStatement": null,
    "colour": 355,
    "tooltip": "",
    "helpUrl": ""
  }
]);

The JSON inside defineBlocksWithJsonArray call describes how the block looks like, e.g. its color, content, types of connectors etc. The type: "play_sound" property defines the block's name, which we will use to inject the block to our workspace.

Add the sound block to the toolbox

Open index.html. First, let's import the newly created sound_blocks.js file:

<script src="scripts/sound_blocks.js"></script>

Now we can update the toolbox to include the new sound block.

The XML structure for toolbox already contains a "Loops" category. After that category, add a "Sounds" category that includes our custom play_sound block:

<xml>
  <category name="Loops" colour="120">
    ...
  </category>
  <category name="Sounds" colour="170">
    <block type="play_sound"></block>
  </category>

</xml>

Run the app one more time, and play around with the new "Sounds" category and the new "Play (sound)" block. It should look like this:

Once the button behavior is defined by the user, it needs to be saved for later use.

Add the save method

Open the scripts/main.js. Add the following code to the save() method:

let xml = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
button.blocklyXml = xml;

This method takes the Blockly workspace, exports it to an XML DOM structure and stores it in a blocklyXml property on the button. This way an exported xml for the block sequence gets associated with a particular button.

Add the load method

Similarly, when a user opens the editor, the blocks previously associated with this button should get loaded into the workspace.

In the scripts/main.js file, add loadWorkspace function:

function loadWorkspace(button) {
  let workspace = Blockly.getMainWorkspace();
  workspace.clear();
  if (button.blocklyXml) {
    Blockly.Xml.domToWorkspace(button.blocklyXml, workspace);
  }
}

It clears the workspace from any blocks and then loads the blocks defined in the XML in blocklyXml property of the clicked button.

Call this function from enableBlocklyMode method:

function enableBlocklyMode(e) {
  ...
  loadWorkspace(currentButton);
}

Now, test the code. Edit the workspace for one of the buttons, add some blocks, save it, and reopen it. The workspace should still contain the blocks you added.

Now that each button can be configured with its own Blockly workspace, the next thing we want to do is to generate JavaScript code from each workspace.

This generated code will be run by the browser, effectively executing the blocks set up in the Blockly workspace.

Add a blockly generator for javascript

Blockly can generate code from blocks for different languages, e.g. JavaScript, Python, or PHP. Let's add the JavaScript generator to our app.

Open index.html and import the javascript_compressed.js file:

<script src="../../../javascript_compressed.js"></script>

It will be used by sound_blocks.js, so be sure to add the script tag for javascript_compressed.js above the sound_blocks.js script.

Add a JavaScript generator for the sound block

When Blockly generates JavaScript code for blocks in a workspace, it translates each block into code. By default, it knows how to translate all library-provided default blocks into JavaScript code. However, for any custom blocks, we need to specify our own translation functions (aka. code generators).

Navigate to scripts/sound_blocks.js. Add the following function definition to this file:

Blockly.JavaScript['play_sound'] = function(block) {
  var value = '\'' + block.getFieldValue('VALUE') + '\'';
  return 'MusicMaker.queueSound(' + value + ');\n';
};

With this translation function, the following play_sound block:

translates into the JavaScript code "MusicMaker.queueSound('Sounds/c4.m4a');".

When a button is pressed, we want to execute the custom code associated with it.

In scripts/main.js. Add the following code to the handlePlay method.

First, load the workspace content associated with the pressed button:

loadWorkspace(event.target);

Next, generate the code out of that workspace:

Blockly.JavaScript.addReservedWords('code');
var code = Blockly.JavaScript.workspaceToCode(
  Blockly.getMainWorkspace());
code += 'MusicMaker.play();';

Because the code is "written" by the user, we do not have full control over its contents. This is why we added code to the list of reserved words so that if the user's code contains a variable of that name it will be automatically renamed instead of colliding.

The user's code will consist of many MusicMaker.queueSound calls. At the end of our generated script, add MusicMaker.play call to play all the sounds added to the queue.

Finally, execute the script with the eval function. Wrap it in a try/catch so that any runtime errors are logged to the console, instead of failing quietly.:

try {
  eval(code);
} catch (error) {
  console.log(error);
}

The end result should look like this:

function handlePlay(event) {
  loadWorkspace(event.target);
  Blockly.JavaScript.addReservedWords('code');
  var code = Blockly.JavaScript.workspaceToCode(
      Blockly.getMainWorkspace());
  code += 'MusicMaker.play();';
  try {
    eval(code);
  } catch (error) {
    console.log(error);
  }
}

Run the app and try it out! Edit one of the button to play a C4 sound 5 times:

Save and exit the edit mode. Now if you tap this button, you should hear the C4 sound played 5 times.

And with that, you're done with the Blockly codelab! If you'd like to continue playing with the app, we suggest adding or changing the available blocks. There are sample sound files in the sounds/ folder - try hooking them up to a new block!

For more documentation, visit the Blockly developer site.

Additionally, Blockly has an active developer forum. Please drop by and say hello. We're happy to answer any questions or give advice on best practices for building an app with Blockly. Feel free to show us your prototypes early; collectively we have a lot of experience and can offer hints which will save you time.

And if you are an active Blockly developer, help us focus our development efforts by telling us what you are doing with Blockly. The questionnaire only takes a few minutes and will help us better support the Blockly community.