What ImageData expects is an Uint8ClampedArray which is a TypedArray, but it sounds that what your worker sends is an Array.
So you will probably get faster results by working directly in you worker from such a TypedArray. Beside possibly making the worker part a bit faster, it will also allow you to transfer the underlying ArrayBuffer from the worker thread to the main thread, without having to copy the data, never.
Once the ArrayBuffer has been received on the main thread, you will just have to create an UInt8ClampedArray view from it, and create an ImageData wrapper from this TypedArray.
At this point, all the main thread would have done is to create a view on the buffer (minimal memory waste) and an ImageData wrapper over this view (once again, a simple object creation). It won't have created nor even read the data your worker produced. This will be done by putImageData.
worker.js
data = new UInt32Array(width * height); //can even be an other type of TypedArray
... // fill 'data'
self.postMessage({
data: data,
width: width,
height: height
},
[data.buffer] // tranfer the ArrayBuffer
);
main.js
worker.onmessage = e => {
const msg = e.data;
const view = new Uint8ClampedArray(msg.data.buffer);
const imageData = new ImageData(view, msg.width, msg.height);
ctx.putImageData(imageData, 0,0);
};
const ctx = c.getContext('2d');
const worker = new Worker(generateWorkerURL());
worker.onmessage = e => {
const msg = e.data;
const view = new Uint8ClampedArray(msg.data.buffer);
const imageData = new ImageData(view, msg.width, msg.height);
ctx.putImageData(imageData, 0,0);
};
worker.postMessage('go');
// stacksnippet only
function generateWorkerURL() {
const workerScript = document.querySelector('script[type="worker-script"]');
const blob = new Blob([workerScript.textContent], {type: 'application/javascript'});
return URL.createObjectURL(blob);
}
<script type="worker-script">
onmessage = e => {
const canvasWidth = 500,
canvasHeight = 500,
// can be any TypedArray
data = new Uint32Array(canvasWidth * canvasHeight);
// fill 'data'
// taken from https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/
for (let y = 0; y < canvasHeight; ++y) {
for (let x = 0; x < canvasWidth; ++x) {
let value = x * y & 0xff;
data[y * canvasWidth + x] =
(255 << 24) | // alpha
(value << 16) | // blue
(value << 8) | // green
value; // red
}
}
self.postMessage({
data: data,
width: canvasWidth,
height: canvasHeight
},
[data.buffer] // tranfer the ArrayBuffer
);
console.log('transfered', !data.length);
}
</script>
<canvas id=c width=500 height=500></canvas>
Also note that it's not clear as to why you want to go through an ImageData, when you could simply use drawImage, either from an HTMLImageElement directly, or even from an ImageBitmap if you really want to work from workers.