diff --git a/SortingVisualizer/Tushar2930/README.md b/SortingVisualizer/Tushar2930/README.md
new file mode 100644
index 000000000..0e15addc1
--- /dev/null
+++ b/SortingVisualizer/Tushar2930/README.md
@@ -0,0 +1,6 @@
+# Sorting Visualizer
+
+This is a project that visualizes the sorting of a given array with multiple algorithm options.
+I started this project because I was always fascinated that the concept of organization could be so easily captured within algorithms, as well as the fact that some core mathematical principles can make some incredibly-fast algorithms.
+
+
diff --git a/SortingVisualizer/Tushar2930/index.html b/SortingVisualizer/Tushar2930/index.html
new file mode 100644
index 000000000..871c232dd
--- /dev/null
+++ b/SortingVisualizer/Tushar2930/index.html
@@ -0,0 +1,86 @@
+
+
+
+
+ Sorting Visualizer
+
+
+
+
+
+
+
+
+
+
+
+
+
Sorting Algorithm Visualizer!
+
+
+
+
+
Current Array Size:
+
+
+
+
+
+ Current Delay (lower is faster): ms
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SortingVisualizer/Tushar2930/src/main.js b/SortingVisualizer/Tushar2930/src/main.js
new file mode 100644
index 000000000..5f1257be7
--- /dev/null
+++ b/SortingVisualizer/Tushar2930/src/main.js
@@ -0,0 +1,169 @@
+
+function clearCanvas() {
+ ctx.beginPath();
+ ctx.fillStyle = BACKGROUND_COLOR;
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ ctx.closePath();
+}
+
+function resetArray() {
+ array = [];
+}
+
+async function sleep() {
+ return new Promise((temp) => {
+ setTimeout(() => temp(2), SPEED); // timeout this function for duration SPEED
+ });
+}
+
+function swap(i, j) {
+ let temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+}
+
+/**
+ *
+ * @param {index in array to draw} index
+ * @param {The value of the index in array} value
+ * @param {The new color to draw the index} color
+ */
+function drawBar(index, value, color = DEFAULT_COLOR) {
+ ctx.beginPath();
+ // clear the previous bar
+ ctx.fillStyle = BACKGROUND_COLOR;
+ ctx.fillRect(
+ Math.ceil((WIDTH / ARR_SIZE) * index),
+ 0,
+ Math.floor(WIDTH / ARR_SIZE),
+ HEIGHT
+ );
+
+ ctx.fillStyle = color;
+ ctx.fillRect(
+ Math.ceil((WIDTH / ARR_SIZE) * index),
+ HEIGHT,
+ Math.floor(WIDTH / ARR_SIZE),
+ Math.floor(-HEIGHT * (value / MAX_ARR_VAL))
+ );
+
+ ctx.closePath();
+}
+
+function drawArray() {
+ clearCanvas();
+
+ for (let i = 0; i < array.length; i++) {
+ let val = array[i];
+ drawBar(i, val, DEFAULT_COLOR);
+ }
+}
+
+function randomizeArray() {
+ if (running) {
+ running = false;
+ }
+ clearCanvas();
+ resetArray();
+
+ for (let i = 0; i < ARR_SIZE; i++) {
+ array.push(Math.floor(Math.random() * MAX_ARR_VAL));
+ }
+
+ drawArray();
+}
+
+function runSort() {
+
+ if (!running) {
+ running = true;
+
+ let sortingAlgorithm;
+ const algoName = sortingAlgo.value;
+ switch (algoName) {
+ case "bubble":
+ sortingAlgorithm = bubbleSort;
+ break;
+ case "selection":
+ sortingAlgorithm = selectionSort;
+ break;
+ case "insertion":
+ sortingAlgorithm = insertionSort;
+ break;
+ case "merge":
+ sortingAlgorithm = mergeSort;
+ break;
+ case "quick": // TODO
+ sortingAlgorithm = quickSort;
+ break;
+ case "heap":
+ sortingAlgorithm = heapSort;
+ break;
+ default:
+ console.log("Invalid input");
+ }
+
+ sortingAlgorithm(array).then((sorted) => {
+ if (running) {
+ array = sorted;
+ drawArray();
+ running = false;
+ }
+ });
+
+ } else {
+ console.log("Cannot restart: sorting is actively running!");
+ }
+}
+
+function speedInput() {
+ SPEED = speedSlider.value;
+ speedOutput.innerHTML = SPEED;
+}
+function sizeInput() {
+ ARR_SIZE = sizeSlider.value;
+ sizeOutput.innerHTML = ARR_SIZE;
+}
+
+var canvas = document.getElementById("myCanvas");
+var ctx = canvas.getContext("2d");
+const WIDTH = canvas.width,
+ HEIGHT = canvas.height;
+
+// button, slider setup
+randomizeBtn = document.getElementById("randomizeBtn");
+sortingBtn = document.getElementById("sortButton");
+
+// array size
+var sizeSlider = document.getElementById("sizeSlider");
+var sizeOutput = document.getElementById("sizeValue");
+var ARR_SIZE;
+// getting latency
+var speedSlider = document.getElementById("speedSlider");
+var speedOutput = document.getElementById("speedValue"); // get the output from the slider span
+var SPEED;
+// getting sorting algorithm
+var sortingAlgo = document.getElementById("sortingFunction");
+
+// everytime we mess with slider, it updates values inside
+speedSlider.oninput = speedInput;
+sizeSlider.oninput = () => {
+ sizeInput();
+
+ randomizeArray();
+};
+
+
+// initializing stuff I use everywhere
+let array = [];
+const MAX_ARR_VAL = 100;
+let running = false;
+
+// COLORS
+const DEFAULT_COLOR = "black";
+const BACKGROUND_COLOR = "white";
+
+// first call
+speedInput();
+sizeInput();
+randomizeArray(); // randomize and draw the array
diff --git a/SortingVisualizer/Tushar2930/src/sort.js b/SortingVisualizer/Tushar2930/src/sort.js
new file mode 100644
index 000000000..f513360a7
--- /dev/null
+++ b/SortingVisualizer/Tushar2930/src/sort.js
@@ -0,0 +1,248 @@
+const isSorted = (array) => {
+ for (let i = 1; i < array.length; i++) {
+ if (array[i - 1] > array[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+const isMaxHeap = (array) => {
+ for (let i = 1; i < array.length; i++) {
+ if (array[Math.floor((i - 1) / 2)] < array[i]) {
+ return false;
+ }
+ }
+ return true;
+};
+
+async function bubbleSort(arr) {
+ let compare = true;
+ while (compare === true && running) {
+ compare = false;
+ for (let i = 0; i < arr.length - 1 && running; i++) {
+ // swap
+ if (arr[i] > arr[i + 1]) {
+ compare = true;
+ // swap
+ swap(i, i + 1);
+ drawBar(i, array[i], "red");
+ drawBar(i + 1, array[i + 1], "red");
+ await sleep();
+ }
+ drawArray();
+ }
+ }
+
+ return arr;
+}
+
+async function selectionSort(arr) {
+ // algorithm works by selecting the minimum element in i...n and swapping
+ for (let i = 0; i < arr.length && running; i++) {
+ let minDex = i;
+ for (let j = i + 1; j < arr.length && running; j++) {
+ drawArray();
+ drawBar(minDex, array[minDex], "red");
+ if (arr[minDex] > arr[j]) {
+ // update smallest element
+ minDex = j;
+
+ drawBar(minDex, array[minDex], "red");
+ // await sleep();
+ }
+ await sleep();
+ }
+
+ // swap i with minDex
+ drawArray();
+ drawBar(i, array[i], "red");
+ drawBar(minDex, array[minDex], "red");
+ swap(i, minDex);
+ await sleep();
+ drawArray();
+ }
+
+ return arr;
+}
+
+async function insertionSort(arr) {
+ for (let i = 1; i < arr.length && running; i++) {
+ // start at first index
+ let j = i;
+ while (running &&
+ j > 0 && arr[j - 1] > arr[j]
+ ) {
+ drawArray();
+ drawBar(j - 1, arr[j - 1], "red");
+ drawBar(j, arr[j], "red");
+ swap(j - 1, j);
+ await sleep();
+
+ j--;
+ }
+ }
+
+ return arr;
+}
+
+/**
+ *
+ * @param {Array to construct heap from, then sort} arr
+ */
+async function heapSort(arr) {
+ const getLeftIndex = (index) => {
+ return index * 2 + 1 < arr.length ? index * 2 + 1 : -1;
+ };
+
+ const getRightIndex = (index) => {
+ return index * 2 + 2 < arr.length ? index * 2 + 2 : -1;
+ };
+
+ const getParentIndex = (index) => {
+ return index != 0 ? Math.floor((index - 1) / 2) : -1;
+ };
+
+ // construct maxheap: swim up for each index
+ for (let index = 1; index < arr.length && running; index++) {
+ let parentIndex = getParentIndex(index);
+ // swim up until we find place in Max-Heap
+ while (running &&
+ parentIndex != -1 && arr[parentIndex] < arr[index]
+ ) {
+ // do the swap and update the value
+ // console.log(parentIndex, index);
+
+ drawArray();
+ drawBar(index, arr[index], "red");
+ drawBar(parentIndex, arr[parentIndex], "red");
+
+ swap(parentIndex, index);
+ await sleep();
+ drawArray();
+
+ // info for next iteration
+ index = parentIndex;
+ parentIndex = getParentIndex(index);
+ }
+ }
+
+ // removeMax and re-establish heap
+ for (let i = arr.length - 1; running && i > 0; i--) {
+ // swap maxNode with end of the heap
+ drawArray();
+ drawBar(0, arr[0], "red");
+ drawBar(i, arr[i], "red");
+
+ swap(0, i);
+ await sleep();
+ drawArray();
+
+ // utility functions to get left and right children
+ const getLeftChild = (index) => {
+ return index * 2 + 1 < i ? arr[index * 2 + 1] : -1;
+ };
+ const getRightChild = (index) => {
+ return index * 2 + 2 < i ? arr[index * 2 + 2] : -1;
+ };
+
+ // sink down the node until in the correct position
+ let index = 0;
+ while (running &&
+ getLeftIndex(index) < i && // heap has at least 1 child node (left firs always)
+ (arr[index] < getLeftChild(index, i) ||
+ arr[index] < getRightChild(index, i))
+ ) {
+ // swap index with larger of two children
+ let newIndex =
+ getLeftChild(index, i) > getRightChild(index, i)
+ ? getLeftIndex(index)
+ : getRightIndex(index);
+
+ drawArray();
+ drawBar(index, arr[index], "red");
+ drawBar(newIndex, arr[newIndex], "red");
+
+ swap(index, newIndex);
+ await sleep();
+ drawArray();
+
+ index = newIndex;
+ }
+ }
+
+ return arr;
+}
+
+async function mergeSort(arr) {
+ array = arr;
+ drawArray(arr);
+ let sorted_arr = await recursiveMergeSort(arr, 0);
+ return sorted_arr;
+}
+
+/**
+ *
+ * @param {* Our array to sort *} arr
+ * @param {* Our shift from the original array (helps with sorting later on)} shift
+ * @returns
+ */
+async function recursiveMergeSort(arr, shift) {
+ if (arr.length <= 1) {
+ return arr;
+ }
+
+ const half = Math.ceil(arr.length / 2);
+ let [left, right] = await Promise.all([
+ recursiveMergeSort(arr.slice(0, half), 0),
+ recursiveMergeSort(arr.slice(half), half),
+ ]);
+
+ let [leftIndex, rightIndex] = [0, 0];
+
+ // ALGO: construct new array from min btwn left and right at each index
+ for (let i = 0; i < arr.length && running; i++) {
+ // update the recursive array representation, and get the index we'll swap
+ if (leftIndex == left.length || right[rightIndex] < left[leftIndex]) {
+ // index will be left + rightIndex of overall array
+ arr[i] = right[rightIndex];
+ rightIndex++;
+ } else if (
+ rightIndex == right.length ||
+ left[leftIndex] <= right[rightIndex]
+ ) {
+ arr[i] = left[leftIndex];
+ leftIndex++;
+ }
+
+ // internal array indices
+ const shiftI = shift + i;
+
+ // we have to directly overwrite the array in order for it to show
+ await sleep();
+ array[shiftI] = arr[i];
+ drawBar(shiftI, arr[i], "red");
+ await sleep();
+ drawArray();
+ }
+
+ return arr;
+}
+
+function test() {
+ rand_arr = [];
+ let s = 10;
+ let maxVal = 100;
+ // build random array
+ for (let i = 0; i < s; i++) {
+ rand_arr.push(Math.floor(Math.random() * maxVal));
+ }
+
+ // test sorting algo
+ mergeSort(rand_arr).then((result) => {
+ console.log(result);
+ console.log(isSorted(result));
+ });
+}
+
+// test();
diff --git a/SortingVisualizer/Tushar2930/styles.css b/SortingVisualizer/Tushar2930/styles.css
new file mode 100644
index 000000000..390c5c6ff
--- /dev/null
+++ b/SortingVisualizer/Tushar2930/styles.css
@@ -0,0 +1,79 @@
+html, body {
+ margin: 5px;
+ padding-bottom: 2em;
+ background: #7cc6fe;
+ font-family: sans-serif;
+}
+
+.intro {
+ text-align: center;
+ margin-left: 10vw;
+ margin-right: 10vw;
+ padding-top: 5vh;
+}
+.intro h1 {
+ font-size: 2.75em;
+ color: azure;
+}
+.intro h3 {
+ color: azure;
+}
+
+.slider-container {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+.slider {
+ text-align: center;
+ padding-right: 2em;
+ padding-left: 2em;
+}
+.slider h3 {
+ color: azure;
+}
+
+
+.algo-selection-container {
+ padding: 10px;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+.algo-selection-container label {
+ font-size: large;
+ color: azure;
+}
+
+.button {
+ background-color: #4CAF50;
+ border: none;
+ color: white;
+ padding: 15px 32px;
+ text-align: center;
+
+ display: inline-block;
+
+ font-size: 16px;
+ margin: 1em;
+ transition: all 200ms ease;
+}
+.button:hover {
+ transform: scale(1.15);
+ background: #e2bd45;
+ border-color: rgb(252, 0, 0);
+ border-width: 0.1rem;
+}
+.sorting-button-container {
+ padding: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+
+
+.canvas-container {
+ text-align: center;
+}
\ No newline at end of file