WebAssembly support

Hi there,

I wanted to raise attention to a topic I’ve started working on - getting scsynth to compile with emscripten, targetting wasm (WebAssembly). Since my C(++) skills are very small, I won’t be able to do this by myself, so I’m looking for people who are more proficient here. What I managed so far, is make a branch off current HEAD and see how far I get. This is the status:

Basically, as far as I can see, emscripten pthread support might actually be good enough, we just have to patch some unsupported holes like getting and setting thread priority, and then the main work is to add another Audio API back-end for OpenAL (which seems to be the API supported by emscripten). While it might also be possible to bypass that and target WebAudio API directly (not sure). Edit: Or, as Claude mentions in the ticket from experience with PD and wasm, one might look at the SDL2 port in emscripten.

With QT and libsndfile disabled, I think there is few other things that would need to be worked on apart from this audio API bridge.

(I have no idea yet, how OSC would work from a presumed client running on the JavaScript side)

4 Likes

Appetizer :slight_smile:

7 Likes

This is amazing! please keep going with this great work.

Well, it’s working now (minus libsndfile support):

https://www.sciss.de/temp/scsynth.wasm/ (currently needs Chrome/Chromium)

Happy New Year 2021 SuperCollider :fireworks:

6 Likes

Thank you for sharing this, it works beautifully here!
How does the client development look like?

Happy new year to you too!!

3 Likes

Hey,

I have slightly refactored the client interface to be more symmetric (just pushed). Basically you have a global JS object Module.oscDriver which is just a dictionary from virtual port numbers to OSC “end points”. And end point is again a JS object which responds to receive with a function(addr, data); addr is the sender’s virtual port number (in case one wants to reply), and data is the raw OSC packet in a Uint8Array. If you are writing in JavaScript, you can use the osc.js library that I included, but of course you can use any other library that can encode/decode from Uint8Array.

When the server boots, it registers such an endpoint (default port 57110), so you can send it a packet using Module.oscDriver[57110].receive(<your-port>, <raw-data>).

Are you having a particular client in mind?

Best, .h.h.

ScalaCollider can be used as client now: https://www.sciss.de/temp/scalacollider.js/ . Check out example('walters'), one of my favourites (credits: Tim Walters)

5 Likes

in|fibrillae is a web sound poem using scsynth.wasm.

3 Likes

This project seems extremely cool! I’ve been trying to figure out how I might get started making some js sketches using this webassembly scsynth setup – any ideas as to how I might get started? Ideally, I’m hoping to be able to write in sclang, a la supercolliderjs, without needing supercollider installed on the machine (e.g., in order to deploy a react app that could act like a GUI for an instrument written as one or more SynthDefs in sclang, which doesn’t seem altogether different from the in|fibrillae example here, though I’m having trouble determining whether that was written in sclang and, if so, where that sclang is located)

/in|fibrillae/ was written in Scala.js, using Mellite/SoundProcesses
(also Scala) as a language client, so replacing sclang.

Adding a vanilla JS slider to a running synth isn’t too hard.

E.g. using the latest PR @ Add wasm build of scsynth by capital-G · Pull Request #6569 · supercollider/supercollider · GitHub one can add a slider which controls the fb param of synth 1000 via

diff --git a/old_index.html b/index.html
index af283e2..c950b1c 100644
--- a/old_index.html
+++ b/index.html
@@ -136,10 +136,27 @@
                 <textarea id="console-osc" rows="8" readonly style="width: 100%;"></textarea>
             </div>
         </div>
+        <input type="range" id="my-slider" min="0" max="1.0" step="any">
     </div>

     <script src="scsynth_demo.js" type="text/javascript"></script>
     <script async type="text/javascript" src="scsynth.js"></script>
 </body>
+
+<script>
+const slider = document.getElementById("my-slider");
+
+slider.addEventListener("input", (event) => {
+    let value = event.target.value;
+    scsynth.sendOscMessage(
+        new scsynth.OscMessage().beginMessage("/n_set").addInt(1000).addString("fb").addFloat(value).endMessage().getData()
+    );
+});
+
+</script>
+
 </html>

The Synth that gets controlled is e.g.

SynthDef(\foo, {|out=0| Out.ar(out, SinOscFB.ar(200.0, \fb.kr(0.0)))}).asBytes.cs

and one simply spawns this synthdef as a synth with id 1000.

Currently one is limited to the raw Server Command Reference | SuperCollider 3.14.0-dev Help as there is no wasm port of sclang (which probably would be solved differently^^ - but I have a PR adding websockets to sclang, which would at least allow to control scsynth running in a local or remote browser).

The raw byte code of a SynthDef can be embedded into the site as well, so embedding a static patch and control it via sliders is indeed possible.