Frontend Development 20 min read

Implementing Sorting Algorithm Visualizations with JavaScript and Canvas

This article explains how to create animated visualizations of 50 sorting algorithms using JavaScript and the HTML5 canvas by converting array elements into polar coordinates, randomizing their positions, and redrawing the canvas step‑by‑step during the sorting process, complete with full source code examples.

ByteFE
ByteFE
ByteFE
Implementing Sorting Algorithm Visualizations with JavaScript and Canvas

Introduction

The author was inspired by a video showing 50 sorting algorithm visualizations in Java and decided to implement the same effect using JavaScript and the HTML5 canvas, starting with a simple bubble‑sort animation.

Implementation Idea

Desired Effect

The goal is to transform a sorted array into a set of points on a polar coordinate system, then animate the transition from the initial scattered state to the sorted state.

Polar Coordinates Refresher

Recall the definitions: O is the origin, ρ is the radius, and θ is the angle. The conversion formulas are x = ρ * Math.cos(θ) and y = ρ * Math.sin(θ) .

Mapping Array to Polar Points

For an array of 37 elements, each index is multiplied by 10 to obtain an angle (θ) covering 0°‑360°, while the element value becomes the radius (ρ). The resulting (θ, ρ) pairs are converted to (x, y) coordinates for drawing.

const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36];

Using the rule above, each element is mapped to a point such as (0 → θ=0°, ρ=0) , (1 → θ=10°, ρ=1) , …, (36 → θ=360°, ρ=36) .

Random Shuffling

To start the animation, a shuffled array is generated. The example creates four random permutations of the numbers 0‑179, giving a total of 720 elements.

let nums = [];
for (let i = 0; i < 4; i++) {
  const arr = [...Array(180).keys()];
  const res = [];
  while (arr.length) {
    const randomIndex = Math.random() * arr.length - 1;
    res.push(arr.splice(randomIndex, 1)[0]);
  }
  nums = [...nums, ...res];
}

Canvas Setup

A 1000×1000 canvas with a black background is added to the page. The canvas origin is moved to the center (500, 500) using ctx.translate(500, 500) , and the drawing color is set to white.

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'white';
ctx.translate(500, 500);

Drawing Points

Pre‑compute cosine and sine values for all integer angles (0‑359) and store them in an array CosandSin . Then, for each element in the shuffled array, select the appropriate angle (two elements per angle) and compute the Cartesian coordinates.

const CosandSin = [];
for (let i = 0; i < 360; i++) {
  const rad = i / 180 * Math.PI;
  CosandSin.push({ cos: Math.cos(rad), sin: Math.sin(rad) });
}

A simple Rect constructor is used to represent each point as a small rectangle, and a drawAll function renders all rectangles on the canvas.

function Rect(x, y, width, height) {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
}
Rect.prototype.draw = function () {
  ctx.beginPath();
  ctx.fillRect(this.x, this.y, this.width, this.height);
  ctx.closePath();
};

The rendering loop creates a Rect for each element using the pre‑computed cosine/sine values and draws them.

function drawAll(arr) {
  const rects = [];
  for (let i = 0; i < arr.length; i++) {
    const num = arr[i];
    const { cos, sin } = CosandSin[Math.floor(i / 2)];
    const x = num * cos;
    const y = num * sin;
    rects.push(new Rect(x, y, 5, 3));
  }
  rects.forEach(rect => rect.draw());
}

drawAll(nums);

Animating the Sort

During sorting, the canvas is cleared with ctx.clearRect(-500, -500, 1000, 1000) , the array is updated, and drawAll is called after each significant step (e.g., after each outer loop iteration). The drawAll function is wrapped in a Promise to allow await delays.

function drawAll(arr) {
  return new Promise(resolve => {
    setTimeout(() => {
      ctx.clearRect(-500, -500, 1000, 1000);
      // ... (same rendering code as above)
      resolve('draw success');
    }, 10);
  });
}

Sorting Algorithms

Several classic sorting algorithms are implemented, each calling await drawAll(arr) to update the visualization:

