Plotting points representing many events on a canvas
Optimising for performance.

Javascript has a way encouraging people to write code that is terribly inefficient. 

You are lucky the problem is one that can get a lot of benefit from just some minor changes.

Memorize (binning) is not going to help, it will actually significantly decrease performance and massively increase your memory load. The crazy thing is you are already binning all the data in your code.

First the rendering


var paintDotS = function(pointX, pointY, color) {
        context.fillStyle = color;
        context.fillRect(pointX, pointY, 1, 1);
};



You are only plotting single pixels. For javascript to draw a pixel via the fillRect method there are 100's of CPU cycles needed, and all you do is set a single 32bit word.

To render pixels write them directly to a pixel buffer. When all the pixels have been rendered move the pixel buffer to the canvas.

var imageData = ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height);
var dat32 = new Uint32Array(imageData.data.buffer);


The variable dat32 holds each pixel as a 32bit integer. The channel order is from high byte Alpha to low byte Red. Eg to write a yellow you would assign an array item dat32[index] = 0xFF00FFFF;

To write a pixel at a coord x,y

 const w = 300;  // the canvas width in pixels
 dat32[x + y * w] = colour;


Loops and function calls

A big performance killer is the context change required when you call a function. This is compounded if the function being called is indirectly called from another. Many times that calling function does a lot of vetting to ensure nothing goes wrong so more cpu cycles used for no reason.

The arrays iteration methods, forEach, Map, etc are agonisingly slow when compared to standard loops.

your code


var paintDotsAtOnce = function(plotColors) {
    plotColors.forEach(function(plotColor){
        if(plotColor.plotDot) {
           memoized(plotColor.pointX, plotColor.pointY, plotColor.color);                
        }
    });
};



Array.forEach calls a function, that starts, then maintains the iteration, (first context change) it calls your iterator function (second context change), your iterator calls the memoized function (third context change) it makes a memory assignment and then calls your paintDots function (fourth context change).

Remember you are just setting 1 32bit word and you are already 4 levels deep into the call stack, each needs to create closures and context, and the memoize need to allocate memory. You paint the pixel and the whole process need to be undone.

Memory and Objects.

Creating objects is slow, like snail pace slow.    

Your code


for (var i = 0; i &lt; dataAsNumbers.length; i++) {
    x = dataAsNumbers[i][paramX];  // 1st copy x
    y = dataAsNumbers[i][paramY];  // 1st copy y
      // 1st object 
    pointX = getPointOnCanvas({  // 4th copy of x in the function return
        value: x           // 2nd copy of x
        ratio: ratio
    });                    // 3rd copy of x in the function
      // 2nd object 
    pointY = getPointOnCanvas({ // 4th copy of y in the function return
        value: y,          // 2nd copy of y
        ratio: ratio
    });                    // 3rd copy of x in the function
      // 3rd object 
    toPlotColors.push({
        pointX: pointX,    // 5th copy of x
        pointY: pointY     // 5th copy of y          
    });
}
paintDotsAtOnce(toPlotColors);

var getPointOnCanvas = function(params) {

    var value = params.value;
    var ratio = params.ratio;

    return Math.floor(ratio * value);
};



You could not have found a worse way to do this. remember you are writing 1 32Bit pixel, but you create x,y coordinates, then copy x to an object, which you pass to a function in which creates another copy, you return the computed value and store it in yet another copy. Then do it again for y. And once y is done you make yet another copy to be pushed to an array as a reference (even more memory used) (remember that memorize will also be making a copys).

X and y should be immediate, on the heap and then forgotten, no variables need be created, no object constructed, no allocation requests made.

The performance rewrite.

Move one 32Bit word to an array of pixels for each item in dataAsNumbers. 

const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const dat32 = new Uint32Array(imageData.data.buffer);
const w = canvas.width;
const color = 0xFFFFFFFF; // white
for (var i = 0; i &lt; dataAsNumbers.length; i++) {
     dat32[(dataAsNumbers[i][paramX] * ratio | 0) + 
           (dataAsNumbers[i][paramY] * ratio | 0) * w] = color;
}
ctx.putImageData(imageData, 0, 0);


Your code did not show where plotColor and plotDot came from. But they should be handled in the same way as above, without making function calls or creating objects. Everything is immediate and on the heap, and not making  allocation requests.

The beauty is that it is also has binned the data for you as the pixel array. You can make a copy and next time you need that data set just dump the pixel array to the canvas, in less than 1 ms