$30 off During Our Annual Pro Sale. View Details »

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 Slide

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

    View Slide

  3. View Slide

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

    View Slide

  5. Code
    JS Main Thread

    View Slide

  6. Doesn’t Block the thread
    Blocks the thread

    View Slide

  7. The code we write
    Web APIs Core JS

    View Slide

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

    View Slide

  9. • 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 Slide

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

    View Slide

  11. Piano by Adrien Coquet from the Noun Project

    View Slide

  12. View Slide

  13. • Loading MIDI files
    • Playing music
    • Visualization

    View Slide

  14. Loading MIDI files

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  18. Web Workers

    View Slide

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

    View Slide

  20. Non blocking web APIs
    Blocking JS
    Worker Thread

    View Slide

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

    View Slide

  22. postMessage API

    View Slide

  23. We can create multiple web workers.

    View Slide

  24. Usage

    View Slide

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

    View Slide

  26. !// 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 Slide

  27. !// 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 Slide

  28. !// 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 Slide

  29. !// 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 Slide

  30. 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 Slide

  31. • Loading files
    • Playing music
    • Visualization

    View Slide

  32. File upload

    View Slide

  33. !// 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 Slide

  34. Playing Music = Scheduling Notes + Playing those notes

    View Slide

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

    View Slide

  36. 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 Slide

  37. • 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 Slide

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

    View Slide

  39. Visualization Sync

    View Slide

  40. View Slide

  41. Canvas

    View Slide

  42. Why separate timer ?

    View Slide

  43. Canvas is scriptable.
    So ?

    View Slide

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

    View Slide

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

    View Slide

  46. postMessage API

    View Slide

  47. Serialization
    Deserialization
    postMessage

    View Slide

  48. Serialization
    Deserialization
    postMessage
    SLOW

    View Slide

  49. Tranferables Objects

    View Slide

  50. Transferring Memory - ArrayBuffers

    View Slide

  51. Transferring Memory - ArrayBuffers
    No more accessible accessible

    View Slide

  52. Shared Array Buffers

    View Slide

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

    View Slide

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

    View Slide

  55. 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 Slide

  56. 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 Slide

  57. More ?

    View Slide

  58. Write Mode

    View Slide

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

    View Slide

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

    View Slide

  61. Thank You
    @ritz078

    View Slide