Web Speech API golfing (plus our new best JS golfing tricks)

October 2016

The Codegolf Team and I constantly make new codegolf-related discoveries, sometimes completely randomly.

Our most recent ones are the following:

  • ES6 template literals allow to get rid of parenthesis in functions accepting a string or a string + other parameters. Examples:
    The last snippet may look a little cryptic, but yes, you can replace one comma in the template literal with an invokation of the next parameter (here, it's 99). The browser splits the template literal into an array (["foo=1,bar=2","baz=3"]), which is transformed into a string ("foo=1,bar=2,baz=3") which in turn, is passed to setInterval, along with the second parameter (99).

  • The obsolete HTML attribute bgColor (that we generally set to the body) can also be set to "document":
    And remember that bgColor is much more permissive than CSS. You can even use "chucknorris" inside!

  • The code we put inside <svg onload=...> is interpreted with two implied scopes: "this" and "document". So it's totally possible to write things like:
    <svg onload=outerHTML="Hi!">
    <svg onload=write`Hi!`>
    <svg onload=bgColor=123456>

  • The "Vertical Tab" control character (U+000B), a.k.a VT, is interpreted as a space in JS but not in HTML. As a result, we can write this kind of attributes with no quotes:
    <svg onload=alert(newDate)>
    (there's a VT between "new" and "Date").
    Sadly, you can't replace spaces in strings with this character because it won't show up in your page.

We also discovered the Web Speech API, allowing to make your browsers generate voices from any text you input (Speech Synthesis) and vice-versa (Speech Recognition)!

Naturally, we tried to golf these new features as much as possible, and here is the result:

And as many people are curious about how we make things like that, here's the detail, for miniSpeechSynthesis, golfed by @p01, @subzey, @nderscore, @veubeke, @literallylara, @sqaxomonophonen and I:

// Non golfed
<input id=i><button onclick="s=speechSynthesis;u=new SpeechSynthesisUtterance(i.value);u.voice=s.getVoices()[0];s.speak(u)">Speak!

// Use new() to avoid using a space, and remove attribute quotes
<input id=i><button onclick=s=speechSynthesis;u=new(SpeechSynthesisUtterance)(i.value);u.voice=s.getVoices()[0];s.speak(u)>Speak!

// Add speechSynthesis's scope to get rid of "s."
<input id=i><button onclick=with(speechSynthesis)u=new(SpeechSynthesisUtterance)(i.value),u.voice=getVoices()[0],speak(u)>Speak!

//Use ES6 destructuring to set u.voice to getVoices()[0] implicitly
<input id=i><button onclick=with(speechSynthesis)u=new(SpeechSynthesisUtterance)(i.value),[u.voice]=getVoices(),speak(u)>Speak!

// Move a bit of previous code in empty parenthesis
<input id=i><button onclick=with(speechSynthesis)[u.voice]=getVoices(u=new(SpeechSynthesisUtterance)(i.value)),speak(u)>Speak!

// Get rid of the button (speak when the input changes), the voice and the id "i" because it's implied now
<input onchange=speechSynthesis.speak(new(SpeechSynthesisUtterance(value)))>

// Use the onblur event (shorter and more handy: indeed, triggering the speaker at each keystroke was not nice)
<input onblur=speechSynthesis.speak(new(SpeechSynthesisUtterance)(value))>

// Use a VT character between "new" and "SpeechSynthesisUtterance"
<input onblur=speechSynthesis.speak(newSpeechSynthesisUtterance(value))>


The other apps use very similar techniques as you can see for example in the source code of miniSpeechRecognition (webkit only):

<svg onload='with(new webkitSpeechRecognition)start(onresult=e=>write(e.results[0][0].transcript))'>

...or its bgColor variant, using the trick described at the beginning of this article:
<svg onload='with(new webkitSpeechRecognition)start(onresult=e=>bgColor=e.results[0][0].transcript)'>

... and as a bonus, an app that repeats with Speech Synthesis what you say in the microphone, interpreted by Speech Recognition! The lite version is only 146b (webkit only)
<svg onload='with(new webkitSpeechRecognition)start(onresult=e=>speechSynthesis.speak(new SpeechSynthesisUtterance(e.results[0][0].transcript)))'>


Update 01/17: a nice article on appendto.com explains these tricks with more detail.