Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Web Workers: A musical intro

Web Workers: A musical intro

Ritesh Kumar

August 22, 2018
Tweet

More Decks by Ritesh Kumar

Other Decks in Programming

Transcript

  1. Web Workers
    A musical intro
    @ritz078
    Ritesh Kumar,
    Lead Software Engineer, Anarock

    View full-size slide

  2. for (let i = 0; i !<= 1000000000; i!++) {
    Math.random();
    }

    View full-size slide

  3. Traditionally web browsers only have a
    single thread (is it?).

    View full-size slide

  4. Code
    JS Main Thread

    View full-size slide

  5. Doesn’t Block the thread
    Blocks the thread

    View full-size slide

  6. The code we write
    Web APIs Core JS

    View full-size slide

  7. The code we write
    Web APIs Core JS
    Blocking + Non blocking Blocking

    View full-size slide

  8. • Language syntax
    • Built-in objects and functions
    • Global objects
    • Prototype based inheritance
    system
    • Error handling mechanisms
    • document
    • requestIdleCallback
    • requestAnimationFrame
    • setTimeout
    • setInterval
    • Promises
    • And many more
    Web APIs Core JavaScript

    View full-size slide

  9. Browser’s thread
    JS Main thread
    Non blocking web APIs
    Blocking JS

    View full-size slide

  10. Piano by Adrien Coquet from the Noun Project

    View full-size slide

  11. • Loading MIDI files
    • Playing music
    • Visualization

    View full-size slide

  12. Loading MIDI files

    View full-size slide

  13. import { parse } from “midiconvert";
    const file = new FileReader();
    file.onload = () !=> {
    !// heavy computation
    const parsedMidi = parse(file.result);
    };

    View full-size slide

  14. import { parse } from “midiconvert";
    const file = new FileReader();
    file.onload = () !=> {
    !// heavy computation
    const parsedMidi = parse(file.result);
    };
    File loading
    +
    Parsing

    View full-size slide

  15. Non blocking web APIs
    Blocking JS
    Can we achieve concurrency for them ?

    View full-size slide

  16. Web Workers is a simple means for web
    content to run scripts in background
    threads.

    View full-size slide

  17. Non blocking web APIs
    Blocking JS
    Worker Thread

    View full-size slide

  18. Non blocking web APIs
    Blocking JS
    Worker Thread
    Browser thread
    JS Main Thread

    View full-size slide

  19. postMessage API

    View full-size slide

  20. We can create multiple web workers.

    View full-size slide

  21. const myWorker = new Worker('worker.js');

    View full-size slide

  22. !// main.js
    const myWorker = new Worker('worker.js');
    myWorker.postMessage("sending data to worker");
    myWorker.onmessage = function(e) {
    console.log('Message received from worker');
    }
    Spawn a worker

    View full-size slide

  23. !// main.js
    const myWorker = new Worker('worker.js');
    myWorker.postMessage("sending data to worker");
    myWorker.onmessage = function(e) {
    console.log('Message received from worker');
    }
    Send message to worker

    View full-size slide

  24. !// worker.js
    onmessage = function(e) {
    console.log('Message received from main script');
    var workerResult = 'Result: ' + (e.data);
    console.log('Posting message back to main script');
    postMessage(workerResult);
    }
    Receive message in worker

    View full-size slide

  25. !// worker.js
    onmessage = function(e) {
    console.log('Message received from main script');
    var workerResult = 'Result: ' + (e.data);
    console.log('Posting message back to main script');
    postMessage(workerResult);
    }
    Send message to the main script

    View full-size slide

  26. Receive message from worker
    !// main.js
    const myWorker = new Worker('worker.js');
    myWorker.postMessage("sending data to worker");
    myWorker.onmessage = function(e) {
    console.log('Message received from worker');
    }

    View full-size slide

  27. • Loading files
    • Playing music
    • Visualization

    View full-size slide

  28. !// This is possible due to the usage of worker-loader in webpack.
    !// You can't do this without that. Only importScripts works.
    import { parse } from “midiconvert";
    const file = new FileReader();
    file.onload = () !=> {
    !// heavy computation
    const parsedMidi = parse(file.result);
    self.postMessage({
    data: parsedMidi
    });
    };
    self.onmessage = e !=> {
    file.readAsArrayBuffer(e.data);
    };

    View full-size slide

  29. Playing Music = Scheduling Notes + Playing those notes

    View full-size slide

  30. Playing Music = Scheduling Notes + Playing those notes
    WebAudio API.
    Audio plays in
    different thread by
    default.

    View full-size slide

  31. Challenges in scheduling notes.
    • Minimum time precision in JS clock is of 1ms.
    • The JS timers (setTimeout, setInterval) call can be
    delayed if the thread is blocked.

    View full-size slide

  32. • Minimum time precision in JS clock is of 1ms. - a
    combination of setTimeout and audio hardware scheduling.
    • The JS timers (setTimeout, setInterval) call can be
    delayed if the thread is blocked. (workers)
    Challenges in scheduling notes.

    View full-size slide

  33. Playing Music = Scheduling Notes + Playing those notes
    WebAudio API.
    Audio plays in
    different thread by
    default.
    WebAudio API’s time
    +
    setTimeOut
    +
    Web Workers

    View full-size slide

  34. Visualization Sync

    View full-size slide

  35. Why separate timer ?

    View full-size slide

  36. Canvas is scriptable.
    So ?

    View full-size slide

  37. Main thread
    const canvasWorker = new Worker("./canvas-worker");
    const offscreenCanvas = document
    .getElementById(“canvas”)
    .transferControlToOffscreen();
    canvasWorker.postMessage(offscreenCanvas, [offscreenCanvas]);

    View full-size slide

  38. Main thread
    What’s this ?
    const canvasWorker = new Worker("./canvas-worker");
    const offscreenCanvas = document
    .getElementById(“canvas”)
    .transferControlToOffscreen();
    canvasWorker.postMessage(offscreenCanvas, [offscreenCanvas]);

    View full-size slide

  39. postMessage API

    View full-size slide

  40. Serialization
    Deserialization
    postMessage

    View full-size slide

  41. Serialization
    Deserialization
    postMessage
    SLOW

    View full-size slide

  42. Tranferables Objects

    View full-size slide

  43. Transferring Memory - ArrayBuffers

    View full-size slide

  44. Transferring Memory - ArrayBuffers
    No more accessible accessible

    View full-size slide

  45. Shared Array Buffers

    View full-size slide

  46. Usage
    worker.postMessage(message, [transferableList]);

    View full-size slide

  47. http://html5-demos.appspot.com/static/workers/transferables/index.html
    Benchmark
    32 MB Data
    Serialization - 302ms
    Transferable Objects - 6.6ms

    View full-size slide

  48. Main thread
    The main thread has tranferred this “transferable” to
    worker and has no control over it anymore.
    const canvasWorker = new Worker("./canvas-worker");
    const offscreenCanvas = document
    .getElementById(“canvas”)
    .transferControlToOffscreen();
    canvasWorker.postMessage(offscreenCanvas, [offscreenCanvas]);

    View full-size slide

  49. In worker thread, we get the canvas.
    import { Visualizer } from "@utils/Visualizer";
    self.onmessage = e !=> {
    const canvas = e.data;
    const visualizer = new Visualizer(canvas);
    };

    View full-size slide

  50. Write Mode
    • Play using 128 sounds.
    • Record.
    • Save MIDI.
    • Connect a keyboard/Piano.

    View full-size slide

  51. Control the keyboard from the
    app or vice versa.

    View full-size slide

  52. Thank You
    @ritz078

    View full-size slide