<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nav</title>
    <description>The latest articles on DEV Community by Nav (@solvelogs).</description>
    <link>https://dev.to/solvelogs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4098255%2Fd0b6046b-0642-4b96-9719-62a5a9d7ea02.png</url>
      <title>DEV Community: Nav</title>
      <link>https://dev.to/solvelogs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/solvelogs"/>
    <language>en</language>
    <item>
      <title>Seven elements, three comparisons: binary search as an animation</title>
      <dc:creator>Nav</dc:creator>
      <pubDate>Fri, 28 Aug 2026 03:49:21 +0000</pubDate>
      <link>https://dev.to/solvelogs/seven-elements-three-comparisons-binary-search-as-an-animation-3o49</link>
      <guid>https://dev.to/solvelogs/seven-elements-three-comparisons-binary-search-as-an-animation-3o49</guid>
      <description>&lt;p&gt;Binary search is one of those algorithms that everyone can describe and plenty of us still get wrong when we write it. The description is easy — halve the interval until you find the target. The details are where it goes sideways: whether the bound is inclusive, whether &lt;code&gt;mid&lt;/code&gt; can overflow, whether the loop is &lt;code&gt;&amp;lt;&lt;/code&gt; or &lt;code&gt;&amp;lt;=&lt;/code&gt;, whether you return the index or the insertion point.&lt;/p&gt;

&lt;p&gt;Here is the whole thing running on a seven-element array, searching for 16.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frvtn7aeysmrd0kpmrzi2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frvtn7aeysmrd0kpmrzi2.gif" alt="Binary search stepping through a sorted array, narrowing from seven elements to one" width="799" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What the picture makes obvious
&lt;/h2&gt;

&lt;p&gt;Reading the code, the halving is a claim you accept. Watching it, the halving is a thing you see happen.&lt;/p&gt;

&lt;p&gt;Three moments stand out that I never noticed from the code alone:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 throws away four of seven elements after one comparison.&lt;/strong&gt; &lt;code&gt;values[3] = 12&lt;/code&gt; is less than 16, so indices 0 through 3 are gone at once. Not one element — over half the array, from a single &lt;code&gt;&amp;lt;&lt;/code&gt;. That is the entire algorithm in one frame, and it is the part the O(log n) notation hides rather than explains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The interval, not the position, is the state.&lt;/strong&gt; I used to think of binary search as "a pointer that jumps around." It is not. The state is &lt;code&gt;[lo, hi]&lt;/code&gt;, and &lt;code&gt;mid&lt;/code&gt; is just a derived value you compute fresh each pass. Once the animation shows the window shrinking instead of a cursor moving, the off-by-one errors mostly stop, because you start asking "is my interval still correct?" instead of "is my index right?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The last step is a comparison, not a discovery.&lt;/strong&gt; At step 5 the interval is a single candidate. You still have to check it. Skipping that check is the classic bug where you return an index for a target that was never in the array.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why seven elements matter
&lt;/h2&gt;

&lt;p&gt;Seven is small enough to hold in your head and large enough to need three comparisons. A million-element array needs twenty. The animation is short, but the shape of what you are watching does not change as the input grows — that is the actual claim binary search makes, and it is easier to believe once you have watched the small version resolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I am curious about
&lt;/h2&gt;

&lt;p&gt;I have been building these step-by-step animations for data structures and algorithms, and I keep going back and forth on how much to show.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is six steps the right pace, or does it move too slowly for something you already know?&lt;/li&gt;
&lt;li&gt;Does the explanation text under each step help, or would you rather just watch the array and work it out?&lt;/li&gt;
&lt;li&gt;Which algorithm is hardest for you to picture? I find graph traversals fine and dynamic programming almost impossible to animate usefully — the table fills in, but the &lt;em&gt;reason&lt;/em&gt; each cell gets its value is invisible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Genuinely curious what people think. If you have seen a visualisation that made something click for you, I would like to see it.&lt;/p&gt;

