<?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: Amar Gul</title>
    <description>The latest articles on DEV Community by Amar Gul (@amargul).</description>
    <link>https://dev.to/amargul</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%2F3922263%2F2747df38-001d-4983-848f-164fd2ef7749.jpg</url>
      <title>DEV Community: Amar Gul</title>
      <link>https://dev.to/amargul</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amargul"/>
    <language>en</language>
    <item>
      <title>Backpropagation Is Just Dynamic Programming (I Animated It to Prove It)</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Wed, 01 Jul 2026 13:51:53 +0000</pubDate>
      <link>https://dev.to/amargul/backpropagation-is-just-dynamic-programming-i-animated-it-to-prove-it-4jhj</link>
      <guid>https://dev.to/amargul/backpropagation-is-just-dynamic-programming-i-animated-it-to-prove-it-4jhj</guid>
      <description>&lt;p&gt;Everyone learns backpropagation as "apply the chain rule." Almost nobody explains why it's &lt;em&gt;fast&lt;/em&gt; — and that "why" is the whole reason deep learning is computationally possible at all.&lt;/p&gt;

&lt;p&gt;So I animated one full training step to show the part most explanations skip.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ZV5HKbCsdfo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're actually seeing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forward pass:&lt;/strong&gt; a single signal travels through 3 weights → a prediction → compared to the target = the loss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backward pass:&lt;/strong&gt; the error (δ) flows back through the network. δ₃ is computed at the output, then &lt;strong&gt;reused&lt;/strong&gt; to get δ₂, which is reused to get δ₁ — never recalculated from scratch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The point:&lt;/strong&gt; one forward pass + one backward pass produces &lt;em&gt;every&lt;/em&gt; weight's gradient with zero redundant work, no matter how deep the network goes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That reuse — store a result once, reuse it instead of recomputing — is the exact definition of dynamic programming. The only difference from a Fibonacci memo is that the stored value is a derivative.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt; (a phase state machine: &lt;code&gt;idle → forward → loss → backward → done&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framer Motion&lt;/strong&gt; for the signal particles and edge transitions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Audio API&lt;/strong&gt; — every tone is synthesized, no audio files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic timing&lt;/strong&gt; so each run records identically for video&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three things that were trickier than expected
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. My animated particle was blinking on every frame
&lt;/h3&gt;

&lt;p&gt;The signal dot flickered constantly. The cause: I'd defined the particle component &lt;em&gt;inside&lt;/em&gt; the main component, so every state update created a new function identity and React remounted it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ inside the component → new identity each render → remount → blink&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Backprop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FlowParticle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ module scope → stable identity → smooth animation&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FlowParticle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;particleKey&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;circle&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lifting it to module scope fixed it instantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The sound wouldn't play
&lt;/h3&gt;

&lt;p&gt;Browsers block audio that isn't triggered by a user gesture — an &lt;code&gt;AudioContext&lt;/code&gt; starts in a &lt;code&gt;suspended&lt;/code&gt; state. The fix is to create it lazily and &lt;code&gt;resume()&lt;/code&gt; it on the first real click:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getAudio&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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;Ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AudioContext&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webkitAudioContext&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;Ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;audioRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Ctx&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;audioRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleStart&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;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAudio&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;ctx&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;suspended&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// unlock on gesture&lt;/span&gt;
  &lt;span class="nf"&gt;startCountdown&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;h3&gt;
  
  
  3. Syncing a fixed-timer animation to a recorded voiceover
&lt;/h3&gt;

&lt;p&gt;This was the real headache. The narration isn't uniform — it lingers on δ₃ and the first gradient, then &lt;em&gt;races&lt;/em&gt; from δ₂ to δ₁. One global delay constant couldn't fit that, so every beat got its own duration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ms each ∂L/∂w stays on screen, keyed by edge — w₂ is short so δ₁&lt;/span&gt;
&lt;span class="c1"&gt;// lands right on "delta one"; w₁ is long so all gradients hold&lt;/span&gt;
&lt;span class="c1"&gt;// through the dynamic-programming wrap.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GRAD_HOLD_BY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8500&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;1000&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="mi"&gt;14000&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;gradMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;GRAD_HOLD_BY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;edge&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gradCalc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One more gotcha: &lt;code&gt;setTimeout&lt;/code&gt; gets throttled when the tab loses focus, so the animation drifts during a long take. Keep the recording tab foregrounded.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The chain rule was always true — &lt;strong&gt;backpropagation is just the dynamic-programming version of it.&lt;/strong&gt; Reframing it that way made it click in a way "here's the partial derivative" never did.&lt;/li&gt;
&lt;li&gt;Building the network data-driven (one &lt;code&gt;HIDDEN&lt;/code&gt; constant drives the layout, math, and animation) meant I could change depth without touching the render code.&lt;/li&gt;
&lt;li&gt;For explainer animations, &lt;strong&gt;deterministic, hand-tunable timing beats physics-based motion&lt;/strong&gt; every time — you need each beat to land on a spoken word.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Watch it
&lt;/h2&gt;

&lt;p&gt;🎥 Full 2-minute walkthrough: &lt;a href="https://youtu.be/ZV5HKbCsdfo" rel="noopener noreferrer"&gt;https://youtu.be/ZV5HKbCsdfo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is part of my &lt;strong&gt;"AI, Visualized"&lt;/strong&gt; series — neural networks → gradient descent → backprop, with Transformers next. What should I animate after that? 👇&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>machinelearning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Visualized Why AI Models Fail to Train — It's One Number</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Tue, 23 Jun 2026 18:27:19 +0000</pubDate>
      <link>https://dev.to/amargul/i-visualized-why-ai-models-fail-to-train-its-one-number-11co</link>
      <guid>https://dev.to/amargul/i-visualized-why-ai-models-fail-to-train-its-one-number-11co</guid>
      <description>&lt;p&gt;Every AI model on earth learns the same way: by rolling downhill. And the single most common reason a model &lt;em&gt;fails&lt;/em&gt; to learn comes down to one number — the learning rate. So I built an animation that runs the exact same gradient descent three times, from the identical starting point, changing only that number.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/wng0ddn0wPo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're actually seeing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;loss curve&lt;/strong&gt; — height is how wrong the model's prediction is. The ball starts high (a random guess); the goal is the bottom.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;cyan tangent line&lt;/strong&gt; is the gradient — the slope under the ball. It points uphill, so the algorithm steps the opposite way.&lt;/li&gt;
&lt;li&gt;Three runs, one variable: &lt;strong&gt;too low&lt;/strong&gt; (crawls, never arrives), &lt;strong&gt;tuned&lt;/strong&gt; (settles dead-on the global minimum), &lt;strong&gt;too high&lt;/strong&gt; (overshoots, swings, never settles — divergence).&lt;/li&gt;
&lt;li&gt;A deliberate &lt;strong&gt;second dip on the left&lt;/strong&gt; — a local minimum, the trap a too-cautious model can get stuck in.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;React (a small phase state machine: &lt;code&gt;idle → gradient → step → done&lt;/code&gt;), Framer Motion for the ball + tangent, the Web Audio API for synthesized tones (no asset files), and deterministic, recording-friendly timing so every take is identical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things that were trickier than expected
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The SVG ball stuttered until I stopped animating &lt;code&gt;cx&lt;/code&gt;/&lt;code&gt;cy&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Animating an SVG circle's &lt;code&gt;cx&lt;/code&gt;/&lt;code&gt;cy&lt;/code&gt; attributes is janky — they're not GPU-composited. The fix is to keep the circle at the origin and animate its &lt;strong&gt;transform&lt;/strong&gt; (&lt;code&gt;x&lt;/code&gt;/&lt;code&gt;y&lt;/code&gt;) instead, which Framer Motion springs smoothly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;circle&lt;/span&gt;
  &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;sx&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ballX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;sy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;L&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ballX&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;spring&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stiffness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;damping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sx&lt;/code&gt;/&lt;code&gt;sy&lt;/code&gt; map math-space (weight, loss) to screen-space pixels.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Recording needs identical-length runs — so I drift-corrected the clock
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;setTimeout&lt;/code&gt; drifts: a loop of &lt;code&gt;await delay(2000)&lt;/code&gt; slowly desyncs because each step also pays for render time. For a video where every run must be the same length, I pin each step to an &lt;strong&gt;absolute&lt;/strong&gt; wall-clock budget instead of a relative sleep:&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="c1"&gt;// Sleep until `targetMs` have elapsed since the run started — not "wait N ms".&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetMs&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;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetMs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;runStartRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remaining&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="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ...inside the loop:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;step&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="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;STEP_BUDGET_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any per-step jitter gets absorbed, so the run always lands on the same total duration.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A stale closure made my "auto" sequence ignore the learning rate
&lt;/h3&gt;

&lt;p&gt;To record all three runs hands-free, I built a choreographed sequence that flips the learning-rate preset between runs. It silently used the &lt;em&gt;wrong&lt;/em&gt; rate every time. The culprit: the descent loop read the &lt;code&gt;lr&lt;/code&gt; React state directly, but that value was captured once when the async sequence started — classic stale closure. The fix is to pass the rate in as an argument instead of reading state mid-flight:&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="c1"&gt;// Before: read `lr` from state inside the loop → frozen at sequence start.&lt;/span&gt;
&lt;span class="c1"&gt;// After: the caller passes the rate explicitly.&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runDescent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;gradient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// uses the rate for THIS run, not a stale one&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runDescent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.02&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// low&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runDescent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.46&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// good&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runDescent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// high&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The whole lesson of the video is a one-liner in code: &lt;code&gt;x = x - rate * gradient&lt;/code&gt;. Everything dramatic on screen is that single update, looped.&lt;/li&gt;
&lt;li&gt;Designing the loss landscape was its own mini-problem — I needed a function with a &lt;em&gt;shallow&lt;/em&gt; local minimum and a &lt;em&gt;deeper&lt;/em&gt; global one so "stuck in a local min" is visible at a glance. A tilted double-well quartic did it.&lt;/li&gt;
&lt;li&gt;Synthesizing audio whose pitch tracks the loss makes the descent &lt;em&gt;audible&lt;/em&gt; — you can hear it settle (or never settle) with your eyes closed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Watch / try it
&lt;/h2&gt;

&lt;p&gt;🎥 Full walkthrough: &lt;a href="https://youtu.be/wng0ddn0wPo" rel="noopener noreferrer"&gt;https://youtu.be/wng0ddn0wPo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm animating a series — neural network training ✅, gradient descent (this one), and backpropagation is next. What should I visualize after that — transformers, CNNs, or something classic like Dijkstra? 👇&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>machinelearning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Built a Neural Network You Can Watch Train — Forward Pass, Loss, and Backprop, Animated</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Sat, 20 Jun 2026 17:36:00 +0000</pubDate>
      <link>https://dev.to/amargul/i-built-a-neural-network-you-can-watch-train-forward-pass-loss-and-backprop-animated-45db</link>
      <guid>https://dev.to/amargul/i-built-a-neural-network-you-can-watch-train-forward-pass-loss-and-backprop-animated-45db</guid>
      <description>&lt;p&gt;Every neural-network tutorial I tried threw equations at me before I ever &lt;em&gt;saw&lt;/em&gt; what was actually happening. I wanted the reverse: &lt;strong&gt;watch&lt;/strong&gt; the activations flow forward, &lt;strong&gt;watch&lt;/strong&gt; the loss bars shrink, &lt;strong&gt;watch&lt;/strong&gt; backprop push gradients right-to-left across the layers.&lt;/p&gt;

&lt;p&gt;So I built it. Here's a neural network that trains itself in front of you 👇&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/71x_jjFDS-Y"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What you're actually seeing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Forward pass&lt;/strong&gt; — particles flow &lt;strong&gt;left → right&lt;/strong&gt; as activations propagate through the layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Loss&lt;/strong&gt; — bars drop each epoch, and the output neurons glow &lt;strong&gt;red → gold → green&lt;/strong&gt; as the error shrinks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backpropagation&lt;/strong&gt; — particles flow &lt;strong&gt;right → left&lt;/strong&gt;, the gradient returning toward the input.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;9 epochs&lt;/strong&gt;, deterministically timed so every run is identical.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No training data is harmed in the making of this animation — it's a faithful &lt;em&gt;visual model&lt;/em&gt; of the phases, built for intuition, not for crunching MNIST.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt; for the phase state machine: &lt;code&gt;idle → forward → loss → backward → done&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Framer Motion&lt;/strong&gt; for the particle and node animations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Audio API&lt;/strong&gt; for synthesized sound (no audio files), tied to the dot movement&lt;/li&gt;
&lt;li&gt;A small &lt;strong&gt;drift-corrected timing loop&lt;/strong&gt; so the whole run lands on a fixed wall-clock budget&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three things that were trickier than expected
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Animate particles with CSS transforms, not SVG attributes
&lt;/h3&gt;

&lt;p&gt;My first version animated each particle's &lt;code&gt;cx&lt;/code&gt;/&lt;code&gt;cy&lt;/code&gt;. It worked but stuttered. Switching to Framer Motion's &lt;code&gt;x&lt;/code&gt;/&lt;code&gt;y&lt;/code&gt; (which compile to GPU-friendly CSS transforms) made it buttery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;motion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nt"&gt;circle&lt;/span&gt;
  &lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;opacity&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;scale&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="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;y1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&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="mi"&gt;1&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;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.65&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;easeInOut&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Backprop has to flow &lt;em&gt;backward&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Sounds obvious, but my first pass spawned the backprop particles in the same direction as the forward pass. The fix was just swapping the source/target layer so the dots travel from the deeper layer back toward the input:&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="c1"&gt;// Forward: layer l-1 → l   (left → right)&lt;/span&gt;
&lt;span class="nf"&gt;spawnParticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&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;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FORWARD_COLOR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Backprop: layer l → l-1  (right → left)&lt;/span&gt;
&lt;span class="nf"&gt;spawnParticles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;l&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;BACKWARD_COLOR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tiny change, huge difference in how "correct" it reads.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Make the network &lt;em&gt;show&lt;/em&gt; it's learning
&lt;/h3&gt;

&lt;p&gt;Loss bars are fine, but I wanted the network itself to react. So the output nodes are colored by the current loss — the same thresholds as the bars, so the legend stays consistent:&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;lossColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loss&lt;/span&gt;&lt;span class="p"&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;loss&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.15&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;GREEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// basically trained&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;loss&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;0.40&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;GOLD&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;RED&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                       &lt;span class="c1"&gt;// high error&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Early epochs glow red; by the end they settle into green. You &lt;em&gt;see&lt;/em&gt; the network heal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus: deterministic timing (so I could record it)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;setInterval&lt;/code&gt; drift made every recording a slightly different length. I anchored a start timestamp and held each epoch to a fixed budget, correcting drift as it goes:&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;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetMs&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;remaining&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetMs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;runStart&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;remaining&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="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ...end of each epoch:&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;epoch&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="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;EPOCH_BUDGET_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every run lands on the same total time regardless of frame jitter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Animation isn't decoration — putting the &lt;em&gt;direction&lt;/em&gt; of data flow on screen taught me backprop better than any equation did.&lt;/li&gt;
&lt;li&gt;Browsers block the Web Audio API until a user gesture, so "start" had to be a real click.&lt;/li&gt;
&lt;li&gt;Deterministic timing is underrated: it made the thing recordable &lt;em&gt;and&lt;/em&gt; the code simpler.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm animating a whole set of these — sorting, Dijkstra, hash tables, binary trees. &lt;strong&gt;What algorithm should I visualize next?&lt;/strong&gt; Drop it in the comments 👇&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>machinelearning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Dynamic Programming Finally Makes Sense — 1900 vs 16 Calculations Visualized in React</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Mon, 15 Jun 2026 17:04:25 +0000</pubDate>
      <link>https://dev.to/amargul/dynamic-programming-finally-makes-sense-1900-vs-16-calculations-visualized-in-react-3bko</link>
      <guid>https://dev.to/amargul/dynamic-programming-finally-makes-sense-1900-vs-16-calculations-visualized-in-react-3bko</guid>
      <description>&lt;p&gt;Dynamic Programming has a reputation as &lt;br&gt;
the hardest interview topic in CS.&lt;/p&gt;

&lt;p&gt;The reputation disappears when you see &lt;br&gt;
it visually.&lt;/p&gt;
&lt;h2&gt;
  
  
  The One Principle
&lt;/h2&gt;

&lt;p&gt;Never calculate the same thing twice.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`javascript&lt;br&gt;
// Without DP — 1900+ calculations&lt;br&gt;
function fibNaive(n) {&lt;br&gt;
  if (n &amp;lt;= 1) return n;&lt;br&gt;
  return fibNaive(n-1) + fibNaive(n-2); // recalculates everything&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// With DP — 16 calculations&lt;br&gt;
const memo = {};&lt;br&gt;
function fibDP(n) {&lt;br&gt;
  if (n &amp;lt;= 1) return n;&lt;br&gt;
  if (memo[n]) return memo[n]; // instant lookup&lt;br&gt;
  memo[n] = fibDP(n-1) + fibDP(n-2);&lt;br&gt;
  return memo[n];&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;fibNaive(15): 1946 function calls&lt;br&gt;
fibDP(15): 16 unique calculations&lt;/p&gt;

&lt;p&gt;Same answer. 1900 vs 16.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two Properties to Identify DP
&lt;/h2&gt;

&lt;p&gt;Overlapping subproblems — same smaller &lt;br&gt;
problems appear multiple times. Check: &lt;br&gt;
does fib(10) appear more than once?&lt;/p&gt;

&lt;p&gt;Optimal substructure — optimal solution &lt;br&gt;
builds from optimal subproblems. Check: &lt;br&gt;
does fib(15) depend on optimal fib(14)?&lt;/p&gt;

&lt;p&gt;Both true for Fibonacci. Both true for &lt;br&gt;
most DP interview problems.&lt;/p&gt;
&lt;h2&gt;
  
  
  Watch It
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/1IdAGLOxk6k"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Next
&lt;/h2&gt;

&lt;p&gt;This is Video 10 — the final algorithm &lt;br&gt;
in the series. Next: AI and Machine &lt;br&gt;
Learning visualized the same way.&lt;/p&gt;

&lt;p&gt;Subscribe to AlgoCanvas:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dynamicprogramming</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Every JavaScript Object is a Hash Table — Here is What is Actually Happening Inside Your Curly Braces</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Tue, 09 Jun 2026 20:35:29 +0000</pubDate>
      <link>https://dev.to/amargul/every-javascript-object-is-a-hash-table-here-is-what-is-actually-happening-inside-your-curly-5hl7</link>
      <guid>https://dev.to/amargul/every-javascript-object-is-a-hash-table-here-is-what-is-actually-happening-inside-your-curly-5hl7</guid>
      <description>&lt;p&gt;Every time you write this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
const user = {&lt;br&gt;
  name: "Amar",&lt;br&gt;
  role: "Senior Dev"&lt;br&gt;
};&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A hash table is running under the hood.&lt;/p&gt;
&lt;h2&gt;
  
  
  What Actually Happens
&lt;/h2&gt;

&lt;p&gt;When you set user.name = "Amar" — the &lt;br&gt;
key "name" passes through a hash function:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
function hashFunction(key) {&lt;br&gt;
  let hash = 0;&lt;br&gt;
  for (let i = 0; i &amp;lt; key.length; i++) {&lt;br&gt;
    hash = (hash + key.charCodeAt(i)) % BUCKET_COUNT;&lt;br&gt;
  }&lt;br&gt;
  return hash;&lt;br&gt;
}&lt;br&gt;
// hashFunction("name") → 3&lt;br&gt;
// Value stored in bucket 3&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Lookup is O(1)
&lt;/h2&gt;

&lt;p&gt;When you read user.name:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run hashFunction("name") → 3&lt;/li&gt;
&lt;li&gt;Go directly to bucket 3&lt;/li&gt;
&lt;li&gt;Return value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No searching. No comparing. No looping.&lt;br&gt;
Direct access regardless of object size.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Collision Problem
&lt;/h2&gt;

&lt;p&gt;Two different keys can produce the same &lt;br&gt;
bucket number. This is called a collision.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
hashFunction("name") → 3&lt;br&gt;
hashFunction("role") → 3  // same bucket!&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;JavaScript handles this through chaining &lt;br&gt;
— each bucket holds a linked list of &lt;br&gt;
all key-value pairs that hash to that &lt;br&gt;
index.&lt;/p&gt;
&lt;h2&gt;
  
  
  Watch It Live
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/emDxJ5spA3A"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Dynamic Programming — turning O(2ⁿ) &lt;br&gt;
problems into O(n) with memoization.&lt;/p&gt;

&lt;p&gt;Subscribe to AlgoCanvas:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The 1959 Algorithm That Still Powers Every GPS on Earth — Visualized in React</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Tue, 02 Jun 2026 20:04:17 +0000</pubDate>
      <link>https://dev.to/amargul/the-1959-algorithm-that-still-powers-every-gps-on-earth-visualized-in-react-3d3f</link>
      <guid>https://dev.to/amargul/the-1959-algorithm-that-still-powers-every-gps-on-earth-visualized-in-react-3d3f</guid>
      <description>&lt;p&gt;A mathematician named Edsger Dijkstra &lt;br&gt;
invented this algorithm in 1959 while &lt;br&gt;
thinking about the shortest route between &lt;br&gt;
two cities in the Netherlands.&lt;/p&gt;

&lt;p&gt;65 years later it runs inside Google Maps &lt;br&gt;
billions of times every day.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why It Still Works
&lt;/h2&gt;

&lt;p&gt;Dijkstra solves the single-source shortest &lt;br&gt;
path problem with one brilliant greedy insight:&lt;/p&gt;

&lt;p&gt;When you process the closest unvisited node &lt;br&gt;
first — its distance is permanently optimal. &lt;br&gt;
No undiscovered shorter path can exist.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
// Always pick closest unvisited node&lt;br&gt;
let u = -1;&lt;br&gt;
NODES.forEach((n) =&amp;gt; {&lt;br&gt;
  if (!visited.has(n.id) &amp;amp;&amp;amp; &lt;br&gt;
     (u === -1 || dist[n.id] &amp;lt; dist[u])) {&lt;br&gt;
    u = n.id;&lt;br&gt;
  }&lt;br&gt;
});&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Edge Relaxation
&lt;/h2&gt;

&lt;p&gt;For every neighbor of the current node — &lt;br&gt;
check if going through this node creates &lt;br&gt;
a shorter path:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
for (const { node: v, weight } of adj[u]) {&lt;br&gt;
  if (!visited.has(v) &amp;amp;&amp;amp; &lt;br&gt;
      dist[u] + weight &amp;lt; dist[v]) {&lt;br&gt;
    dist[v] = dist[u] + weight;&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If yes — update. This is called relaxation.&lt;br&gt;
Watch it happen in real time in the &lt;br&gt;
visualization below.&lt;/p&gt;
&lt;h2&gt;
  
  
  The One Weakness
&lt;/h2&gt;

&lt;p&gt;Negative edge weights break the greedy &lt;br&gt;
assumption entirely. A negative edge &lt;br&gt;
could always create a shorter path through &lt;br&gt;
an already finalized node.&lt;/p&gt;

&lt;p&gt;For negative weights — Bellman-Ford &lt;br&gt;
takes over.&lt;/p&gt;
&lt;h2&gt;
  
  
  Watch It Live
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/DIzRNkBDzig"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Hash Tables — O(1) lookup explained &lt;br&gt;
visually. How JavaScript objects work &lt;br&gt;
under the hood in 2 minutes.&lt;/p&gt;

&lt;p&gt;Subscribe to AlgoCanvas:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>BFS vs DFS — Same Graph, Why Such Different Paths? Visualized in React</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Sat, 30 May 2026 21:35:30 +0000</pubDate>
      <link>https://dev.to/amargul/bfs-vs-dfs-same-graph-why-such-different-paths-visualized-in-react-52ba</link>
      <guid>https://dev.to/amargul/bfs-vs-dfs-same-graph-why-such-different-paths-visualized-in-react-52ba</guid>
      <description>&lt;p&gt;BFS and DFS show up in every technical &lt;br&gt;
interview. Most developers know the &lt;br&gt;
names but get confused explaining &lt;br&gt;
the difference.&lt;/p&gt;

&lt;p&gt;Until you see them run on the same graph.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Core Difference
&lt;/h2&gt;

&lt;p&gt;BFS (Breadth First Search) uses a Queue.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
// BFS — process level by level&lt;br&gt;
const queue = [startNode];&lt;br&gt;
while (queue.length &amp;gt; 0) {&lt;br&gt;
  const node = queue.shift(); // front&lt;br&gt;
  visited.add(node);&lt;br&gt;
  for (const neighbor of adj[node]) {&lt;br&gt;
    if (!visited.has(neighbor)) {&lt;br&gt;
      queue.push(neighbor); // back&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;DFS (Depth First Search) uses a Stack &lt;br&gt;
(or recursion).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
// DFS — go deep first&lt;br&gt;
function dfs(node) {&lt;br&gt;
  visited.add(node);&lt;br&gt;
  for (const neighbor of adj[node]) {&lt;br&gt;
    if (!visited.has(neighbor)) {&lt;br&gt;
      dfs(neighbor); // recurse deep&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  When to Use Each
&lt;/h2&gt;

&lt;p&gt;BFS → Shortest path, level-order traversal,&lt;br&gt;
GPS navigation, social network degrees&lt;/p&gt;

&lt;p&gt;DFS → Cycle detection, topological sort,&lt;br&gt;
maze solving, all possible paths&lt;/p&gt;
&lt;h2&gt;
  
  
  Watch It
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/cYL_hOjac64"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Dijkstra's Algorithm — shortest path in &lt;br&gt;
weighted graphs. The algorithm powering &lt;br&gt;
GPS navigation.&lt;/p&gt;

&lt;p&gt;Subscribe to AlgoCanvas:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Linked Lists Finally Simple — Why Insert is O(1) When Arrays Are O(n)Uses This Algorithm for .sort()</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Sat, 23 May 2026 20:44:08 +0000</pubDate>
      <link>https://dev.to/amargul/linked-lists-finally-simple-why-insert-is-o1-when-arrays-are-onuses-this-algorithm-for-46l2</link>
      <guid>https://dev.to/amargul/linked-lists-finally-simple-why-insert-is-o1-when-arrays-are-onuses-this-algorithm-for-46l2</guid>
      <description>&lt;p&gt;Most developers understand arrays intuitively.&lt;br&gt;
But Linked Lists feel abstract until you &lt;br&gt;
see them visually.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Core Problem with Arrays
&lt;/h2&gt;

&lt;p&gt;When you insert in the middle of an array,&lt;br&gt;
every element after the insertion point &lt;br&gt;
must shift one position right.&lt;/p&gt;

&lt;p&gt;With 1000 elements that means 999 shifts.&lt;br&gt;
That's O(n) time.&lt;/p&gt;
&lt;h2&gt;
  
  
  How Linked Lists Solve This
&lt;/h2&gt;

&lt;p&gt;A Linked List node contains two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The actual data value&lt;/li&gt;
&lt;li&gt;A pointer to the next node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Insertion only updates 2 pointers:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`javascript&lt;br&gt;
// Point previous node to new node&lt;br&gt;
prevNode.next = newNode;&lt;/p&gt;

&lt;p&gt;// Point new node to next node&lt;br&gt;&lt;br&gt;
newNode.next = nextNode;&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That's it. O(1) time regardless of &lt;br&gt;
list size.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Trade Off
&lt;/h2&gt;

&lt;p&gt;Fast insertion. Slow access.&lt;/p&gt;

&lt;p&gt;Arrays: O(1) access by index&lt;br&gt;
Linked Lists: O(n) access — must traverse&lt;/p&gt;

&lt;p&gt;Choose based on your use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequent insertion/deletion → Linked List&lt;/li&gt;
&lt;li&gt;Frequent access by index → Array&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Built in React
&lt;/h2&gt;

&lt;p&gt;Three operations animated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traverse — walks HEAD to TAIL&lt;/li&gt;
&lt;li&gt;Insert — shows 2 pointer updates&lt;/li&gt;
&lt;li&gt;Delete — shows pointer reconnection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`javascript&lt;br&gt;
async function insertMiddle() {&lt;br&gt;
  const insertPos = Math.floor(nodes.length / 2);&lt;/p&gt;

&lt;p&gt;// Traverse to position&lt;br&gt;
  for (let i = 0; i &amp;lt;= insertPos; i++) {&lt;br&gt;
    setTraversing(i);&lt;br&gt;
    await addDelay(SPEED);&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;// Insert new node&lt;br&gt;
  setNodes(prev =&amp;gt; {&lt;br&gt;
    const updated = [...prev];&lt;br&gt;
    updated.splice(insertPos, 0, newNode);&lt;br&gt;
    return updated;&lt;br&gt;
  });&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Watch It
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/PGiqJTCUORo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Binary Search Tree — self organizing &lt;br&gt;
structure that achieves O(log n) for &lt;br&gt;
search, insert and delete simultaneously.&lt;/p&gt;

&lt;p&gt;Subscribe to AlgoCanvas:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Quick Sort — Why Your Programming Language Uses This Algorithm for .sort()</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Sat, 23 May 2026 20:42:45 +0000</pubDate>
      <link>https://dev.to/amargul/quick-sort-why-your-programming-language-uses-this-algorithm-for-sort-25nm</link>
      <guid>https://dev.to/amargul/quick-sort-why-your-programming-language-uses-this-algorithm-for-sort-25nm</guid>
      <description>&lt;p&gt;Every time you call .sort() in JavaScript,&lt;br&gt;
Python, or Java — Quick Sort is running &lt;br&gt;
under the hood.&lt;/p&gt;

&lt;p&gt;Not Bubble Sort. Not Merge Sort. Quick Sort.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Quick Sort?
&lt;/h2&gt;

&lt;p&gt;It combines two things perfectly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O(n log n) average performance&lt;/li&gt;
&lt;li&gt;In-place sorting — no extra memory needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Merge Sort also achieves O(n log n) but &lt;br&gt;
requires O(n) extra space. Quick Sort only &lt;br&gt;
needs O(log n) stack space for recursion.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Core Idea — The Pivot
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`javascript&lt;br&gt;
function partition(arr, low, high) {&lt;br&gt;
  const pivot = arr[high];&lt;br&gt;
  let i = low - 1;&lt;/p&gt;

&lt;p&gt;for (let j = low; j &amp;lt; high; j++) {&lt;br&gt;
    if (arr[j] &amp;lt;= pivot) {&lt;br&gt;
      i++;&lt;br&gt;
      [arr[i], arr[j]] = [arr[j], arr[i]];&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
  [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];&lt;br&gt;
  return i + 1;&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Pick a pivot. Everything smaller goes left.&lt;br&gt;
Everything larger goes right.&lt;br&gt;
The pivot is now in its permanent position.&lt;/p&gt;
&lt;h2&gt;
  
  
  The One Weakness
&lt;/h2&gt;

&lt;p&gt;Worst case is O(n²) — when pivot is always &lt;br&gt;
the smallest or largest element.&lt;/p&gt;

&lt;p&gt;Modern implementations use randomized pivot &lt;br&gt;
selection to avoid this trap.&lt;/p&gt;
&lt;h2&gt;
  
  
  Watch It
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/9Pbn345vsxY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Linked Lists — a completely different &lt;br&gt;
approach to storing data in memory.&lt;/p&gt;

&lt;p&gt;Subscribe to AlgoCanvas:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Merge Sort vs Bubble Sort — Why 800 Comparisons Beats 147 Every Time</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Sat, 16 May 2026 19:00:50 +0000</pubDate>
      <link>https://dev.to/amargul/merge-sort-vs-bubble-sort-why-800-comparisons-beats-147-every-time-1de0</link>
      <guid>https://dev.to/amargul/merge-sort-vs-bubble-sort-why-800-comparisons-beats-147-every-time-1de0</guid>
      <description>&lt;p&gt;Most developers know Merge Sort is faster &lt;br&gt;
than Bubble Sort. But watching it happen &lt;br&gt;
makes the difference visceral.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;Bubble Sort: 800+ comparisons&lt;br&gt;
Merge Sort: 147 comparisons&lt;br&gt;
Same 30 elements. Same result.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Such a Difference?
&lt;/h2&gt;

&lt;p&gt;Bubble Sort compares adjacent elements &lt;br&gt;
and moves them one step at a time — &lt;br&gt;
O(n²) in worst case.&lt;/p&gt;

&lt;p&gt;Merge Sort divides the array completely &lt;br&gt;
down to single elements, then merges &lt;br&gt;
them back in sorted order — O(n log n) &lt;br&gt;
guaranteed every single time.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Key Code
&lt;/h2&gt;

&lt;p&gt;The entire algorithm is built on one &lt;br&gt;
recursive insight:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;&lt;code&gt;javascript&lt;br&gt;
function mergeSort(arr, left, right) {&lt;br&gt;
  if (left &amp;gt;= right) return;&lt;br&gt;
  const mid = Math.floor((left + right) / 2);&lt;br&gt;
  mergeSort(arr, left, mid);&lt;br&gt;
  mergeSort(arr, mid + 1, right);&lt;br&gt;
  merge(arr, left, mid, right);&lt;br&gt;
}&lt;br&gt;
\&lt;/code&gt;&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;A single element is already sorted by &lt;br&gt;
definition. Everything else is just &lt;br&gt;
merging sorted groups together.&lt;/p&gt;
&lt;h2&gt;
  
  
  Built in React — Zero Libraries
&lt;/h2&gt;

&lt;p&gt;Animation state managed entirely with &lt;br&gt;
useState and useRef. No external &lt;br&gt;
animation libraries.&lt;/p&gt;

&lt;p&gt;Color coding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Purple = unsorted&lt;/li&gt;
&lt;li&gt;Gold = currently merging
&lt;/li&gt;
&lt;li&gt;Red = comparing&lt;/li&gt;
&lt;li&gt;Cyan = fully sorted&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Watch It
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/K5Cy4-47d5s"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Quick Sort — the algorithm that actually &lt;br&gt;
powers sorting in most programming &lt;br&gt;
languages. Spoiler: it's faster than &lt;br&gt;
Merge Sort in practice despite worse &lt;br&gt;
worst-case complexity.&lt;/p&gt;

&lt;p&gt;Subscribe to AlgoCanvas:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Binary Search vs Linear Search — Visualized in React with No Libraries</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Tue, 12 May 2026 16:45:09 +0000</pubDate>
      <link>https://dev.to/amargul/binary-search-vs-linear-search-visualized-in-react-with-no-libraries-3gke</link>
      <guid>https://dev.to/amargul/binary-search-vs-linear-search-visualized-in-react-with-no-libraries-3gke</guid>
      <description>&lt;p&gt;As a Senior React developer I wanted to &lt;br&gt;
show exactly WHY binary search exists — &lt;br&gt;
not just explain it with words.&lt;/p&gt;

&lt;p&gt;So I built a side by side comparison &lt;br&gt;
showing both algorithms running on the &lt;br&gt;
same array searching for the same target.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Visual Difference
&lt;/h2&gt;

&lt;p&gt;Linear search checks every element from &lt;br&gt;
left to right. Predictable but slow.&lt;/p&gt;

&lt;p&gt;Binary search goes straight to the middle. &lt;br&gt;
Higher or lower? Eliminate half. Repeat.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;React functional components&lt;/li&gt;
&lt;li&gt;useState for animation state&lt;/li&gt;
&lt;li&gt;useRef for timeout management&lt;/li&gt;
&lt;li&gt;Zero external animation libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Key Code
&lt;/h2&gt;

&lt;p&gt;Two separate animation pipelines running &lt;br&gt;
sequentially — linear first, then binary:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`javascript&lt;br&gt;
// Linear search steps&lt;br&gt;
for (let i = 0; i &amp;lt; array.length; i++) {&lt;br&gt;
  steps.push({ checking: i });&lt;br&gt;
  if (array[i] === target) break;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// Binary search steps&lt;br&gt;&lt;br&gt;
while (low &amp;lt;= high) {&lt;br&gt;
  const mid = Math.floor((low + high) / 2);&lt;br&gt;
  steps.push({ middle: mid, eliminated });&lt;br&gt;
  if (array[mid] === target) break;&lt;br&gt;
  else if (array[mid] &amp;lt; target) low = mid + 1;&lt;br&gt;
  else high = mid - 1;&lt;br&gt;
}&lt;br&gt;
`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/FHQLbVrWEJo"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Building visualizations for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Merge Sort&lt;/li&gt;
&lt;li&gt;Quick Sort
&lt;/li&gt;
&lt;li&gt;Dijkstra's Algorithm&lt;/li&gt;
&lt;li&gt;Binary Search Trees&lt;/li&gt;
&lt;li&gt;Hash Tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subscribe to AlgoCanvas on YouTube:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

&lt;p&gt;Feedback welcome — drop a comment below!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>How I Built a Bubble Sort Visualizer in React — No Animation Libraries</title>
      <dc:creator>Amar Gul</dc:creator>
      <pubDate>Sat, 09 May 2026 18:08:50 +0000</pubDate>
      <link>https://dev.to/amargul/how-i-built-a-bubble-sort-visualizer-in-react-no-animation-libraries-3d18</link>
      <guid>https://dev.to/amargul/how-i-built-a-bubble-sort-visualizer-in-react-no-animation-libraries-3d18</guid>
      <description>&lt;p&gt;As a Senior React developer I've built dozens &lt;br&gt;
of complex applications — but I wanted to create &lt;br&gt;
something that actually helps people understand &lt;br&gt;
computer science fundamentals visually.&lt;/p&gt;

&lt;p&gt;So I built AlgoCanvas — a series of algorithm &lt;br&gt;
visualizations built purely in React.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;Most algorithm explanations use static diagrams &lt;br&gt;
or walls of code. Neither actually shows you &lt;br&gt;
what the algorithm is &lt;em&gt;doing&lt;/em&gt; at each step.&lt;/p&gt;

&lt;p&gt;I wanted to change that. Watch it work — and &lt;br&gt;
it clicks instantly.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;React functional components&lt;/li&gt;
&lt;li&gt;useState for animation state&lt;/li&gt;
&lt;li&gt;useRef for timeout management&lt;/li&gt;
&lt;li&gt;CSS-in-JS inline styles&lt;/li&gt;
&lt;li&gt;Zero external animation libraries&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How the Animation Works
&lt;/h2&gt;

&lt;p&gt;The key insight is building all animation steps &lt;br&gt;
upfront, then replaying them with setTimeout:&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;bubbleSort&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;arr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;array&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="k"&gt;for &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;i&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;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &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;j&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;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="nx"&gt;i&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;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&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="na"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;:&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="na"&gt;comparing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;j&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;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;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;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;j&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="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;j&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;j&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="o"&gt;=&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;j&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;arr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&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="na"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;:&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="na"&gt;comparing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;j&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="p"&gt;}&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;Then replay each step with a delay:&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="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;step&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="nf"&gt;setComparing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comparing&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;120&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;h2&gt;
  
  
  Color Coding System
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🟣 Purple — unsorted elements&lt;/li&gt;
&lt;li&gt;🔴 Red — currently being compared&lt;/li&gt;
&lt;li&gt;🩵 Cyan — fully sorted and done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes it immediately obvious what the &lt;br&gt;
algorithm is doing at every single step.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/0_7TuvGp0GM"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm building visualizations for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Binary Search&lt;/li&gt;
&lt;li&gt;Merge Sort&lt;/li&gt;
&lt;li&gt;Quick Sort&lt;/li&gt;
&lt;li&gt;Dijkstra's Algorithm&lt;/li&gt;
&lt;li&gt;Binary Search Trees&lt;/li&gt;
&lt;li&gt;Hash Tables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subscribe to AlgoCanvas on YouTube if you &lt;br&gt;
want to follow along:&lt;br&gt;
👉 youtube.com/@AlgoCanvas&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback Welcome
&lt;/h2&gt;

&lt;p&gt;Would love to hear from other React developers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better patterns for animation state?&lt;/li&gt;
&lt;li&gt;Which algorithm should I visualize next?&lt;/li&gt;
&lt;li&gt;Any performance improvements?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop a comment below!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>algorithms</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
