diff --git a/public/coding_challenges/sorting-visualiser/algo.js b/public/coding_challenges/sorting-visualiser/algo.js
index 16a8a5bb4f1bb807e80d1c8ef2df36a59dcdd59b..af7d87e411c2706663638972ddd4326aeba58921 100644
--- a/public/coding_challenges/sorting-visualiser/algo.js
+++ b/public/coding_challenges/sorting-visualiser/algo.js
@@ -3,6 +3,10 @@ class Algorithm {
     constructor(arr) {
         this.elems = arr;
     }
+    sort() {
+        while (!this.getComplete())
+            this.sortIteration();
+    }
 }
 let Algorithms = [];
 //# sourceMappingURL=algo.js.map
\ No newline at end of file
diff --git a/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js
index 671bd0ad4c50ec773254392ba262f57f7b2aa4e6..6453806636e21c0fc03a7ec2b4e051d43eac585c 100644
--- a/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js
+++ b/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js
@@ -4,8 +4,6 @@ class BubbleSort extends Algorithm {
         this.noIters = 0;
         this.position = 0;
     }
-    sort() {
-    }
     sortIteration() {
         let a = this.elems.get(this.position);
         let b = this.elems.get(this.position + 1);
@@ -16,14 +14,15 @@ class BubbleSort extends Algorithm {
         }
         this.position++;
         // console.log(this.position, this.noIters)
-        if (this.position >= (this.elems.length - this.noIters)) {
+        if (this.position >= (this.elems.length - this.noIters - 1)) {
             this.noIters++;
             this.position = 0;
         }
     }
     getComplete() {
-        return this.noIters >= this.elems.length;
+        return this.noIters >= (this.elems.length - 1);
     }
+    setOpt(key, val) { }
 }
 Algorithms.push({
     name: "bubble",
diff --git a/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js
index 4e55b94e568051fd0a38427b1aadbc3341e4d7df..d772045260e0075dc246f352cf598576ed690157 100644
--- a/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js
+++ b/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js
@@ -4,8 +4,6 @@ class CocktailShakerSort extends Algorithm {
         this.noIters = 0;
         this.position = 0;
     }
-    sort() {
-    }
     sortIteration() {
         let a = this.elems.get(this.position);
         let b = this.elems.get(this.position + 1);
@@ -18,7 +16,7 @@ class CocktailShakerSort extends Algorithm {
         else
             this.position--;
         // console.log(this.position, this.noIters)
-        if ((this.position >= (this.elems.length - Math.ceil(this.noIters / 2))) || (this.position < (Math.floor(this.noIters / 2)))) {
+        if ((this.position >= (this.elems.length - Math.ceil(this.noIters / 2) - 1)) || (this.position < (Math.floor(this.noIters / 2)))) {
             this.noIters++;
             if (this.noIters % 2 == 0)
                 this.position++;
@@ -29,6 +27,7 @@ class CocktailShakerSort extends Algorithm {
     getComplete() {
         return this.noIters >= this.elems.length;
     }
+    setOpt(key, val) { }
 }
 Algorithms.push({
     name: "cocktailshaker",
diff --git a/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js b/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js
index a6bb8066dc7a49e78df012a6e3e0a6a066f94483..fac783b82556bea5672436f7ea647ce027b8f618 100644
--- a/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js
+++ b/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js
@@ -3,8 +3,6 @@ class FisherYates extends Algorithm {
         super(...arguments);
         this.shuffle_pos = 0;
     }
-    sort() {
-    }
     sortIteration() {
         if (this.getComplete())
             return;
@@ -17,5 +15,6 @@ class FisherYates extends Algorithm {
             return true;
         return false;
     }
+    setOpt(key, val) { }
 }
 //# sourceMappingURL=fisher-yates.js.map
\ No newline at end of file
diff --git a/public/coding_challenges/sorting-visualiser/algorithms/heap-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/heap-sort.js
new file mode 100644
index 0000000000000000000000000000000000000000..778981c98425a6c1f583e6a657e5f8a47acc3c1d
--- /dev/null
+++ b/public/coding_challenges/sorting-visualiser/algorithms/heap-sort.js
@@ -0,0 +1,119 @@
+var HeapSortState;
+(function (HeapSortState) {
+    HeapSortState[HeapSortState["START"] = 0] = "START";
+    HeapSortState[HeapSortState["HEAPIFY"] = 1] = "HEAPIFY";
+    HeapSortState[HeapSortState["SORT"] = 2] = "SORT";
+})(HeapSortState || (HeapSortState = {}));
+class HeapSort extends Algorithm {
+    constructor() {
+        super(...arguments);
+        this.state = HeapSortState.START;
+    }
+    sortIteration() {
+        if (this.state == HeapSortState.START) {
+            this.heapSize = this.elems.length;
+            //0 -> 1,2
+            //1 -> 3,4
+            //2 -> 5,6
+            //n -> 2n+1,2n+2
+            this.pos = Math.floor((this.heapSize - 1) / 2);
+            this.filterPos = this.pos;
+            this.state = HeapSortState.HEAPIFY;
+        }
+        else if (this.state == HeapSortState.HEAPIFY) {
+            if (!this.filterDownIteration()) {
+                this.pos--;
+                this.filterPos = this.pos;
+                if (this.pos < 0) {
+                    this.state = HeapSortState.SORT;
+                    console.log("Heap created");
+                    this.pos = 0;
+                    this.filterPos = this.pos;
+                    this.verifyMaxHeap();
+                }
+            }
+        }
+        else if (this.state == HeapSortState.SORT) {
+            // this.heapSize = 0;
+            // return;
+            if (this.filterPos == -1) {
+                this.elems.swap(0, this.heapSize - 1);
+                this.heapSize--;
+                this.filterPos = 0;
+            }
+            else if (!this.filterDownIteration()) {
+                this.filterPos = -1;
+            }
+            // this.state = HeapSortState.
+            // this.heapSize = 0;
+        }
+    }
+    filterDownIteration() {
+        let filtered = false;
+        let fp = this.filterPos;
+        if (this.filterPos < 0 || this.filterPos >= this.heapSize) {
+            debugger;
+            return;
+        }
+        let parent = this.elems.get(this.filterPos);
+        let childIdx = this.filterPos * 2 + 1;
+        let c1, c2;
+        if (childIdx < this.heapSize)
+            c1 = this.elems.get(childIdx);
+        if ((childIdx + 1) < this.heapSize)
+            c2 = this.elems.get(childIdx + 1);
+        let greaterIdx, greater;
+        if (c1 != undefined) {
+            greaterIdx = childIdx;
+            greater = c1;
+            if (c2 != undefined && c2 > c1) {
+                greaterIdx++;
+                greater = c2;
+            }
+        }
+        if (greaterIdx && greater > parent) {
+            //swap
+            this.elems.swap(this.filterPos, greaterIdx);
+            //now filter child
+            this.filterPos = greaterIdx;
+            filtered = true;
+        }
+        //check
+        let a = this.elems.getArr();
+        if (((fp * 2 + 1) < this.heapSize && a[fp] < a[fp * 2 + 1]) || ((fp * 2 + 2) < this.heapSize && a[fp] < a[fp * 2 + 2])) {
+            console.error(`INVALID HEAP index ${fp} : ${a[fp]} => ${a[fp * 2 + 1]}, ${a[fp * 2 + 2]}`);
+            console.log({
+                parent,
+                c1,
+                c2,
+                greater,
+                greaterIdx
+            });
+            debugger;
+        }
+        return filtered;
+    }
+    getComplete() {
+        return this.heapSize <= 0;
+    }
+    verifyMaxHeap() {
+        let a = this.elems.getArr();
+        for (let i = 0; i < this.heapSize; i++) {
+            let p = a[i];
+            let childIdx = i * 2 + 1;
+            let c1, c2;
+            c1 = a[childIdx];
+            c2 = a[childIdx + 1];
+            if (c1 > p && c2 > p) {
+                console.error(`INVALID HEAP index ${i} : ${p} => ${c1}, ${c2}`);
+                debugger;
+            }
+        }
+    }
+    setOpt(key, val) { }
+}
+Algorithms.push({
+    name: "heapsort",
+    constructor: HeapSort,
+});
+//# sourceMappingURL=heap-sort.js.map
\ No newline at end of file
diff --git a/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js
index 99129ce86e1c4867632f83f794a31ed45a161b14..6bec6e71978fa3c705ae80e1513a462265ae23fc 100644
--- a/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js
+++ b/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js
@@ -5,14 +5,21 @@ var QuickSortState;
     QuickSortState[QuickSortState["FILTER"] = 2] = "FILTER";
     QuickSortState[QuickSortState["SOLVED"] = 3] = "SOLVED";
 })(QuickSortState || (QuickSortState = {}));
+var QuickSortPivot;
+(function (QuickSortPivot) {
+    QuickSortPivot[QuickSortPivot["LOW"] = 0] = "LOW";
+    QuickSortPivot[QuickSortPivot["MIDDLE"] = 1] = "MIDDLE";
+    QuickSortPivot[QuickSortPivot["HIGH"] = 2] = "HIGH";
+    QuickSortPivot[QuickSortPivot["RANDOM"] = 3] = "RANDOM";
+    QuickSortPivot[QuickSortPivot["LMH_MEAN"] = 4] = "LMH_MEAN";
+})(QuickSortPivot || (QuickSortPivot = {}));
 class QuickSort extends Algorithm {
     constructor() {
         super(...arguments);
         this.state = 0;
+        this.pivotType = 0;
         this.bt = [];
     }
-    sort() {
-    }
     sortIteration() {
         if (this.state == QuickSortState.START) {
             this.hi = this.elems.length - 1;
@@ -20,7 +27,33 @@ class QuickSort extends Algorithm {
             this.state = QuickSortState.GETPIVOT;
         }
         if (this.state == QuickSortState.GETPIVOT) {
-            this.pivot = this.elems.get(this.lo); //could be any value really
+            switch (this.pivotType) {
+                case QuickSortPivot.LOW:
+                    this.pivotIdx = this.lo + 1;
+                    this.pivot = this.elems.get(this.pivotIdx);
+                    break;
+                case QuickSortPivot.MIDDLE:
+                    // this.pivot = this.elems.get(this.lo) + this.elems.get(this.hi);
+                    // this.pivot /= 2;
+                    this.pivotIdx = Math.floor((this.lo + this.hi) / 2);
+                    this.pivot = this.elems.get(this.pivotIdx);
+                    break;
+                case QuickSortPivot.HIGH:
+                    this.pivotIdx = this.hi;
+                    this.pivot = this.elems.get(this.pivotIdx);
+                    break;
+                case QuickSortPivot.RANDOM:
+                    // this.pivot = this.elems.get(Math.floor(Math.random() * (this.hi - this.lo)));
+                    // this.pivot += this.elems.get(Math.floor(Math.random() * (this.hi - this.lo)));
+                    // this.pivot /= 2;
+                    this.pivotIdx = Math.floor(Math.random() * (this.hi - this.lo)) + this.lo;
+                    this.pivot = this.elems.get(this.pivotIdx);
+                    break;
+                case QuickSortPivot.LMH_MEAN:
+                    this.pivot = this.elems.get(this.lo) + 1 + this.elems.get(this.hi) + this.elems.get(Math.round((this.lo + this.hi) / 2)); //could be any value really
+                    this.pivot /= 3;
+                    break;
+            }
             this.anchor = this.lo;
             this.insert = this.hi;
             this.state = QuickSortState.FILTER;
@@ -55,9 +88,23 @@ class QuickSort extends Algorithm {
     getComplete() {
         return this.state == QuickSortState.SOLVED;
     }
+    setOpt(key, val) {
+        if (key == "pivotmode") {
+            this.pivotType = val;
+        }
+    }
 }
 Algorithms.push({
     name: "quicksort",
-    constructor: QuickSort
+    constructor: QuickSort,
+    opts: {
+        "pivotmode": [
+            ["first", QuickSortPivot.LOW],
+            ["middle", QuickSortPivot.MIDDLE],
+            ["last", QuickSortPivot.HIGH],
+            ["random", QuickSortPivot.RANDOM],
+            ["average", QuickSortPivot.LMH_MEAN]
+        ]
+    }
 });
 //# sourceMappingURL=quick-sort.js.map
\ No newline at end of file
diff --git a/public/coding_challenges/sorting-visualiser/index.html b/public/coding_challenges/sorting-visualiser/index.html
index 24c6d349bfa862b7a96095d91655e70ac0b095a9..e4a56e3605c2822100d599d2f2bad86f6acc5eb0 100644
--- a/public/coding_challenges/sorting-visualiser/index.html
+++ b/public/coding_challenges/sorting-visualiser/index.html
@@ -33,6 +33,9 @@
 			<li>
 				<a href="sorter.html?algo=quicksort">Quick Sort</a>
 			</li>
+			<li>
+				<a href="sorter.html?algo=heapsort">Heap Sort</a>
+			</li>
 		</ul>
 
 	</div>
diff --git a/public/coding_challenges/sorting-visualiser/main.js b/public/coding_challenges/sorting-visualiser/main.js
index a78121e2812b288e1bf74e4b7d321f19f320edf3..0108c0368cad06d32795c6b0ea4fc9b1794d7f37 100644
--- a/public/coding_challenges/sorting-visualiser/main.js
+++ b/public/coding_challenges/sorting-visualiser/main.js
@@ -1,12 +1,16 @@
 /// <reference path="../../../p5.global-mode.d.ts" />
 /// <reference path="./visualiser.ts" />
 /// <reference path="./algo.ts" />
+let Bar;
+let sBar;
 let Arr;
 let Algo;
 let AlgoName;
 let Vis;
 let visSelect;
 let algoSelect;
+let sOpts;
+let sReset;
 let sAccessors;
 let sWrites;
 let params;
@@ -25,51 +29,95 @@ function setup() {
         speed = parseFloat(params["speed"]);
     else
         speed = 1;
+    //@ts-ignore
+    Bar = createDiv();
+    Bar.id("bar");
+    //@ts-ignore
+    sBar = createSpan();
+    sBar.parent(Bar);
     visSelect = createSelect(false);
+    visSelect.parent(sBar);
     for (let v of Visualisers) {
         //@ts-ignore
         visSelect.option(v.name);
     }
     //@ts-ignore
     let ShuffleButton = createButton("Shuffle");
+    ShuffleButton.parent(sBar);
     ShuffleButton.mouseClicked(() => {
         Algo = new FisherYates(Arr);
         AlgoName = "shuffle";
         // shuffled = false;
         sorting = true;
     });
+    ShuffleButton.doubleClicked(() => {
+        if (Algo)
+            Algo.sort();
+    });
     algoSelect = createSelect(false);
+    algoSelect.parent(sBar);
     for (let a of Algorithms) {
         //@ts-ignore
         algoSelect.option(a.name);
     }
     algoSelect.value(params["algo"]);
+    algoSelect.input(createOptSels);
     //@ts-ignore
     let SortButton = createButton("Sort");
+    SortButton.parent(sBar);
     SortButton.mouseClicked(() => {
         if (shuffled && !sorting) {
             sorting = true;
             if (algoSelect.elt.value) {
-                let AlgoCon = Algorithms.reduce((a, c) => c.name == algoSelect.elt.value ? c : a, null).constructor;
+                let A = Algorithms.reduce((a, c) => c.name == algoSelect.elt.value ? c : a, null);
+                let AlgoCon = A.constructor;
                 if (AlgoCon) {
                     Algo = new AlgoCon(Arr);
                     AlgoName = algoSelect.elt.value;
+                    //set opts
+                    let opts = document.getElementsByClassName("sort_opt");
+                    for (let elem of opts) {
+                        let id = elem.id.split(':')[1];
+                        //@ts-ignore
+                        Algo.setOpt(id, A.opts[id].reduce((a, c) => c[0] == elem.value ? c[1] : a, -1));
+                    }
                 }
             }
-            //  else
-            // if (params["algo"]) {
-            // 	let AlgoCon: AlgorithmConstructor = Algorithms.reduce((a, c) => c.name == params["algo"] ? c : a, null).constructor;
-            // 	if (AlgoCon) {
-            // 		Algo = new AlgoCon(Arr);
-            // 		AlgoName = params["algo"];
-            // 	}
-            // }
         }
     });
+    SortButton.doubleClicked(() => {
+        if (Algo)
+            Algo.sort();
+    });
     //@ts-ignore
     sAccessors = createSpan();
+    sAccessors.parent(Bar);
     //@ts-ignore
     sWrites = createSpan();
+    sWrites.parent(Bar);
+    //@ts-ignore
+    sOpts = createSpan();
+    sOpts.parent(Bar);
+    sOpts.style("margin-left", "10px");
+    //@ts-ignore
+    sReset = createSpan();
+    sReset.parent(Bar);
+    sReset.style("position", "relative");
+    sReset.style("float", "right");
+    //@ts-ignore
+    let resetButton = createButton("Reset.");
+    resetButton.parent(sReset);
+    resetButton.mouseClicked(() => {
+        Algo = null;
+        if (params["count"])
+            Arr = new WatchedArray(parseInt(params["count"]));
+        else
+            Arr = new WatchedArray(100);
+    });
+    // resetButton.style("position", "absolute");
+    // resetButton.style("right", "0");
+    // resetButton.style("position", "absolute");
+    createOptSels();
     createCanvas(windowWidth - 40, 700);
 }
 function draw() {
@@ -101,4 +149,31 @@ function draw() {
 function windowResized() {
     resizeCanvas(windowWidth - 30, height);
 }
+function createOptSels() {
+    let old_opts = document.getElementsByClassName("sort_opt");
+    while (old_opts.length)
+        old_opts[0].remove();
+    //create new opts
+    let algoOpts = Algorithms.reduce((a, c) => c.name == algoSelect.elt.value ? c : a, null).opts;
+    if (algoOpts) {
+        for (let opt_key in algoOpts) {
+            let optSel = createSelect(false);
+            optSel.parent(sOpts);
+            optSel.addClass("sort_opt");
+            optSel.id(`sort_opt:${opt_key}`);
+            for (let opt of algoOpts[opt_key]) {
+                //@ts-ignore
+                optSel.option(opt[0]);
+            }
+            optSel.input(() => {
+                if (Algo) {
+                    Algo.setOpt(opt_key, algoOpts[opt_key].reduce((a, c) => c[0] == optSel.value() ? c[1] : a, -1));
+                }
+            });
+        }
+    }
+}
+// new p5(null, document.getElementById("sorter"));
+//@ts-ignore
+new p5(null, "sorter");
 //# sourceMappingURL=main.js.map
\ No newline at end of file
diff --git a/public/coding_challenges/sorting-visualiser/sorter.html b/public/coding_challenges/sorting-visualiser/sorter.html
index 6733c01c9eef105caed160173f7117bf39faf484..a05ae76c5d645a079748966cb7b62968370cb457 100644
--- a/public/coding_challenges/sorting-visualiser/sorter.html
+++ b/public/coding_challenges/sorting-visualiser/sorter.html
@@ -14,11 +14,13 @@
 	<script src="./visualisers/standard.js"></script>
 	<script src="./visualisers/spiral.js"></script>
 	<script src="./visualisers/pie.js"></script>
+	<script src="./visualisers/scatter.js"></script>
 
 	<script src="./algorithms/fisher-yates.js"></script>
 	<script src="./algorithms/bubble-sort.js"></script>
 	<script src="./algorithms/cocktail-shaker-sort.js"></script>
 	<script src="./algorithms/quick-sort.js"></script>
+	<script src="./algorithms/heap-sort.js"></script>
 
 </head>
 
diff --git a/public/coding_challenges/sorting-visualiser/visualisers/scatter.js b/public/coding_challenges/sorting-visualiser/visualisers/scatter.js
new file mode 100644
index 0000000000000000000000000000000000000000..6014fd0f3f738152f2d266592458a3ca4206b4d3
--- /dev/null
+++ b/public/coding_challenges/sorting-visualiser/visualisers/scatter.js
@@ -0,0 +1,21 @@
+Visualisers.push({
+    name: "scatter",
+    fxn: (arr, c) => {
+        c.background(180, 180, 180);
+        c.push();
+        c.noStroke();
+        c.colorMode(HSB);
+        let a = arr.getArr();
+        translate(0, height);
+        for (let i = 0; i < a.length; i++) {
+            let pScale = a[i] / a.length;
+            let col = c.color(pScale * 360, 100, 100);
+            c.stroke(col);
+            c.strokeWeight(10);
+            c.point(c.width / a.length, -c.height * pScale);
+            c.translate(c.width / a.length, 0);
+        }
+        c.pop();
+    }
+});
+//# sourceMappingURL=scatter.js.map
\ No newline at end of file