It\'s possible to send a CanvasPixelArray
obtained via getImageData
to a worker script, and let the worker script manipulate the pixels in its back
[Edit ~5 years later: some of this is beginning to change, and there are new web platform features that actually allow rendering to a canvas from a Worker! See this blog for more info: https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/ - the rest of the answer is provided for 2011-era information ;)]
Web workers can only calculate, not modify the DOM or make any calls to draw to a canvas. However like you say you can post an array of pixels to a web worker to process it there and post it back. Since that's asynchronous I don't see why that would cause any slowdown in the UI thread, unless you deliberately block until the web worker responds (which you shouldn't).
So it seems odd that your drawImage
calls are taking so long it's affecting the UI. Most canvases are hardware-accelerated these days so they should skip along nicely. My guess is that you're drawing via a web worker to a canvas pixel array every frame, which effectively means you're software rendering the canvas in javascript. Javascript is still far too slow to do this - even C++ software renderers are kind of slow, which is why hardware acceleration is important. So you can render something to a canvas pixel array in a web worker once, and then when you get your result cache it in to an Image
once, and then draw that Image
to the canvas as much as you like. That should still be really fast.
Edit: you might want to look in to WebGL, where you can write fragment shaders which are effectively little programs to process pixel effects. They run entirely on the graphics card so they're stupidly fast.
You can post the ImageData
to the Web Worker, which sends the manipulated ImageData
back to the caller (Main UI) thread.
E.g.:
Create a Web Worker:
this.renderer = new Worker("renderer.js");
Post the ImageData
created from the canvas to the Web Worker:
var ctx = this.canvas.getContext('2d'); var imageData = ctx.createImageData(width, height); this.renderer.postMessage({ image: imageData });
Do ImageData
manipulation in Web Worker and post it back to Main thread:
onmessage = function(e) { var processedImage = self.doImageProcessing(e.data.image); postMessage({ image: processedImage }); };
Set the manipulated ImageData
to the canvas in the Main thread:
this.renderer.onmessage = function (e) { var ctx = this.canvas.getContext('2d'); ctx.putImageData(e.data.image, 0, 0); }
[community edit: This answer was written and accepted in 2011. Other technologies have emerged (or are emerging) which may allow Web Workers and Canvas to co-exist better; the reader should be aware of all the answers on this page besides this answer.]
You cannot pass a canvas object or canvas context to a worker thread because the canvas is part of the DOM.
There's a new API to do this (currently only supported in Firefox if you enable a pref).
See https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas and https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/.