Im coding an r/place clone right now and I'm having issues with sending the stored pixel data from the server to the connecting clients. While it works, it takes a bit too much time depending on the canvas size.
While for a 1000x1000 pixel sized canvas, sending the whole canvas to the client takes only 0.8 seconds which is fine, but for a 3000x3000 canvas it takes up to 6 seconds which is a bit too long.
Right now im using socket.io for the connection part. I store the pixel data serversided inside an array and send it to the client like this:
let canvasData = [];
//more code where i retrieve the canvasData from a txt file
io.on('connection', (socket) => {
socket.emit('initialCanvasData', canvasData); //this part here takes a long time
});
Then on the client side I read it like this:
function drawInitialCanvas(canvasData) {
const imageData = new ImageData(newCanvasWidth, newCanvasHeight);
const data = new Uint8ClampedArray(imageData.data.buffer);
let dataIndex = 0;
for (let y = 0; y < newCanvasHeight; y++) {
for (let x = 0; x < newCanvasWidth; x++) {
const colorObj = canvasData[y][x];
const color = colorObj.color;
const rgbValues = color.match(/\d+/g);
data[dataIndex++] = parseInt(rgbValues[0]);
data[dataIndex++] = parseInt(rgbValues[1]);
data[dataIndex++] = parseInt(rgbValues[2]);
data[dataIndex++] = 255; // Alpha (255 means fully opaque)
}
}
newContext.putImageData(imageData, 0, 0); // Draw pixel data onto new canvas context
context.drawImage(newCanvas, newCanvasX, newCanvasY);
}
The Array looks like this which can get very long depending on the canvas size:
[[{"color":"rgb(255,255,255)","user":null},{"color":"rgb(125,255,255)","user":null},{"color":"rgb(255,125,255)","user":null},{"color":"rgb(255,255,80)","user":null},...{"color":"rgb(125,125,255)","user":null}]]]
I basically store the RGB value, and the name of the user who painted the pixel for each pixel.
Now I wonder if there is another more efficient method, so it won't take that much time to load the canvas.
I already tried compressing the array as a string serversided, sending it to the client and then decompress it which speeds up the sending process from 4 seconds down to 0.4 seconds, but the compression and decompression together takes up to 8 seconds which is even longer than if I would send the whole array uncompressed.
Maybe someone has another idea
First off, you keep sending the user: null with each pixel, and that should take shave off a good chunk of the time needed.
Also, you could remove some redundant data like "rgb" for each entry.
You could send an object like:
{ "user": null, "pixels": ["255,255,255", "125,255,255", ... ] }
Also, you could base64 encode the string on the server and decode on the client.
This is all assuming that your timing issue is due to network latency.
Also, regular expressions are notoriously slow, and not having to run a regex for each pixel should also help speed things up.
Hope this helps