Merge Sort Visualisation JS // variables
// arr has elements to be sorted
var arr = []
// temp is to store the intermediate results after merging
var temp = []
// seen is for marking visited i.e. sorted half as green
var seen = []
// length of array
var len = 50

// canvas initialisations
var canvas = document.getElementById("myCanvas")
canvas.width = canvas.height = 1000
var canvaswidth = canvas.width
var canvasheight = canvas.height
var ctx = canvas.getContext("2d")

// random array
for (let i = 0; i &lt; len; i++) {
    arr.push(parseInt(Math.random() * 500))
    temp.push(parseInt(0))
    seen.push(parseInt(0))
}

// initial contents of array to be sorted
// console.log(arr)

// draw the bars
draw = (s, e) =&gt; {
    ctx.clearRect(0, 0, 1000, 1000)

    // this loop will make unvisited bars in the upper half as black
    // and visited bars in the upper half as green
    for (let i = 0; i &lt; len; i++) {
        ctx.fillStyle = "#000000"
        ctx.fillRect(15 * i, 500 - arr[i], 10, arr[i])
        if (seen[i]) {
            ctx.fillStyle = "#00ff00"
            ctx.fillRect(15 * i, 500 - arr[i], 10, arr[i])
        }
    }

    // the part that was merged is made blue in the lower half
    // also its equivalent in the uper half is made white
    for (let i = s; i &lt;= e; i++) {
        ctx.fillStyle = "#ffffff"
        ctx.fillRect(15 * i, 500 - arr[i], 10, arr[i])
        ctx.fillStyle = "#0000ff"
        ctx.fillRect(15 * i, 500, 10, arr[i])
        seen[i] = 1
    }

}

// merge
merge = (s, e) =&gt; {

    let m = parseInt((s + e) / 2)
    let p1 = s
    let p2 = m + 1
    let n1 = m
    let n2 = e
    let idx = s

    while (p1 &lt;= n1 &amp;&amp; p2 &lt;= n2) {
        if (arr[p1] &lt;= arr[p2]) {
            temp[idx++] = arr[p1++]
        }
        else {
            temp[idx++] = arr[p2++]
        }
    }

    while (p1 &lt;= n1) {
        temp[idx++] = arr[p1++]
    }

    while (p2 &lt;= n2) {
        temp[idx++] = arr[p2++]
    }

    idx = s
    while (idx &lt;= e) {
        arr[idx] = temp[idx++]
    }

}

// delay
function mytimeout(ms) {
    return new Promise(resolve =&gt; setTimeout(resolve, ms));
}

// mergesort
const mergesort = async (s, e) =&gt; {

    if (s &lt; e) {
        let m = parseInt((s + e) / 2)
        await mergesort(s, m)
        await mergesort(m + 1, e)
        await merge(s, e)
        // await console.log(`merged ${s} to ${e} now draw...`)
        await draw(s, e)
        await mytimeout(500)
    }

}

// calls merge sort and at last 
// makes all bars become green in upper half
const performer = async () =&gt; {
    await mergesort(0, len - 1)
    // await console.log(arr)
    await draw()
}



performer()
&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;

&lt;head&gt;
    &lt;meta charset="UTF-8"&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;
    &lt;canvas id="myCanvas"&gt;
        Your browser does not support the canvas element.
    &lt;/canvas&gt;
    &lt;script src="testmerge.js"&gt;&lt;/script&gt;
&lt;/body&gt;

&lt;/html&gt;



Now if i change the len variable in JS to above 50 i.e. 75 or 100 the code behaves oddly..why is it so?
Are there any optimisations possible??
There is nothing wrong with your code when changing the data size to a larger number, except that the draw() method doesn't adjust the width of the columns by the ratio between the canvas size and the number of columns to be drawn.
You'll have to do some math like:
  let offset = Math.round(canvaswidth / len);
  let rectWidth = Math.round(offset * 0.9);

The rounding is because you want integer values (pixels).


ctx.fillStyle = &quot;#000000&quot;
ctx.fillRect(15 * i, 500 - arr[i], 10, arr[i])
if (seen[i]) {
  ctx.fillStyle = &quot;#00ff00&quot;
  ctx.fillRect(15 * i, 500 - arr[i], 10, arr[i])
}


This seems inefficient as you draw more and more columns twice through the sorting. A better solution would be something like:
ctx.fillStyle = seen[i] ? &quot;#00ff00&quot; : &quot;#000000&quot;;
ctx.fillRect(offset * i, 500 - arr[i], rectWidth, arr[i]);



for (let i = s; i &lt;= e; i++) {
    ctx.fillStyle = &quot;#ffffff&quot;
    ctx.fillRect(15 * i, 500 - arr[i], 10, arr[i])
    ctx.fillStyle = &quot;#0000ff&quot;
    ctx.fillRect(15 * i, 500, 10, arr[i])
    seen[i] = 1
}


You shouldn't &quot;undraw&quot; a column by drawing a white column over it. Instead you could use clearRect with the same dimensions.


let m = parseInt((s + e) / 2)

You can integer divide by two by right shifting by one:
(s + e) &gt;&gt; 1


You should really be careful to end each statement with a ';' (see here)


await merge(s, e)
// await console.log(`merged ${s} to ${e} now draw...`)
await draw(s, e)


You call these with await but they are not defined as async?

Your algorithm seems to works like a merge sort - as I recall it. I think I would encapsulate the parts into a class instead of having the functions in the global scope.

Besides that, I find you graphics very illustrative.