Simple Text to Speech With Skulpt

On my to do list for many years has been getting my head round how Skulpt works. In case you haven’t come across it before, Skulpt is a small, client-side Javascript package that implements elements of Python in the browser. (Originally it only supported Python 2.7 syntax, but the master branch has now moved to supporting Python 3.7 syntax.)

My proximal reason for playing with it is that it is used in a Ev3devSim, a simple browser based robot simulator that I’m pretty sure we’re going to use in a course update. I’m itching to get back to tinkering with it, but as we’re on strike, I’m not going to until the strike action is over. This will make it hard to get it into the state I want it, and to develop the activities I’d like to create around it, by the time that they’re required to meet handover deadlines, but the strike action seems designed to cause stress and disruption to the strikers rather than the organisation we work for. Such is life. The collective decided and we’re out.

Although this is related to that, this blog has been languishing somewhat, lately, so before I forget for myself the small progress I made with tinkering with Skulpt, here’s a quick write-up.

The challenge I set myself was to create a simple text-to-speech function callable from an in-browser Skulpt Python environment that would convert a provided text string to speech using the SpeechSynthesis part of modern web browsers’ Web Speech API (this also defines a speech recognition API, which could offer some interesting accessibility directed possibilities…).

Calling the API from Javascript is straightforward enough: the speechSynthesis.speak(new SpeechSynthesisUtterance(txt)) javascript call run in a browser will speak the provided text string aloud. To ensure that the text object is a string and not some other object type, we force it to a string type in a Skulpt context by calling it as txt.$jsstr().

If we wrap this API call in a Javascript function, we can make it available by saving into a module file (such as src/lib/playsound.js) with some boilerplate that identifies the file as a loadable module and defines the function(s) available within it.

// src/lib/playsound.js
//  
// Define a Javascript function to do 
// whatever we want the Skulpt Python function to do...
function say (obj) {
    speechSynthesis.speak(new SpeechSynthesisUtterance(obj.$jsstr()))
  }

// Define the file as a Skulpt Python module
var $builtinmodule = function(name)
{
    var mod = {};

    // Add the say function to the module
    mod.say = new Sk.builtin.func(function(obj){
        say(obj);
        return new Sk.builtin.none;
    })

    return mod;
}

With node.js installed, run npm run dist in the top level repo directory. A series of tests will be executed and copies of skulpt.min.js and skulpt-stdlib.js built into the Skulpt dist folder. These files can then be loaded into a web page and used to power in-browser Python code execution.

The module can them be loaded into a Skulpt Python context and the text to speech function called as follows:

import playsound
playsound.say('hello world')

In passing, I note that the forked version of Skulpt used in BlockPy supports other handy packages not in the base Skulpt distribution, including matplotlib (src/lib/matplotlib). I’m not sure how tightly this is bound into the BlockPy UI, or how straightforward a task it would be to be able to make use of it in an Ev3Dev context?

I also note that the sqlite3 module seems to be unimplemented. Would it make sense to try to wrap kripken/sql.js in a Skulpt src/lib module, I wonder?