Binary Search

Raw code animation is often not insightful. Many algorithm ideas are hard to communicate with raw line-by-line execution alone for various reasons, and missing the chance to highlight indices on an array directly is one of them. Binary search is a simple example that makes this obvious: the idea is really about watching left, right, and mid move across the array, with those indices highlighted where they land. In more complex algorithms, visualizing indices like this is even more valuable.

The version built in this tutorial is tuned for a general audience, but depending on your visualization goal you might make even smaller changes to the original code.

1) Raw JavaScript

Here is a plain JavaScript implementation. If you were to animate it line-by-line without any context about how left, right, and mid relate to the array, the result would not clearly communicate why each step happens or where the search window is moving.

function mainFunction(nums, target) {
  let left = 0;
  let right = nums.length - 1;

  while (left <= right) {
    const mid = Math.floor((left + right) / 2);

    if (nums[mid] === target) {
      return mid;
    }
    if (nums[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }

  return -1;
}

2) Make it insightful (pointers + mid highlight)

Visucodize lets you keep the same algorithm while making the visualization match the idea. Below, the left panel is the same raw code. The right panel is the Visucodize version (no notifications yet): it enrolls left and right as index pointers on the array so those indices are highlighted with a specified style, and it highlights mid on every iteration (then clears it at the end).

Plain JavaScript (same)
function mainFunction(nums, target) {  let left = 0;  let right = nums.length - 1;  while (left <= right) {    const mid = Math.floor((left + right) / 2);    if (nums[mid] === target) {      return mid;    }    if (nums[mid] < target) {      left = mid + 1;    } else {      right = mid - 1;    }  }  return -1;}
Visucodize (no notifications)
const LEFT_CLASS = 'bs-left', RIGHT_CLASS = 'bs-right', MID_CLASS = 'bs-mid';const LEFT_COLOR = 'blue', RIGHT_COLOR = 'gold', MID_COLOR = 'darkgray';function visMainFunction(inputNums, target) {  initClassProperties();  startBatch();  const nums = VisArray.from(inputNums);  let left = 0;  let right = nums.length - 1;  makeVisVariable(left).registerAsIndexPointer(nums, LEFT_CLASS);  makeVisVariable(right).registerAsIndexPointer(nums, RIGHT_CLASS);  makeVisVariable(target).options({ label: 'target' }).createVis();  endBatch();  while (left <= right) {    startPartialBatch();    const mid = Math.floor((left + right) / 2);    const midVisManager = nums.makeVisManagerForIndex(mid).addClass(MID_CLASS);    if (nums[mid] === target) {      endBatch();      return mid;    }    if (nums[mid] < target) {      left = mid + 1;    } else {      right = mid - 1;    }    midVisManager.removeClass(MID_CLASS);    endBatch();  }  return -1;}function initClassProperties() {  setClassProperties(LEFT_CLASS, { borderColor: LEFT_COLOR, borderWidth: '3px' });  setClassProperties(RIGHT_CLASS, { borderColor: RIGHT_COLOR, borderWidth: '3px' });  setClassProperties(MID_CLASS, { backgroundColor: MID_COLOR, color: '#111827' });}

What the Visucodize additions do:

  • setClassProperties(...): defines the visual style for a class name. When that class is applied to any visual element (directly via addClass(), or indirectly via helpers like registerAsIndexPointer()), Visucodize uses those properties for rendering. initClassProperties() is just a helper to keep styling out of the main algorithm.
  • VisArray.from(inputNums): turns the input array into a visual structure (the variable name is used as the default label).
  • makeVisVariable(left/right).registerAsIndexPointer(nums, LEFT_CLASS/RIGHT_CLASS): turns left and right into moving index pointers on nums.
  • makeVisVariable(target).options({ label: 'target' }).createVis(): renders the search target as a labeled visual value.
  • startBatch() / endBatch(): groups a block into a single step; internal changes are not stepped through.
  • startPartialBatch() / endBatch(): groups a block, but still allows internal changes to animate within that step.
  • const midVisManager = nums.makeVisManagerForIndex(mid).addClass(...): highlights mid in the array for this iteration.
  • midVisManager.removeClass(...): clears the mid highlight at the end of the iteration.

3) Add notifications when it helps

Once the visualization is clear, short notifications make each decision easier to follow. Below is the same code from (2), with only the new notification lines highlighted in green.

const LEFT_CLASS = 'bs-left', RIGHT_CLASS = 'bs-right', MID_CLASS = 'bs-mid';const LEFT_COLOR = 'blue', RIGHT_COLOR = 'gold', MID_COLOR = 'darkgray';function visMainFunction(inputNums, target) {  initClassProperties();  startBatch();  const nums = VisArray.from(inputNums);  let left = 0;  let right = nums.length - 1;  makeVisVariable(left).registerAsIndexPointer(nums, LEFT_CLASS);  makeVisVariable(right).registerAsIndexPointer(nums, RIGHT_CLASS);  makeVisVariable(target).options({ label: 'target' }).createVis();  endBatch();  while (left <= right) {    startPartialBatch();    const mid = Math.floor((left + right) / 2);    const midVisManager = nums.makeVisManagerForIndex(mid).addClass(MID_CLASS);    if (nums[mid] === target) {      notification(`Found target (<b>${target}</b>) at index ${mid}`);      endBatch();      return mid;    }    if (nums[mid] < target) {      notification(`Target (<b>${target}</b>) is greater than nums[${makeColoredSpan('mid', MID_COLOR)}] (${nums[mid]}).        Since the array is sorted in increasing order, if the target exists it must be to the right of ${makeColoredSpan('mid', MID_COLOR)}.        Move ${makeColoredSpan('left', LEFT_COLOR)} to ${makeColoredSpan('mid', MID_COLOR)} + 1.`);      left = mid + 1;    } else {      notification(`Target (<b>${target}</b>) is less than nums[${makeColoredSpan('mid', MID_COLOR)}] (${nums[mid]}).        Since the array is sorted in increasing order, if the target exists it must be to the left of ${makeColoredSpan('mid', MID_COLOR)}.        Move ${makeColoredSpan('right', RIGHT_COLOR)} to ${makeColoredSpan('mid', MID_COLOR)} - 1.`);      right = mid - 1;    }    midVisManager.removeClass(MID_CLASS);    endBatch();  }  notification(`Target (<b>${target}</b>) is not found: ${makeColoredSpan('left', LEFT_COLOR)} (${left}) passed ${makeColoredSpan('right', RIGHT_COLOR)} (${right}), so the search range is empty.`);  return -1;}function initClassProperties() {  setClassProperties(LEFT_CLASS, { borderColor: LEFT_COLOR, borderWidth: '3px' });  setClassProperties(RIGHT_CLASS, { borderColor: RIGHT_COLOR, borderWidth: '3px' });  setClassProperties(MID_CLASS, { backgroundColor: MID_COLOR, color: '#111827' });}

See notification() and log() in the docs.

Opens a new Code Mode tab with a ready-to-run binary search sample.