Bubble Sort

async function bubbleSort(arr) {
  var len = arr.length;
  for (var i = 0; i < len; i++) {
    for (var j = 0; j < len - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        var temp = arr[j + 1];
        arr[j + 1] = arr[j];
        arr[j] = temp;
      }
    }
    await drawAll(arr);
  }
  return arr;
}

Selection Sort

async function selectionSort(arr) {
  var len = arr.length;
  for (var i = 0; i < len - 1; i++) {
    var minIndex = i;
    for (var j = i + 1; j < len; j++) {
      if (arr[j] < arr[minIndex]) minIndex = j;
    }
    var temp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = temp;
    await drawAll(arr);
  }
  return arr;
}

Insertion Sort

async function insertionSort(arr) {
  for (var i = 1; i < arr.length; i++) {
    var key = arr[i];
    var j = i - 1;
    while (j >= 0 && arr[j] > key) {
      arr[j + 1] = arr[j];
      j--;
    }
    arr[j + 1] = key;
    await drawAll(arr);
  }
  return arr;
}

Heap Sort

async function heapSort(array) {
  // build heap
  var heapSize = array.length;
  for (var i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
    heapify(array, i, heapSize);
    await drawAll(array);
  }
  // sort
  for (var j = heapSize - 1; j >= 1; j--) {
    var temp = array[0];
    array[0] = array[j];
    array[j] = temp;
    heapify(array, 0, --heapSize);
    await drawAll(array);
  }
  return array;
}
function heapify(arr, x, len) {
  var l = 2 * x + 1, r = 2 * x + 2, largest = x, temp;
  if (l < len && arr[l] > arr[largest]) largest = l;
  if (r < len && arr[r] > arr[largest]) largest = r;
  if (largest != x) {
    temp = arr[x];
    arr[x] = arr[largest];
    arr[largest] = temp;
    heapify(arr, largest, len);
  }
}

Quick Sort

async function quickSort(array, left, right) {
  if (left < right) {
    var pivot = array[right];
    var i = left - 1, temp;
    for (var j = left; j <= right; j++) {
      if (array[j] <= pivot) {
        i++;
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
      }
    }
    await drawAll(array);
    await quickSort(array, left, i - 1);
    await quickSort(array, i + 1, right);
    await drawAll(array);
  }
  return array;
}

Radix Sort

async function radixSort(arr, maxDigit) {
  var mod = 10, dev = 1, counter = [];
  for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
    for (var j = 0; j < arr.length; j++) {
      var bucket = parseInt((arr[j] % mod) / dev);
      if (counter[bucket] == null) counter[bucket] = [];
      counter[bucket].push(arr[j]);
    }
    var pos = 0;
    for (var j = 0; j < counter.length; j++) {
      var value = null;
      if (counter[j] != null) {
        while ((value = counter[j].shift()) != null) {
          arr[pos++] = value;
          await drawAll(arr);
        }
      }
    }
  }
  return arr;
}

Shell Sort

async function shellSort(arr) {
  var len = arr.length, gap = 1;
  while (gap < len / 5) gap = gap * 5 + 1;
  for (; gap > 0; gap = Math.floor(gap / 5)) {
    for (var i = gap; i < len; i++) {
      var temp = arr[i];
      var j;
      for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap) {
        arr[j + gap] = arr[j];
      }
      arr[j + gap] = temp;
      await drawAll(arr);
    }
  }
  return arr;
}

Full Source Code

The article provides a complete HTML page containing the canvas element, a start button, and all JavaScript functions shown above, ready to be copied and run.

<canvas id="canvas" width="1000" height="1000" style="background:#000;"></canvas>
<button id="btn">开始排序</button>

// JavaScript code from the sections above

Conclusion

By converting array indices and values into polar coordinates and redrawing the canvas after each sorting step, the article demonstrates a clear method for visualizing the dynamics of many classic sorting algorithms in a web environment.

JavaScriptcanvasVisualizationsorting algorithmsAlgorithm Animation
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.