title: Webworker canvas performance terrible
body: <p>I'm trying to use webworkers to render parts of the frames for an animated mandelbrot zoomer, since there is a lot of calculating involved, and since this can be easily split up in blocks this should be an ideal situation for parallel processing.</p>
  
  <p>But no matter what I try I do not get any performance in return for the extra cpu the workers use. Compared to a non worker version, in Chrome my benchmark is somewhat slower, in Firefox it is much slower.</p>
  
  <p>My guess is that transferring the image data to the webworkers is incredibly expensive, I tried just receiving raw data and using that to render frames but the result is much the same. I don't think this is the ideal way to send and receive imagedata to the workers (in fact I only need to receive it, but I have not been able to create a buffer inside the workers that can be used for the canvas directly). So at it stands sending any serious amount of data creates a real bottleneck. </p>
  
  <p>Dear stackoverflow, please help me answer these two questions: What am I doing wrong here, and what can be improved?</p>
  
  <p>A demo can be found <a href="http://mandelbrot.eu01.aws.af.cm/worker/" rel="nofollow">here</a> for workers, and for reference a non worker version on <a href="http://jsfiddle.net/3YeMC/" rel="nofollow">jsfiddle</a>.</p>
  
  <p>Code is as follows:</p>
  
  <pre><code>"use strict";
  
  /*global $*/
  
  $(function() {
  
      var mandelbrot = new Mandelbrot();
  
  });
  
  var Mandelbrot = function() {
  
      // set some values
      this.width = 500;
      this.height = 500;
  
      this.x_center = -1.407566731001088;
      this.y_center = 2.741525895538953e-10;
  
      this.iterations = 250;
      this.escape = 4,
      this.zoom = 10;
      this.count = 0;
      this.worker_size = 10;
      this.received = 0;
      this.refresh = true;
  
      //let's go - create canvas, image data and workers
      this.init();
      //start animation loop
      this.animate();
  
  };
  
  Mandelbrot.prototype = {
  
      init: function() {
  
          var self = this;
  
          //create main canvas and append it to div
          var container = $("#content");
  
          this.canvas = document.createElement("canvas");
          this.canvas.width = this.width;
          this.canvas.height = this.height;
  
          container.append(this.canvas);
  
          //create imagedata
          this.context = this.canvas.getContext("2d");
          this.image = this.context.getImageData(0, 0, this.width, this.height);
          this.data = new Int32Array(this.image.data.buffer);
  
          //create imagedata for webworkers
          this.worker_data = this.context.getImageData(0, 0, this.width, this.height / this.worker_size);
  
          //create webworkers drop them in array
          this.pool = [];
  
          for (var i = 0; i &lt; this.worker_size; i++) {
  
              this.pool[i] = new Worker("js/worker.js");
              this.pool[i].idle = true;
              this.pool[i].id = i;
  
              //on webworker finished 
              this.pool[i].onmessage = function(e) {
  
                  self.context.putImageData(e.data, 0, self.height / self.worker_size * e.target.id);
                  self.received++;
  
              };
  
          }
      },
  
      iterate: function() {
  
          for (var i = 0; i &lt; this.pool.length; i++) {
  
              this.pool[i].postMessage({
  
                  image: this.worker_data,
                  id: this.pool[i].id,
                  worker_size: this.worker_size,
                  width: this.width,
                  height: this.height,
                  x_center: this.x_center,
                  y_center: this.y_center,
                  iterations: this.iterations,
                  escape: this.escape,
                  zoom: this.zoom
  
              });
          }
      },
  
      animate: function() {
  
          requestAnimationFrame(this.animate.bind(this));
  
          //poor man's benchmark over 250 frames
          if (this.count === 0) {
              console.time("timer");
          } 
  
          if (this.count === 250) {
              console.timeEnd("timer");
          }
  
          //refresh at init, then refresh when all webworkers are done and reset
          if (this.received === this.worker_size | this.refresh) {
  
              this.received = 0;
              this.refresh = false;
              this.count++;
              this.zoom *= 0.95;
              this.iterate();
  
          }
      }
  };
  </code></pre>
  
  <p>and worker.js:</p>
  
  <pre><code>self.onmessage = function(e) {
  
      "use strict";
  
      var x_step = e.data.zoom / e.data.width;
      var y_step = e.data.zoom / e.data.height;
  
      var y_start = e.data.height / e.data.worker_size * e.data.id;
      var y_end = e.data.height / e.data.worker_size;
  
      var data = new Int32Array(e.data.image.data.buffer);
  
      for (var y = 0; y &lt; y_end; y++) {
  
          var iy = e.data.y_center - e.data.zoom / 2 + (y + y_start) * y_step;
  
          for (var x = 0; x &lt; e.data.width; x++) {
  
              var rx = e.data.x_center - e.data.zoom / 2 + x * x_step;
  
              var zx = rx;
              var zy = iy;
              var zx2 = 0;
              var zy2 = 0;
  
              for (var i = 0; zx2 + zy2 &lt; e.data.escape &amp;&amp; i &lt; e.data.iterations; ++i) {
  
                  zx2 = zx * zx;
                  zy2 = zy * zy;
                  zy = (zx + zx) * zy + iy;
                  zx = zx2 - zy2 + rx;
              }
  
              data[y * e.data.width + x] = (255 &lt;&lt; 24) | (i &lt;&lt; 16) | (i &lt;&lt; 8) | i;
  
          }
      }
  
      self.postMessage(e.data.image);
  
  };
  </code></pre>