&lt;p&gt;The full walkthrough with the code in Python, C++ and Java is at &lt;a href="https://solvelogs.com/learn/binary-search/" rel="noopener noreferrer"&gt;solvelogs.com/learn/binary-search&lt;/a&gt; — free, no signup.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>beginners</category>
      <category>computerscience</category>
      <category>discuss</category>
    </item>
    <item>
      <title>What I learned writing 1,096 tests for algorithm animations</title>
      <dc:creator>Nav</dc:creator>
      <pubDate>Fri, 28 Aug 2026 03:38:19 +0000</pubDate>
      <link>https://dev.to/solvelogs/what-i-learned-writing-1096-tests-for-algorithm-animations-307g</link>
      <guid>https://dev.to/solvelogs/what-i-learned-writing-1096-tests-for-algorithm-animations-307g</guid>
      <description>&lt;p&gt;I spent the last few months building visual walkthroughs for data structures and algorithms. Not diagrams — step-by-step animations where you watch the pointers move, the stack grow, and the visited set fill in.&lt;/p&gt;

&lt;p&gt;Somewhere in the middle of it I discovered the animations were lying to me. Here is what went wrong and what I did about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with hand-written animation steps
&lt;/h2&gt;

&lt;p&gt;My first version hardcoded every animation frame. A binary search page had a list of steps like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Check the middle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Target is larger, go right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works right up until you change the input array. Then every frame after the first is quietly wrong, and nothing tells you. The animation still plays. It still looks convincing. It just teaches the wrong thing.&lt;/p&gt;

&lt;p&gt;I found this the embarrassing way: a tree traversal page was animating the correct-looking sequence for a tree that no longer matched the diagram beside it. The picture said one thing, the animation said another, and both were rendered from different sources of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the real algorithm instead
&lt;/h2&gt;

&lt;p&gt;The fix was to stop writing the frames and start recording them. Each page now runs the actual algorithm and logs a snapshot at every meaningful state change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Compare &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt; against &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mid&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The animation is now a byproduct of the algorithm, not a parallel description of it. Change the input and every frame updates. Break the algorithm and the animation breaks visibly, in the same way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually mattered
&lt;/h2&gt;

&lt;p&gt;The real win was that this made the animations &lt;strong&gt;testable&lt;/strong&gt;. Once the steps come from running code, you can assert on the last one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;mid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I now have 1,096 of these assertions across every animation on the site. They run on every build. The suite has caught things I would never have noticed by watching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A heap animation that showed the sift-down but stopped one swap early, so the final heap it displayed was not actually a valid heap.&lt;/li&gt;
&lt;li&gt;Ten separate pages animating a linked list to explain a tree, because the generic fallback visual was easier than writing the right one.&lt;/li&gt;
&lt;li&gt;Several tracers truncated at exactly seven steps — a number that had crept in as a default and then silently became the ceiling for operations that needed fourteen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is my favourite failure. Every one of those pages looked finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell my earlier self
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;If a visual explanation is not generated by the thing it explains, it will drift.&lt;/strong&gt; Documentation drifts from code for exactly the same reason, and we have long since accepted that generated docs beat hand-written ones. Animations are no different — they are just documentation that moves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test the output, not the rendering.&lt;/strong&gt; I do not screenshot-test the SVG. I assert on the final state of the trace. That catches the errors that matter (wrong algorithm) and ignores the ones that do not (a node moved three pixels).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A convincing wrong explanation is worse than no explanation.&lt;/strong&gt; A learner who reads a static wrong sentence often catches it. A learner watching a smooth animation assumes the machine knows better than they do. Getting this right felt less like polish and more like an obligation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The result
&lt;/h2&gt;

&lt;p&gt;The site is &lt;a href="https://solvelogs.com" rel="noopener noreferrer"&gt;SolveLog&lt;/a&gt; — worked LeetCode problems and data structure lessons, each with its own generated animation. A few where the tracer approach earns its keep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://solvelogs.com/learn/binary-search/" rel="noopener noreferrer"&gt;Binary search&lt;/a&gt; — watch the search window actually collapse&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://solvelogs.com/learn/avl-trees/" rel="noopener noreferrer"&gt;AVL trees&lt;/a&gt; — the rebalance rotations are the whole point, and they are impossible to follow as static pictures&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://solvelogs.com/learn/backtracking/" rel="noopener noreferrer"&gt;Backtracking&lt;/a&gt; — the call stack panel beside the tree is what made this click for me&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is free and there is no signup. I built it to learn this material properly myself, and the testing discipline above is the only reason I now trust it.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the tracer architecture in the comments.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>datastructures</category>
      <category>testing</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
