<?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: javascript</title>
    <description>The latest articles tagged 'javascript' on DEV Community.</description>
    <link>https://dev.to/t/javascript</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/javascript"/>
    <language>en</language>
    <item>
      <title>The Brutal Truths of AR Memory Apps: Why My GPS-Powered Dreams Crashed After 6 Months</title>
      <dc:creator>KevinTen</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:42:00 +0000</pubDate>
      <link>https://dev.to/kevinten10/the-brutal-truths-of-ar-memory-apps-why-my-gps-powered-dreams-crashed-after-6-months-5boj</link>
      <guid>https://dev.to/kevinten10/the-brutal-truths-of-ar-memory-apps-why-my-gps-powered-dreams-crashed-after-6-months-5boj</guid>
      <description>&lt;h1&gt;
  
  
  The Brutal Truths of AR Memory Apps: Why My GPS-Powered Dreams Crashed After 6 Months
&lt;/h1&gt;

&lt;p&gt;Honestly, when I first imagined this AR memory app, I saw something straight out of a sci-fi movie. People walking through their cities, tapping invisible memories into the air, each GPS coordinate a digital time capsule waiting to be rediscovered. "This is going to be revolutionary!" I thought, coding through the nights with the enthusiasm of someone who'd clearly never actually built a real mobile app.&lt;/p&gt;

&lt;p&gt;Here's the thing: after six months of development, two complete rewrites, and countless hours wrestling with JavaScript and Java, my "revolutionary" AR app is... well, let's just say it's taught me some harsh truths about the gap between cool ideas and practical reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Spark: A Digital Time Machine
&lt;/h2&gt;

&lt;p&gt;The dream was simple yet powerful: an app that lets you pin multimedia memories to real-world GPS locations. Take a photo at a park, record a voice note at your favorite coffee shop, or save a video from your first date – then when you return to that spot, the AR overlay brings those memories flooding back like a digital time machine.&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;// My initial WebXR AR rendering - so naive, so hopeful&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryRenderer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;renderMemoryAtLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userLocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&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;arSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;immersive-ar&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;optionalFeatures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;local-floor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hit-test&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="c1"&gt;// This was supposed to "magically" place memories in real world&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;arSession&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestHitTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userLocation&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;memoryElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createMemoryElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Spoiler: It never worked this simply&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memoryElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;anchor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referenceSpace&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;memoryElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// My first introduction to AR reality&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AR session failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backend was equally ambitious – a Java Spring Boot API ready to handle thousands of memories, with geospatial indexing for instant recall.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/memories"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/nearby"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findNearbyMemories&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
        &lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@RequestParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"100"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// This was supposed to find memories within "radius" meters&lt;/span&gt;
        &lt;span class="c1"&gt;// Reality: GPS + AR = precision nightmare&lt;/span&gt;
        &lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;userLocation&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;Point&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lat&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lng&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nearby&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memoryService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findMemoriesWithinRadius&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userLocation&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nearby&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lesson #1: GPS is Not as Precise as You Think
&lt;/h2&gt;

&lt;p&gt;Oh boy, did I learn this one the hard way. I spent weeks optimizing geospatial queries, implementing complex MySQL spatial indexing, and designing algorithms to calculate "nearby" memories with millimeter precision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The brutal truth? GPS accuracy is basically a lie.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In open areas: 3-5 meters accuracy (if you're lucky)&lt;/li&gt;
&lt;li&gt;In cities with tall buildings: 20-30 meters (basically "somewhere in this neighborhood")&lt;/li&gt;
&lt;li&gt;Indoors: Forget about it, GPS doesn't work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means my "pin memories to exact locations" feature was more like "pin memories to the general vicinity of where you think you were." Not exactly the precise digital time machine I envisioned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// My naive distance calculation - assumes GPS is accurate to within 1 meter&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nf"&gt;calculateDistance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Distance in meters (simplified Haversine formula)&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6371&lt;/span&gt;&lt;span class="n"&gt;e3&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Earth's radius in meters&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;φ1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toRadians&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLatitude&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;φ2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toRadians&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLatitude&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nc"&gt;Δφ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toRadians&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLatitude&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLatitude&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="nc"&gt;Δλ&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toRadians&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLongitude&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;p1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLongitude&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Δφ&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Δφ&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cos&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;φ1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cos&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;φ2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
                &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Δλ&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sin&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Δλ&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;atan2&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sqrt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sqrt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;R&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// distance in meters&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// The reality check: 20-30 meter "accuracy" makes this calculation almost meaningless&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isUserNearMemory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Memory&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calculateDistance&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLocation&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLocation&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Had to increase this from 10m to 50m "just to work"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lesson #2: AR Rendering is a Nightmare
&lt;/h2&gt;

&lt;p&gt;Let me be clear: WebXR is an amazing technology. The browsers that support it are doing incredible work. But the reality of cross-device AR rendering is... challenging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Device Compatibility Issues:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iPhone 12 Pro: Works beautifully&lt;/li&gt;
&lt;li&gt;Android phones from 2020+: Basically works, with some quirks&lt;/li&gt;
&lt;li&gt;Budget Android phones: LOL, nope&lt;/li&gt;
&lt;li&gt;Desktop browsers: Just don't even ask&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Battery drain that would make your phone cry&lt;/li&gt;
&lt;li&gt;Rendering delays that break the AR magic&lt;/li&gt;
&lt;li&gt;Memory management issues that crash the app
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// My attempt to handle "all devices" - spoiler: I failed&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ARDeviceCompatibility&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;checkARSupport&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;isIOS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/iPad|iPhone|iPod/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&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;isAndroid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/Android/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userAgent&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;isIOS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkIOSARSupport&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;excellent&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="k"&gt;else&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;isAndroid&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkAndroidARSupport&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;good&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="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;checkWebXRSupport&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;basic&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unsupported&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="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// This ended up being a giant switch statement of device-specific hacks&lt;/span&gt;
  &lt;span class="nf"&gt;handleDeviceSpecificRendering&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deviceType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deviceType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ios-pro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;// Enable advanced features, better performance&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;android-flagship&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;// Basic AR, warn about battery drain&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;android-budget&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;// Just show a message saying "AR not supported"&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;// Generic fallback that rarely worked&lt;/span&gt;
        &lt;span class="k"&gt;break&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lesson #3: The Database Horror Story
&lt;/h2&gt;

&lt;p&gt;I thought, "How hard can storing multimedia memories be?" Famous last words.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problems I didn't anticipate:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File storage:&lt;/strong&gt; S3 seemed simple until I realized managing versions, metadata, access control, and CDN caching was basically a full-time job&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Metadata extraction:&lt;/strong&gt; Extracting EXIF data from photos, audio duration, video thumbnails – each one was a rabbit hole of specialized libraries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Spatial queries:&lt;/strong&gt; MySQL's spatial indexing is powerful but fragile. One slightly malformed coordinate and your query performance goes from 200ms to 47 seconds (yes, I timed this)&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// My "simple" metadata extraction service - became surprisingly complex&lt;/span&gt;
&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MediaMetadataService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;MediaMetadata&lt;/span&gt; &lt;span class="nf"&gt;extractMetadata&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt; &lt;span class="n"&gt;mediaFile&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;MediaMetadata&lt;/span&gt; &lt;span class="n"&gt;metadata&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;MediaMetadata&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mediaFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;endsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".jpg"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;ImageInputStream&lt;/span&gt; &lt;span class="n"&gt;iis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ImageIO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createImageInputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mediaFile&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="nc"&gt;Iterator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ImageReader&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;readers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ImageIO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getImageReaders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iis&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hasNext&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="nc"&gt;ImageReader&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;readers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                    &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setInput&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;iis&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

                    &lt;span class="c1"&gt;// Extract EXIF data&lt;/span&gt;
                    &lt;span class="nc"&gt;IIOMetadata&lt;/span&gt; &lt;span class="n"&gt;exifData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getImageMetadata&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                    &lt;span class="c1"&gt;// ... 200 lines of EXIF parsing code ...&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mediaFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;endsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".mp3"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Audio metadata extraction with JAudioTagger&lt;/span&gt;
                &lt;span class="c1"&gt;// ... another 100 lines ...&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// And so on for every media type...&lt;/span&gt;

        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// My database grew full of "metadata extraction failed" entries&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Failed to extract metadata"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Reality Check: User Testing Results
&lt;/h2&gt;

&lt;p&gt;After building what I thought was a "minimum viable product," I got 20 brave souls to test it. The results were... humbling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The good news:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users loved the concept ("That's so cool!" they said)&lt;/li&gt;
&lt;li&gt;The mobile app interface worked well&lt;/li&gt;
&lt;li&gt;Memory creation was straightforward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The harsh reality:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only 3 users ever successfully used the AR feature more than once&lt;/li&gt;
&lt;li&gt;Battery drain complaints were constant&lt;/li&gt;
&lt;li&gt;GPS accuracy made the "pin memories to locations" feature basically useless&lt;/li&gt;
&lt;li&gt;Users preferred simple photo galleries over the AR experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is when I had to ask myself the hard question: was I building something users actually wanted, or was I building something I thought was technically impressive?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Worked (Surprisingly)
&lt;/h2&gt;

&lt;p&gt;After stripping away all the complex AR and GPS features, I discovered something interesting: the simple memory management parts were actually useful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The boring stuff that people actually used&lt;/span&gt;
&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleMemoryService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getUserMemories&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Simple query, no complex spatial math&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memoryRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByUserIdOrderByCreatedAtDesc&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Memory&lt;/span&gt; &lt;span class="nf"&gt;createMemory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt; &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// No AR, no GPS precision headaches&lt;/span&gt;
        &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCreatedAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memoryRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;searchMemories&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Text search instead of spatial search&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memoryRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByUserIdAndContentContaining&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyword&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Users loved being able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take photos and add notes&lt;/li&gt;
&lt;li&gt;Search through their memories by keywords&lt;/li&gt;
&lt;li&gt;See a timeline of their experiences&lt;/li&gt;
&lt;li&gt;Export their data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All without the complex AR and GPS features that I thought were the "main attraction."&lt;/p&gt;

&lt;h2&gt;
  
  
  The Brutal Financial Math
&lt;/h2&gt;

&lt;p&gt;Let's talk about the real cost of this "passion project":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Development time:&lt;/strong&gt; 6+ months, approximately 200 hours&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud services (AWS):&lt;/strong&gt; $150/month for S3, RDS, and Lambda functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer tools and accounts:&lt;/strong&gt; $200&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total investment:&lt;/strong&gt; ~$1,200 + 200 hours of my life&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Return on investment:&lt;/strong&gt; $0&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real users gained:&lt;/strong&gt; 20 (most of them were friends and family asking for favors)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lessons learned:&lt;/strong&gt; Priceless&lt;/p&gt;

&lt;h2&gt;
  
  
  The Meta Lesson: When Cool Ideas Meet Reality
&lt;/h2&gt;

&lt;p&gt;I learned that the biggest gap in tech isn't between "what's possible" and "what's currently available" – it's between what seems cool in theory and what actually works in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My AR memory app taught me:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;GPS精度残酷现实&lt;/strong&gt; (GPS precision cruel reality): 3-5 meter accuracy means "nearby" is basically a guess&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AR渲染噩梦&lt;/strong&gt; (AR rendering nightmare): Cross-device compatibility and battery drain make it impractical for daily use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;数据库复杂性&lt;/strong&gt; (Database complexity): Simple features hide complex implementation challenges&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;用户需求vs个人偏好&lt;/strong&gt; (User needs vs personal preferences): I built what I thought was cool, not what users actually needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;But here's the unexpected upside:&lt;/strong&gt; By documenting my failures and sharing the brutal truths, I actually helped other developers avoid the same mistakes. The meta-promotion of my failures became more valuable than the app itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Irony of It All
&lt;/h2&gt;

&lt;p&gt;The funny part? I'm now known as the guy who "built that AR memory app that failed spectacularly." I get consulting offers from companies who want to avoid the same mistakes. My failure to build a successful AR app has actually led to more opportunities than if I'd succeeded.&lt;/p&gt;

&lt;p&gt;This is the meta-promotion paradox I discovered: sometimes, promoting your failures leads to more success than promoting your successes.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I were to build this again (spoiler: I'm not), here's what I'd change:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with user validation, not technical exploration&lt;/strong&gt;: Talk to actual users before writing a single line of code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on the boring parts first&lt;/strong&gt;: Simple photo management, search, and organization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leave AR for later&lt;/strong&gt;: Build the core functionality first, then add AR as an optional "fun" feature&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be realistic about GPS&lt;/strong&gt;: Accept that location-based features are inherently imprecise&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan for battery drain&lt;/strong&gt;: Assume AR will kill batteries, plan accordingly&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What About You?
&lt;/h2&gt;

&lt;p&gt;Here's where I turn the question to you: have you ever built something that seemed like a brilliant idea in theory but failed spectacularly in practice? What did you learn from those "brutal truths"?&lt;/p&gt;

&lt;p&gt;Or maybe you're considering a location-based AR app yourself – what questions do you have about the practical challenges I discovered?&lt;/p&gt;

&lt;p&gt;Let me know in the comments below – I'd love to hear about your own technology horror stories (or success stories that learned from others' failures).&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;P.S.&lt;/strong&gt; If you're interested in seeing the actual code (warts and all), you can check out the project on GitHub: &lt;a href="https://github.com/kevinten10/spatial-memory" rel="noopener noreferrer"&gt;https://github.com/kevinten10/spatial-memory&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just don't say I didn't warn you about the GPS precision issues.&lt;/p&gt;

</description>
      <category>ar</category>
      <category>javascript</category>
      <category>mobile</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Deep Dive: JavaScript Innovations in April 2026</title>
      <dc:creator>Norvik Tech</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:38:55 +0000</pubDate>
      <link>https://dev.to/norviktech/deep-dive-javascript-innovations-in-april-2026-f4k</link>
      <guid>https://dev.to/norviktech/deep-dive-javascript-innovations-in-april-2026-f4k</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published at &lt;a href="https://norvik.tech/en/news/analisis-javascript-apr-2026" rel="noopener noreferrer"&gt;norvik.tech&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Explore the latest developments in JavaScript, including procedural UI sounds and LiquidGlass effects. A technical analysis for developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Procedural Sound Synthesis
&lt;/h2&gt;

&lt;p&gt;Procedural sound synthesis enables developers to create sounds programmatically using the Web Audio API, eliminating the need for pre-recorded audio files. By generating sounds directly in the browser, applications can achieve smaller bundle sizes and faster load times. This approach allows for dynamic audio experiences that can adapt to user interactions, making it ideal for gaming and interactive media.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates audio on-the-fly&lt;/li&gt;
&lt;li&gt;Reduces bandwidth usage&lt;/li&gt;
&lt;li&gt;Enables unique soundscapes tailored to application needs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Exploring LiquidGlass Effects in Web Development
&lt;/h2&gt;

&lt;p&gt;LiquidGlass is a JavaScript library designed to render visually appealing glass-like effects on web pages. By leveraging CSS and JavaScript, it creates a pixel-perfect illusion of depth and transparency. This library is particularly useful in enhancing user interfaces, making them more modern and engaging. Its compatibility with various frameworks allows seamless integration into existing projects, whether for dashboards or multimedia sites.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enhances UI aesthetics&lt;/li&gt;
&lt;li&gt;Compatible with various frameworks&lt;/li&gt;
&lt;li&gt;Easy to implement in existing projects&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Applications and Impact
&lt;/h2&gt;

&lt;p&gt;Both procedural sound synthesis and LiquidGlass effects have significant implications for web development. Companies like Spotify and interactive gaming platforms utilize these technologies to enhance user experiences. The benefits include improved engagement metrics and reduced bounce rates, leading to higher conversions. As these innovations become more accessible, teams should consider adopting them to stay competitive in a rapidly evolving digital landscape.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increased user engagement&lt;/li&gt;
&lt;li&gt;Measurable ROI from enhanced experiences&lt;/li&gt;
&lt;li&gt;Competitive advantage through innovation&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Need Custom Software Solutions?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Norvik Tech&lt;/strong&gt; builds high-impact software for businesses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;development&lt;/li&gt;
&lt;li&gt;consulting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://norvik.tech" rel="noopener noreferrer"&gt;Visit norvik.tech&lt;/a&gt; to schedule a free consultation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webaudioapi</category>
      <category>liquidglass</category>
    </item>
    <item>
      <title>The 2nd Attempt: When Your AR App's GPS Dreams Meet Reality's Harsh Truths</title>
      <dc:creator>KevinTen</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:34:46 +0000</pubDate>
      <link>https://dev.to/kevinten10/the-2nd-attempt-when-your-ar-apps-gps-dreams-meet-realitys-harsh-truths-3h24</link>
      <guid>https://dev.to/kevinten10/the-2nd-attempt-when-your-ar-apps-gps-dreams-meet-realitys-harsh-truths-3h24</guid>
      <description>&lt;h1&gt;
  
  
  The 2nd Attempt: When Your AR App's GPS Dreams Meet Reality's Harsh Truths
&lt;/h1&gt;

&lt;p&gt;Honestly, I never thought I'd be writing another article about my spatial memory project. But here we are again, round number two. After months of development, one GitHub star, and countless hours spent chasing what I thought was the next big thing in AR, I've learned more about what doesn't work than what does. So here's the thing: building a spatial memory app that pins multimedia memories to real-world GPS locations is way harder than it sounds. Like, "why-did-I-even-start-this-project" hard.&lt;/p&gt;

&lt;p&gt;Let me tell you about my journey from excited AR enthusiast to seasoned realist.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dream: Digital Time Machine
&lt;/h2&gt;

&lt;p&gt;It all started with a simple idea, right? Everyone wants to capture memories in a more meaningful way. Photos are great, but they're just... photos. What if you could pin a memory to the exact location where it happened? Walk down the street and see your old college dorm pop up in AR. Visit a park and see your wedding ceremony replaying in augmented reality. That was my dream - a digital time machine that brings memories back to life.&lt;/p&gt;

&lt;p&gt;So I built it. Or rather, I tried to build it. The spatial memory network, with a Java Spring Boot backend serving up GPS-located multimedia memories that would render in WebXR on mobile devices. S3 for storage, MySQL with spatial indexes for location queries, and a fancy JavaScript frontend that would make it all come together.&lt;/p&gt;

&lt;p&gt;The architecture was beautiful on paper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestController&lt;/span&gt;
&lt;span class="nd"&gt;@RequestMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/memories"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MemoryController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MemoryRepository&lt;/span&gt; &lt;span class="n"&gt;memoryRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;StorageService&lt;/span&gt; &lt;span class="n"&gt;storageService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createMemory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;MemoryRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Memory&lt;/span&gt; &lt;span class="n"&gt;memory&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;Memory&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTitle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTitle&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDescription&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDescription&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setGeoLocation&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;GeoPoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLatitude&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLongitude&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
        &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMediaUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storageService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uploadFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMediaFile&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
        &lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCreatedAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/nearby"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getMemoriesNearby&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
            &lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nd"&gt;@RequestParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"100"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memoryRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByGeoLocationNear&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GeoPoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="n"&gt;radius&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the data structure was equally elegant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Memory&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;mediaUrl&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;mediaType&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// image, video, audio&lt;/span&gt;
    &lt;span class="nd"&gt;@Type&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GeoJsonPointType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"point"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt; &lt;span class="n"&gt;geoLocation&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt; &lt;span class="n"&gt;createdAt&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;viewCount&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// getters and setters&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was going to be revolutionary. Users could create memories with photos, videos, or audio notes, pin them to specific GPS coordinates, and then when they returned to those locations, their memories would come to life in augmented reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reality: GPS Nightmares and AR Dreams
&lt;/h2&gt;

&lt;p&gt;Fast forward six months later, and I've learned some harsh truths that no blog post prepared me for. Seriously, I learned more from failure than from any tutorial.&lt;/p&gt;

&lt;h3&gt;
  
  
  GPS Accuracy: The 3-5 Meter Myth
&lt;/h3&gt;

&lt;p&gt;Here's a little secret nobody tells you: GPS is not as accurate as you think. In ideal conditions, you might get 3-5 meters of accuracy. But in a city with tall buildings? That number can balloon to 20-30 meters. Try to "pin a memory to the exact location where it happened" when you can't even be within 30 meters of that exact location.&lt;/p&gt;

&lt;p&gt;This isn't just a theoretical problem. I tried to pin a memory to a specific bench in a park. The GPS kept saying I was 25 meters away, even when I was sitting on that exact bench. The algorithm would say "you're not close enough to view this memory" while I was literally sitting on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findMemoriesWithinRadius&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GeoPoint&lt;/span&gt; &lt;span class="n"&gt;userLocation&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;maxDistance&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Query database for memories within maxDistance meters&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;jpql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SELECT m FROM Memory m WHERE ST_Distance(m.geoLocation, :userLocation) &amp;lt;= :maxDistance"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;TypedQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;entityManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpql&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userLocation"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userLocation&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"maxDistance"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxDistance&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResultList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is that 3-5 meters might sound precise, but when you're trying to pin a memory to a specific bench or table, that's not precise enough. It's the difference between "somewhere in this large park" and "right at this specific spot where something meaningful happened."&lt;/p&gt;

&lt;h3&gt;
  
  
  AR Rendering: The Device Compatibility Nightmare
&lt;/h3&gt;

&lt;p&gt;If GPS wasn't bad enough, AR rendering was even worse. I thought WebXR was going to be the great equalizer, but it turns out every device handles AR differently.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iOS devices: Works reasonably well, but you need to ask for camera permissions upfront&lt;/li&gt;
&lt;li&gt;Android devices: A mixed bag. Some devices have great AR support, others barely work&lt;/li&gt;
&lt;li&gt;Budget devices: Forget about it. AR performance is terrible, and battery drains in minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The JavaScript WebXR code was straightforward in theory:&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="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;three&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;XRButton&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;three/examples/jsm/webxr/XRButton.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&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;initializeAR&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;scene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Scene&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;camera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;PerspectiveCamera&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&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;innerWidth&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;innerHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;WebGLRenderer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;antialias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;XRButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Load user's memories near current location&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memories&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchMemoriesNearUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Create AR markers for each memory&lt;/span&gt;
    &lt;span class="nx"&gt;memories&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;memory&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;geometry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BoxGeometry&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;1&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;material&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MeshBasicMaterial&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x00ff00&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;cube&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;THREE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;geometry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;material&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Position based on GPS location relative to user&lt;/span&gt;
        &lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nf"&gt;calculateXOffset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geoLocation&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;calculateYOffset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geoLocation&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;calculateZOffset&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cube&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;camera&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;But the reality? Different devices had different performance. Some would show the AR markers perfectly. Others would lag and stutter. Some would show everything twice. Others would crash completely.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Database Debacle: Multimedia Storage and Spatial Queries
&lt;/h3&gt;

&lt;p&gt;Then there was the database problem. Storing multimedia memories is more complex than you'd think. It's not just "upload a file to S3." It's version control, metadata extraction, access control, CDN caching, and a dozen other things I never considered.&lt;/p&gt;

&lt;p&gt;The MySQL spatial queries that were supposed to be so efficient initially took 47 seconds to execute. That's right - 47 seconds to find memories within 100 meters of your location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Initially slow query&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;memories&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ST_Distance_Sphere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geo_location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;POINT&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After months of optimization, I got it down to 200ms. But that still involved complex indexing, caching strategies, and database restructuring that I never would have predicted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Optimized query with spatial index&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;memories&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ST_Distance_Sphere&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geo_location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;POINT&lt;/span&gt;&lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And don't get me started on the media storage. S3 uploads, version control, metadata extraction, CDN integration - it's a whole other project in itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Brutal Truth: Hours vs Real Users
&lt;/h2&gt;

&lt;p&gt;So how much did all this cost? About 200+ hours of development. And how many real users do I have? About 20. That's it.&lt;/p&gt;

&lt;p&gt;The numbers don't lie:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;200+ development hours&lt;/li&gt;
&lt;li&gt;20 real users
&lt;/li&gt;
&lt;li&gt;$0 in revenue&lt;/li&gt;
&lt;li&gt;-100% ROI&lt;/li&gt;
&lt;li&gt;0 GitHub stars (sadly)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here's where it gets interesting. I learned more building this failed project than I ever could have building a successful one. Seriously.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Learned: The Hard Way
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. GPS Limitations are Real (And Brutal)
&lt;/h3&gt;

&lt;p&gt;I thought I understood GPS until I actually worked with it at scale. The 3-5 meter accuracy in ideal conditions? That's theoretical reality. In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cities: 20-30 meters is common&lt;/li&gt;
&lt;li&gt;Indoor: Forget about it, GPS doesn't work&lt;/li&gt;
&lt;li&gt;Dense areas: Multipath interference ruins accuracy&lt;/li&gt;
&lt;li&gt;Moving vehicles: Accuracy degrades dramatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson? Physical constraints are real. You can't build a hyper-precise location-based app with current GPS technology. It's just not possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. AR is Cool, But Users Don't Actually Need It
&lt;/h3&gt;

&lt;p&gt;This was the biggest surprise. I thought AR was going to be the killer feature. Users would love reliving memories in augmented reality. But honestly? Most people just want to look at photos.&lt;/p&gt;

&lt;p&gt;The AR part of my app had the lowest engagement of any feature. Most users would take a photo, add a caption, and that was it. The AR rendering? That was the least used feature by far.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Simple is Better Than Complex
&lt;/h3&gt;

&lt;p&gt;My first iteration tried to be everything to everyone. AI-powered memory suggestions, advanced search, social features, and complex algorithms. What did users actually use?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add photo/memory at location&lt;/li&gt;
&lt;li&gt;View photos at location&lt;/li&gt;
&lt;li&gt;Basic search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it. The complex features? 95% abandonment rate.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Battery Life is a Hard Constraint
&lt;/h3&gt;

&lt;p&gt;AR apps kill batteries. What looks cool in a 2-minute demo becomes a battery-draining nightmare in real-world use. Users might try AR once, but they won't use it regularly if it means their phone dies by noon.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Accidental Benefits: What I Gained
&lt;/h2&gt;

&lt;p&gt;While the app itself wasn't successful, the skills I gained were invaluable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Advanced mobile development&lt;/strong&gt;: I learned more about mobile performance optimization in 6 months than most developers learn in years&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Geospatial database design&lt;/strong&gt;: Spatial indexing, location-based queries, performance optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AR/VR development&lt;/strong&gt;: WebXR, device compatibility, 3D rendering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS services&lt;/strong&gt;: S3, RDS with spatial data, CDN integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-world user testing&lt;/strong&gt;: How users actually interact with apps vs how you think they will&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Code Museum
&lt;/h3&gt;

&lt;p&gt;I have a museum of bad decisions in my codebase. The overly complex AI recommendation engine with 0.2% click-through rate. The sophisticated search algorithm that took 5 seconds to run. The beautiful but completely unnecessary user interface.&lt;/p&gt;

&lt;p&gt;But each failure taught me something:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple search &amp;gt; complex AI&lt;/li&gt;
&lt;li&gt;Basic text search works perfectly fine&lt;/li&gt;
&lt;li&gt;Users care more about speed than features&lt;/li&gt;
&lt;li&gt;Don't build features people won't use&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  So Should You Build This?
&lt;/h2&gt;

&lt;p&gt;Honestly? Probably not. The reality is much harsher than the dream:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You'll learn a ton about AR, GPS, and mobile development&lt;/li&gt;
&lt;li&gt;Great portfolio piece to show technical complexity&lt;/li&gt;
&lt;li&gt;Interesting intellectual challenge&lt;/li&gt;
&lt;li&gt;Unique learning experience about real-world constraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPS accuracy makes precise location-based features impossible&lt;/li&gt;
&lt;li&gt;AR compatibility and battery life are major hurdles&lt;/li&gt;
&lt;li&gt;Complex technical challenges that aren't worth the payoff&lt;/li&gt;
&lt;li&gt;Low user engagement for "cool" features&lt;/li&gt;
&lt;li&gt;High development cost for minimal return&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The brutal truth is that most AR location-based apps fail for the same reasons mine did. The technology isn't ready yet for what we want to build with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd Do Differently
&lt;/h2&gt;

&lt;p&gt;If I were to start over, I'd focus on what actually works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Simple location-based photos&lt;/strong&gt;: No AR, just photos with GPS coordinates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on indoor spaces&lt;/strong&gt;: Where GPS doesn't work but beacons do&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start with web-first&lt;/strong&gt;: Build a web app that shows location-based memories before thinking about mobile AR&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate before building&lt;/strong&gt;: Actually talk to potential users before writing code&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Final Question
&lt;/h2&gt;

&lt;p&gt;Here's the thing I'm still struggling with: When do you push through vs when do you quit? I spent 200 hours on this project, learned a ton, but essentially built something nobody really wants. Was that worth it? Or should I have quit after the first month when I realized the GPS limitations?&lt;/p&gt;

&lt;p&gt;Honestly, I don't know the answer. What's your experience with projects that seemed great in theory but faced harsh reality in practice? Have you built something similar? What did you learn?&lt;/p&gt;

&lt;p&gt;Let me know in the comments - I'd love to hear about your own technology dreams vs reality moments.&lt;/p&gt;

</description>
      <category>ar</category>
      <category>javascript</category>
      <category>mobile</category>
      <category>opensource</category>
    </item>
    <item>
      <title>How I Reduced PDF File Size by 80% in the Browser — No Server Needed</title>
      <dc:creator>kabir daki</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:24:36 +0000</pubDate>
      <link>https://dev.to/kabir_daki/how-i-reduced-pdf-file-size-by-80-in-the-browser-no-server-needed-2dd</link>
      <guid>https://dev.to/kabir_daki/how-i-reduced-pdf-file-size-by-80-in-the-browser-no-server-needed-2dd</guid>
      <description>&lt;p&gt;When I started building &lt;a href="https://pdfonlinelovepdf.com" rel="noopener noreferrer"&gt;PDFOnlineLovePDF&lt;/a&gt;, I faced one big challenge: &lt;strong&gt;how do you compress a PDF file entirely in the browser, without sending it to a server?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most PDF tools upload your file to a remote server, process it, then send it back. That's slow, raises privacy concerns, and costs money to run at scale.&lt;/p&gt;

&lt;p&gt;I wanted something different: &lt;strong&gt;100% client-side PDF compression.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's exactly how I did it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With Server-Side PDF Compression
&lt;/h2&gt;

&lt;p&gt;Most online PDF tools work like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User uploads file → goes to your server&lt;/li&gt;
&lt;li&gt;Server runs ghostscript or similar tool&lt;/li&gt;
&lt;li&gt;Compressed file is sent back to the user&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This works, but has real problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy:&lt;/strong&gt; Your files touch someone else's server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; Upload + processing + download = slow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; Server bandwidth and CPU are expensive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; 1000 users uploading 10MB files = infrastructure nightmare&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted to eliminate all of these problems.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: PDF.js + PDF-lib in the Browser
&lt;/h2&gt;

&lt;p&gt;The key insight is that modern browsers are incredibly powerful. With the right libraries, you can parse, manipulate, and re-render PDF files entirely in JavaScript — no server required.&lt;/p&gt;

&lt;p&gt;Here are the two libraries that made this possible:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. PDF.js (by Mozilla)
&lt;/h3&gt;

&lt;p&gt;PDF.js lets you &lt;strong&gt;read and render&lt;/strong&gt; PDF files in the browser. It parses the PDF structure and gives you access to every page, image, font, and element.&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="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;pdfjsLib&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdfjs-dist&lt;/span&gt;&lt;span class="dl"&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;loadPDF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pdfjsLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;promise&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;pdf&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;
  
  
  2. PDF-lib
&lt;/h3&gt;

&lt;p&gt;PDF-lib lets you &lt;strong&gt;create and modify&lt;/strong&gt; PDF files entirely in JavaScript. You can add pages, embed images, compress content, and save the result as a new PDF.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdf-lib&lt;/span&gt;&lt;span class="dl"&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;compressPDF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arrayBuffer&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;compressedBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pdfDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;useObjectStreams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// This is key for compression&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;compressedBytes&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;
  
  
  The Compression Strategy
&lt;/h2&gt;

&lt;p&gt;Simply re-saving a PDF with &lt;code&gt;useObjectStreams: true&lt;/code&gt; gives you some compression, but not enough. The real gains come from &lt;strong&gt;re-rendering each page as a compressed image&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's the full strategy I used:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Render Each Page to Canvas
&lt;/h3&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;renderPageToCanvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.5&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;scale&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;canvasContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;promise&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;canvas&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;
  
  
  Step 2: Convert Canvas to Compressed JPEG
&lt;/h3&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;canvasToJpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/jpeg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="c1"&gt;// 0.7 = 70% quality, good balance&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Rebuild the PDF With Compressed Images
&lt;/h3&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;rebuildPDF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pdfjsLib&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pdfjs-dist/build/pdf&lt;/span&gt;&lt;span class="dl"&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;loadingTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pdfjsLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;originalArrayBuffer&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;pdfDoc_original&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;loadingTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promise&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;newPdfDoc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;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;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;pdfDoc_original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numPages&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pdfDoc_original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&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;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;renderPageToCanvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.5&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;jpegBlob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;canvasToJpeg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&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;jpegArrayBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;jpegBlob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arrayBuffer&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;jpegImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;newPdfDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embedJpg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jpegArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jpegImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newPdfDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPage&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="nx"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jpegImage&lt;/span&gt;&lt;span class="p"&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="mi"&gt;0&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;height&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;newPdfDoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&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;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;Using this approach, here's what I observed across different PDF types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;PDF Type&lt;/th&gt;
&lt;th&gt;Original Size&lt;/th&gt;
&lt;th&gt;Compressed Size&lt;/th&gt;
&lt;th&gt;Reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scanned document (10 pages)&lt;/td&gt;
&lt;td&gt;8.2 MB&lt;/td&gt;
&lt;td&gt;1.4 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;83%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Report with images (5 pages)&lt;/td&gt;
&lt;td&gt;3.1 MB&lt;/td&gt;
&lt;td&gt;0.7 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;77%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Text-heavy document (20 pages)&lt;/td&gt;
&lt;td&gt;1.8 MB&lt;/td&gt;
&lt;td&gt;0.4 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;78%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Presentation slides (15 pages)&lt;/td&gt;
&lt;td&gt;12.4 MB&lt;/td&gt;
&lt;td&gt;2.1 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;83%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Average reduction: &lt;strong&gt;~80%&lt;/strong&gt; — entirely in the browser.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quality vs Compression Tradeoff
&lt;/h2&gt;

&lt;p&gt;The JPEG quality setting is the main lever you can pull:&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;// Higher quality = larger file, better text readability&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HIGH_QUALITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.85&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// ~60% reduction&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MEDIUM_QUALITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// ~75% reduction  &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LOW_QUALITY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// ~85% reduction&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For my tool, I offer three preset levels so users can choose based on their needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High Quality&lt;/strong&gt; — best for documents you'll print&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium Quality&lt;/strong&gt; — best for email attachments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low Quality&lt;/strong&gt; — best for web upload or WhatsApp sharing&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Handling Large Files Without Freezing the UI
&lt;/h2&gt;

&lt;p&gt;Processing large PDFs page by page can freeze the browser if you're not careful. The solution is to use &lt;strong&gt;Web Workers&lt;/strong&gt; and update a progress bar as each page is processed.&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;// In your main thread&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&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;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/pdf-compress-worker.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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;progress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;updateProgressBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// e.g. "Processing page 3 of 10"&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;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;downloadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&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;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;arrayBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In pdf-compress-worker.js&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quality&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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;totalPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="cm"&gt;/* get from pdf */&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;totalPages&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="c1"&gt;// process page i&lt;/span&gt;
    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;progress&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="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="s2"&gt; / &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;totalPages&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="p"&gt;}&lt;/span&gt;

  &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;compressedBytes&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;
  
  
  Privacy as a Feature
&lt;/h2&gt;

&lt;p&gt;One unexpected benefit of this approach: &lt;strong&gt;you can genuinely tell users their files never leave their device.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This turned out to be a real differentiator. Many users explicitly mentioned in feedback that they chose PDFOnlineLovePDF because they didn't want to upload sensitive documents (contracts, medical records, financial statements) to an unknown server.&lt;/p&gt;

&lt;p&gt;The privacy benefit is not just marketing — it's a direct result of the technical architecture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;This approach isn't perfect. Here are the tradeoffs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Text becomes non-selectable&lt;/strong&gt;&lt;br&gt;
When you re-render pages as images, the text layer is lost. The output is a purely image-based PDF — you can't select or copy text from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. File size depends on content complexity&lt;/strong&gt;&lt;br&gt;
Text-heavy PDFs compress more than image-heavy ones. A PDF that's already full of JPEG photos may not compress much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Processing is CPU-intensive&lt;/strong&gt;&lt;br&gt;
On older or low-end devices, processing a 50-page PDF can take 10-20 seconds. The progress bar is essential for UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Very large files (100+ pages) are slow&lt;/strong&gt;&lt;br&gt;
For very large documents, a hybrid approach (client-side for preview, server-side for heavy processing) might be better.&lt;/p&gt;




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

&lt;p&gt;Building this feature taught me several things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The browser is more powerful than most developers assume.&lt;/strong&gt; PDF manipulation, image compression, file download — all doable without a backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy-first architecture is a real competitive advantage&lt;/strong&gt;, especially for document tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Workers are underused.&lt;/strong&gt; They make CPU-intensive tasks smooth and non-blocking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UX matters more than perfect compression.&lt;/strong&gt; Users care more about a fast, responsive experience than squeezing out the last few kilobytes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The PDF compression tool is live at &lt;a href="https://pdfonlinelovepdf.com/compress-pdf" rel="noopener noreferrer"&gt;PDFOnlineLovePDF.com&lt;/a&gt;. It's completely free, no signup required.&lt;/p&gt;

&lt;p&gt;If you're building something similar or have questions about the implementation, drop a comment below — happy to help!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If this was useful, consider following me here on DEV.to for more posts about building browser-based tools.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How anagram solvers actually work: algorithms behind the scenes</title>
      <dc:creator>Dean Gilley</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:16:20 +0000</pubDate>
      <link>https://dev.to/dean_gilley/how-anagram-solvers-actually-work-algorithms-behind-the-scenes-2hec</link>
      <guid>https://dev.to/dean_gilley/how-anagram-solvers-actually-work-algorithms-behind-the-scenes-2hec</guid>
      <description>&lt;h1&gt;
  
  
  How anagram solvers actually work: algorithms behind the scenes
&lt;/h1&gt;

&lt;p&gt;If youâ€™ve ever built a word game or a tool to help with Scrabble, youâ€™ve likely run into the "anagram problem." Given a string of characters, how do you efficiently find every valid word in the dictionary that can be formed using those letters?&lt;/p&gt;

&lt;p&gt;A naive approachâ€”generating every possible permutation of the input string and checking if each exists in a setâ€”is a recipe for disaster. For a 10-letter word, youâ€™re looking at 3,628,800 permutations. Thatâ€™s not just slow; itâ€™s unusable. &lt;/p&gt;

&lt;p&gt;To build a production-grade anagram solver, we need to shift our thinking from &lt;em&gt;permutation&lt;/em&gt; to &lt;em&gt;canonical representation&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Canonical Sorted-Key Approach
&lt;/h2&gt;

&lt;p&gt;The most efficient way to solve for exact anagrams is to normalize the dictionary. If two words are anagrams, they contain the exact same characters with the same frequencies. Therefore, if you sort the letters of any word alphabetically, all anagrams of that word will result in the same "key."&lt;/p&gt;

&lt;p&gt;For example, "listen" and "silent" both become "eilnst" when sorted.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Implementation
&lt;/h3&gt;

&lt;p&gt;We preprocess our dictionary into a hash map (or dictionary in Python) where the key is the sorted string and the value is a list of matching words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;build_anagram_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word_list&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;word_list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Canonicalize: sort the letters
&lt;/span&gt;        &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
        &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;

&lt;span class="c1"&gt;# Lookup is O(1) after O(n log n) preprocessing
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_anagrams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&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;This approach is incredibly fast. The lookup time is effectively $O(L \log L)$ where $L$ is the length of the input word (due to the sorting step). Once sorted, the hash map lookup is $O(1)$.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://a2zwordfinder.com/" rel="noopener noreferrer"&gt;Try it live&lt;/a&gt; to see how this canonical mapping handles complex dictionary lookups in real-time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Moving Beyond Exact Matches: The Trie
&lt;/h2&gt;

&lt;p&gt;The sorted-key approach is perfect for finding full-word anagrams, but what if you want to find words that can be formed from a &lt;em&gt;subset&lt;/em&gt; of your letters? Or what if you want to support "wildcard" tiles?&lt;/p&gt;

&lt;p&gt;This is where the &lt;strong&gt;Trie (Prefix Tree)&lt;/strong&gt; shines. A Trie stores words as a tree structure where each node represents a character. &lt;/p&gt;

&lt;p&gt;Instead of sorting, you traverse the tree. If you have the letters "a, r, t," you start at the root and move to the 'a' branch, then 'r', then 't'. If you hit a node marked as a "word end," youâ€™ve found a valid word.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why use a Trie?
&lt;/h3&gt;

&lt;p&gt;Tries are excellent for partial matches and prefix searching. If you are building a game like Boggle or a crossword helper, you can prune the search space early. If the current path in your Trie doesn't exist, you stop searching that branch immediately.&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;class&lt;/span&gt; &lt;span class="nc"&gt;TrieNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEndOfWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="c1"&gt;// Searching for words that can be formed by a set of letters&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;findWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentWord&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;results&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;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEndOfWord&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;results&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;currentWord&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;char&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&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;letters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;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="nx"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&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="nf"&gt;findWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentWord&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;letters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&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="c1"&gt;// Backtrack&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is significantly more flexible than the sorted-key method, though it requires more memory to store the tree structure. For a more visual look at how these letter-based constraints work in practice, check out &lt;a href="https://lettersintowords.com/" rel="noopener noreferrer"&gt;lettersintowords.com&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bit-Vector Optimization
&lt;/h2&gt;

&lt;p&gt;If you are working in a memory-constrained environment or need to perform millions of subset checks per second, you can represent a word's character count using a &lt;strong&gt;bit-vector&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Since there are only 26 letters in the English alphabet, you can map each letter to a specific bit position. However, a simple bit-mask only tells you if a letter is &lt;em&gt;present&lt;/em&gt;. To handle anagrams, you need a frequency count. You can store the count of each letter in a fixed-size array (or a 64-bit integer if the counts are small).&lt;/p&gt;

&lt;p&gt;Comparing if word A is a subset of word B becomes a simple vector subtraction:&lt;br&gt;
&lt;code&gt;if (wordA_counts[i] &amp;lt;= wordB_counts[i])&lt;/code&gt; for all &lt;code&gt;i&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is the "secret sauce" behind high-performance engines that need to check if a rack of tiles can form a specific word without traversing a massive tree structure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Iâ€™d try next
&lt;/h2&gt;

&lt;p&gt;If I were scaling this for a massive dictionary (like the Scrabble Tournament Word List), Iâ€™d look into &lt;strong&gt;Bloom Filters&lt;/strong&gt;. They allow you to check if a word &lt;em&gt;might&lt;/em&gt; exist in the dictionary with very little memory overhead, acting as a fast-fail layer before hitting the more expensive Trie or Hash Map.&lt;/p&gt;

&lt;p&gt;Iâ€™d also experiment with &lt;strong&gt;DAWGs (Directed Acyclic Word Graphs)&lt;/strong&gt;. A DAWG is essentially a compressed Trie. Since many words share suffixes (like "-ing" or "-tion"), a DAWG merges these nodes, drastically reducing the memory footprint of your dictionary while maintaining the same lookup speed as a standard Trie.&lt;/p&gt;

&lt;p&gt;Whether you choose the simplicity of the sorted-key hash map or the power of a Trie, the key is understanding the trade-off between your dictionary's memory footprint and the speed of your search. Happy coding!&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>python</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>10 Tips to Speed Up Your Web App in 2026</title>
      <dc:creator>Alaa Shamel (Genious)</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:12:42 +0000</pubDate>
      <link>https://dev.to/alaashamel/10-tips-to-speed-up-your-web-app-in-2026-25o2</link>
      <guid>https://dev.to/alaashamel/10-tips-to-speed-up-your-web-app-in-2026-25o2</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Users don’t wait for slow websites. If your app takes too long to load or respond, they leave — it’s that simple.&lt;/p&gt;

&lt;p&gt;Performance is not a “nice to have” anymore. It’s a core part of user experience.&lt;/p&gt;

&lt;p&gt;Here are practical, real-world techniques to make your web apps faster in 2024.&lt;/p&gt;




&lt;h2&gt;
  
  
  📊 The Performance Budget
&lt;/h2&gt;

&lt;p&gt;Before optimizing anything, you need clear targets.&lt;/p&gt;

&lt;p&gt;A good baseline is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LCP (Largest Contentful Paint):&lt;/strong&gt; &amp;lt; 2.5s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FID (First Input Delay):&lt;/strong&gt; &amp;lt; 100ms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLS (Cumulative Layout Shift):&lt;/strong&gt; &amp;lt; 0.1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTFB (Time to First Byte):&lt;/strong&gt; &amp;lt; 600ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without measuring, optimization becomes guesswork.&lt;/p&gt;




&lt;h2&gt;
  
  
  🖼️ Tip 1: Optimize Images
&lt;/h2&gt;

&lt;p&gt;Images are usually the biggest performance bottleneck.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Bad
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/huge-image.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Good
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; 
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/image-400.jpg"&lt;/span&gt;
  &lt;span class="na"&gt;srcSet=&lt;/span&gt;&lt;span class="s"&gt;"/image-400.jpg 400w, /image-800.jpg 800w"&lt;/span&gt;
  &lt;span class="na"&gt;sizes=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 600px) 100vw, 50vw"&lt;/span&gt;
  &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Description"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also prefer modern formats like &lt;strong&gt;WebP&lt;/strong&gt; or &lt;strong&gt;AVIF&lt;/strong&gt; — they are significantly smaller than JPEG/PNG.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Tip 2: Code Splitting
&lt;/h2&gt;

&lt;p&gt;Don’t load everything at once.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&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;Dashboard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Dashboard&lt;/span&gt;&lt;span class="dl"&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;Settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Settings&lt;/span&gt;&lt;span class="dl"&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;App&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;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&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;Loading&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&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;Dashboard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/settings&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&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;Settings&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Routes&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&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;This reduces initial bundle size and improves load time.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ Tip 3: Memoize Expensive Calculations
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&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;ExpensiveList&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&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;filteredItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&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;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;filteredItems&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ⏱️ Tip 4: Debounce &amp;amp; Throttle
&lt;/h2&gt;

&lt;p&gt;Avoid triggering heavy operations on every keystroke.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash/debounce&lt;/span&gt;&lt;span class="dl"&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;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;debounce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;query&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;fetchResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input&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="nx"&gt;e&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;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="nx"&gt;value&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;
  
  
  🌐 Tip 5: Use CDN for Static Assets
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/library@1.0.0/dist/library.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CDNs like &lt;strong&gt;Cloudflare&lt;/strong&gt;, &lt;strong&gt;jsDelivr&lt;/strong&gt;, and &lt;strong&gt;unpkg&lt;/strong&gt; reduce latency significantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  💾 Tip 6: Service Worker Caching
&lt;/h2&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;CACHE_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-app-v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;install&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="nx"&gt;event&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="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&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;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CACHE_NAME&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/index.html&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="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;
  
  
  📜 Tip 7: Virtualize Long Lists
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useVirtualizer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tanstack/react-virtual&lt;/span&gt;&lt;span class="dl"&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;virtualizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVirtualizer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;getScrollElement&lt;/span&gt;&lt;span class="p"&gt;:&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;parentRef&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="na"&gt;estimateSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;50&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;This prevents rendering thousands of DOM nodes.&lt;/p&gt;




&lt;h2&gt;
  
  
  📉 Tip 8: Reduce Bundle Size
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Bad
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ✅ Good
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash/debounce&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔤 Tip 9: Optimize Font Loading
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"MyFont"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("/myfont.woff2")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff2"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;font-display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;swap&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;
  
  
  📈 Tip 10: Monitor Performance in Production
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getCLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getFID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getLCP&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;web-vitals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;getCLS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sendToAnalytics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;getFID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sendToAnalytics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;getLCP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sendToAnalytics&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What gets measured gets improved.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Performance Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Images optimized (WebP + lazy loading)&lt;/li&gt;
&lt;li&gt;Code splitting implemented&lt;/li&gt;
&lt;li&gt;Bundle size under control&lt;/li&gt;
&lt;li&gt;Fonts optimized&lt;/li&gt;
&lt;li&gt;CDN enabled&lt;/li&gt;
&lt;li&gt;Caching active&lt;/li&gt;
&lt;li&gt;Core Web Vitals monitored&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Conclusion
&lt;/h2&gt;

&lt;p&gt;Performance is not a feature you add at the end — it’s something you design from the beginning.&lt;/p&gt;

&lt;p&gt;Start with the biggest wins like images, bundle size, and lazy loading, then measure and improve continuously.&lt;/p&gt;

&lt;p&gt;A fast app is not just better technically — it directly improves user retention and experience.&lt;/p&gt;




&lt;p&gt;💬 What’s your go-to performance optimization trick in React or JavaScript?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>TypeScript vs JavaScript: The Complete Guide for 2026</title>
      <dc:creator>Alaa Shamel (Genious)</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:00:02 +0000</pubDate>
      <link>https://dev.to/alaashamel/typescript-vs-javascript-the-complete-guide-for-2026-1471</link>
      <guid>https://dev.to/alaashamel/typescript-vs-javascript-the-complete-guide-for-2026-1471</guid>
      <description>&lt;h1&gt;
  
  
  TypeScript vs JavaScript in 2024 — A Practical Developer Perspective
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;One of the most common discussions in modern web development is whether to use JavaScript or TypeScript.&lt;/p&gt;

&lt;p&gt;Instead of treating it as a “which is better” argument, it’s more useful to understand &lt;strong&gt;when and why each one should be used&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Both tools are powerful — but they serve slightly different purposes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding the Core Difference
&lt;/h2&gt;

&lt;p&gt;At a fundamental level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript is the core language executed in the browser&lt;/li&gt;
&lt;li&gt;TypeScript is a superset of JavaScript that adds static typing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TypeScript code is compiled into JavaScript before running in the browser.&lt;/p&gt;

&lt;p&gt;The key benefit is that TypeScript introduces &lt;strong&gt;type safety during development&lt;/strong&gt;, helping catch errors early.&lt;/p&gt;




&lt;h2&gt;
  
  
  When JavaScript Is the Right Choice
&lt;/h2&gt;

&lt;p&gt;JavaScript is still very relevant, especially in scenarios like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small-scale projects&lt;/li&gt;
&lt;li&gt;Quick prototypes&lt;/li&gt;
&lt;li&gt;Learning programming fundamentals&lt;/li&gt;
&lt;li&gt;Simple scripts or utilities&lt;/li&gt;
&lt;li&gt;Projects with no long-term maintenance requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these cases, simplicity and speed matter more than strict structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&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;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&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="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&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="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;World&lt;/span&gt;&lt;span class="dl"&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 is clean, fast, and requires no additional setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  When TypeScript Becomes Valuable
&lt;/h2&gt;

&lt;p&gt;TypeScript starts to show its real value in larger or more complex systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scalable applications&lt;/li&gt;
&lt;li&gt;Team-based development&lt;/li&gt;
&lt;li&gt;Production-level software&lt;/li&gt;
&lt;li&gt;Long-term projects requiring maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, TypeScript ensures that only valid data is passed into functions, reducing runtime errors significantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Developers Prefer TypeScript
&lt;/h2&gt;

&lt;p&gt;Over time, TypeScript has become the standard in many production environments because it provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better developer experience with autocomplete and IntelliSense&lt;/li&gt;
&lt;li&gt;Early detection of errors before runtime&lt;/li&gt;
&lt;li&gt;Easier refactoring in large codebases&lt;/li&gt;
&lt;li&gt;More structured and maintainable code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These advantages become more noticeable as a project grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Misconceptions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  “TypeScript slows down development”
&lt;/h3&gt;

&lt;p&gt;Initially, there is a small learning curve. However, in real-world projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Debugging time decreases&lt;/li&gt;
&lt;li&gt;Code becomes more predictable&lt;/li&gt;
&lt;li&gt;Refactoring becomes safer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In most cases, it actually improves productivity over time.&lt;/p&gt;




&lt;h3&gt;
  
  
  “It adds unnecessary complexity”
&lt;/h3&gt;

&lt;p&gt;TypeScript does not require heavy usage from day one.&lt;/p&gt;

&lt;p&gt;It can be adopted gradually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with relaxed compiler settings&lt;/li&gt;
&lt;li&gt;Convert existing files step by step&lt;/li&gt;
&lt;li&gt;Enable stricter rules over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes the transition smooth and manageable.&lt;/p&gt;




&lt;h2&gt;
  
  
  When You Don’t Need TypeScript
&lt;/h2&gt;

&lt;p&gt;TypeScript may not be necessary for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Small experimental projects&lt;/li&gt;
&lt;li&gt;Simple scripts&lt;/li&gt;
&lt;li&gt;Learning JavaScript fundamentals&lt;/li&gt;
&lt;li&gt;One-off applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In these cases, JavaScript is often more than enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;JavaScript is still the foundation of the web and will remain important for a long time.&lt;/p&gt;

&lt;p&gt;However, TypeScript has become the standard choice for modern, scalable applications due to its structure, safety, and maintainability benefits.&lt;/p&gt;

&lt;p&gt;For new production-level projects, TypeScript is usually the more future-proof option.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Instead of choosing one permanently, it’s better to think in terms of context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use JavaScript when simplicity and speed matter&lt;/li&gt;
&lt;li&gt;Use TypeScript when scalability and maintainability matter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both are essential tools — the key is knowing when to use each one.&lt;/p&gt;




&lt;p&gt;💬 What’s your experience? Do you prefer JavaScript flexibility or TypeScript structure in your projects?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Gemini 3.1 Flash TTS for Next.js: ship voice UX in 15 min (2026)</title>
      <dc:creator>BeanBean</dc:creator>
      <pubDate>Tue, 21 Apr 2026 23:00:00 +0000</pubDate>
      <link>https://dev.to/bean_bean/gemini-31-flash-tts-for-nextjs-ship-voice-ux-in-15-min-2026-8fp</link>
      <guid>https://dev.to/bean_bean/gemini-31-flash-tts-for-nextjs-ship-voice-ux-in-15-min-2026-8fp</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://nextfuture.io.vn/blog/gemini-31-flash-tts-for-nextjs-ship-voice-ux-in-15-min-2026" rel="noopener noreferrer"&gt;NextFuture&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What's new this week
&lt;/h2&gt;

&lt;p&gt;On April 15, 2026, Google shipped &lt;a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-3-1-flash-tts/" rel="noopener noreferrer"&gt;Gemini 3.1 Flash TTS&lt;/a&gt; as a public preview on AI Studio and Vertex AI. The model ID is &lt;code&gt;gemini-3.1-flash-tts-preview&lt;/code&gt;, and it introduces 200+ inline audio tags (for example &lt;code&gt;[whispers]&lt;/code&gt;, &lt;code&gt;[happy]&lt;/code&gt;, &lt;code&gt;[pause]&lt;/code&gt;), 30 prebuilt voices, native multi-speaker dialogue, and coverage across 70+ languages. The free tier is open for prototyping; paid usage is $1 per million text input tokens and $20 per million audio output tokens — roughly an order of magnitude cheaper than ElevenLabs at the same run-time. Output is 24 kHz mono PCM, returned inline as base64, so there is no webhook dance and no separate voice-studio account to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters for builders
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Web engineer.&lt;/strong&gt; Previously, adding a "listen to this article" button to a Next.js blog meant wiring ElevenLabs or patching &lt;code&gt;tts-1-hd&lt;/code&gt; with custom SSML to fix prosody. With Flash TTS, a single &lt;code&gt;generateContent&lt;/code&gt; call and one inline &lt;code&gt;[slow]&lt;/code&gt; or &lt;code&gt;[excited]&lt;/code&gt; tag produces the same emotional pacing — no SSML build step, no separate voice studio, and the audio streams back as base64 PCM you can buffer straight into an &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; element. The full call lives in a server action, so API keys stay on the server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI engineer.&lt;/strong&gt; Building a voice agent that reads CRM notes aloud used to need two inference passes (LLM then TTS) plus manual speaker diarization. Flash TTS accepts multi-speaker transcripts inline: your LLM emits &lt;code&gt;Joe: ... Jane: ...&lt;/code&gt; and TTS returns one WAV with two distinct voices. Your agent graph loses a node, latency drops by a full network round-trip, and you skip maintaining a separate speaker-labelling prompt that drifts every model upgrade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Indie maker.&lt;/strong&gt; A Duolingo-style pronunciation app priced at $4/month barely broke even on Azure TTS at roughly $0.05 per lesson. At $20 per million output audio tokens, a 30-second lesson now costs about $0.003 — gross margin holds above 90% on the same $4 tier. Voice is no longer the line item that kills your side project's unit economics, which means TTS-heavy features like audiobook summaries, podcast previews, or accessibility narration finally pencil out on a free-tier SaaS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands-on: try it in under 15 minutes
&lt;/h2&gt;

&lt;p&gt;Grab a free API key from &lt;a href="https://aistudio.google.com" rel="noopener noreferrer"&gt;aistudio.google.com&lt;/a&gt;, store it as &lt;code&gt;GEMINI_API_KEY&lt;/code&gt;, then install the SDK:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @google/genai wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Minimal Node/TypeScript call wrapped as a Next.js 16 server action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenAI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google/genai&lt;/span&gt;&lt;span class="dl"&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;ai&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;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&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;synthesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;voice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Kore&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3.1-flash-tts-preview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;responseModalities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AUDIO&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="na"&gt;speechConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;voiceConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;prebuiltVoiceConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;voiceName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;voice&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;b64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="o"&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="nx"&gt;content&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="o"&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="nx"&gt;inlineData&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&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;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 24kHz mono PCM&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;synthesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Say warmly: [slow] Welcome back, Alex. [happy] You crushed this week.&lt;/span&gt;&lt;span class="dl"&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 inline &lt;code&gt;[slow]&lt;/code&gt; and &lt;code&gt;[happy]&lt;/code&gt; tags steer pacing and emotion mid-sentence — no separate prosody config. Tags must live inside square brackets, separated by text or punctuation: two adjacent tags will error. For a two-person podcast intro via cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-tts-preview:generateContent"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-goog-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$GEMINI_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "contents":[{"parts":[{"text":"TTS between Joe and Jane: Joe: [excited] The feed dropped. Jane: [amused] Took long enough."}]}],
    "generationConfig":{"responseModalities":["AUDIO"]}
  }'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; podcast.wav
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pipe the output through &lt;code&gt;ffmpeg -i in.wav -b:a 64k out.mp3&lt;/code&gt; if you need smaller transfer. Preview rate limits follow Gemini Flash defaults (10 RPM on free, 1,000 RPM on paid) — fine for prototyping. For production, queue synthesis in a BullMQ worker and cache finished clips in S3 or R2 keyed by a hash of &lt;code&gt;text + voice + tagSet&lt;/code&gt;; a cache hit rate above 60% on a changelog feature is common. One caveat: preview model IDs have been renamed twice in the Gemini 3.1 family this quarter, so read the exact ID from an env var rather than hard-coding it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it compares to alternatives
&lt;/h2&gt;

&lt;p&gt;Gemini 3.1 Flash TTSOpenAI gpt-4o-mini-ttsElevenLabs Flash v2.5Starts atFree tier; $1/M text + $20/M audio tokens paid$0.60 per 1M input chars, no free tier$5/mo Starter (30k credits)Best forMultilingual, expressive multi-speaker narrationLow-latency voice replies inside GPT appsCloned brand voices, audiobook productionKey limitPreview only — no SLA, model ID may change before GA~8 voices, fewer expressive tagsPer-character billing scales fast on free-tier SaaSIntegration@google/genai SDK, Vertex AI, REST/cURLOpenAI SDK, streaming WebSocketREST API, WebSocket streaming, native SDK&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it this week
&lt;/h2&gt;

&lt;p&gt;Pick one text-heavy screen in your product — an onboarding intro, a weekly changelog entry, a lesson summary — and wire Flash TTS behind a "Play" button. Ship it behind a feature flag so you can A/B the voice UX on 10% of sessions, then compare time-on-page and replay counts; if replay rate clears 15%, keep it and expand to every long-form page. For wider context on where the Gemini stack sits today, read our &lt;a href="https://nextfuture.io.vn/blog/google-gemma-4-review-2026-the-open-model-that-runs-locally-and-beats-closed-apis" rel="noopener noreferrer"&gt;Gemma 4 review&lt;/a&gt; and the &lt;a href="https://nextfuture.io.vn/blog/the-q1-2026-webai-recap-launches-outages-pricing" rel="noopener noreferrer"&gt;Q1 2026 Web+AI recap&lt;/a&gt; for the pricing shifts since January.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://nextfuture.io.vn" rel="noopener noreferrer"&gt;NextFuture&lt;/a&gt;. Follow us for more fullstack &amp;amp; AI engineering content.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fullstack</category>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building Scalable React Applications in 2026: Best Practices &amp; Patterns</title>
      <dc:creator>Alaa Shamel (Genious)</dc:creator>
      <pubDate>Tue, 21 Apr 2026 22:46:10 +0000</pubDate>
      <link>https://dev.to/alaashamel/building-scalable-react-applications-in-2026-best-practices-patterns-37f0</link>
      <guid>https://dev.to/alaashamel/building-scalable-react-applications-in-2026-best-practices-patterns-37f0</guid>
      <description>&lt;h1&gt;
  
  
  Building Scalable React Applications in 2024: Architecture, Patterns &amp;amp; Real-World Practices
&lt;/h1&gt;

&lt;p&gt;React has become the default choice for modern frontend development. But while building small applications is straightforward, scaling React apps into maintainable, high-performance systems is where real engineering starts.&lt;/p&gt;

&lt;p&gt;Over time, most projects naturally grow in complexity. What starts as a clean structure can quickly turn into something harder to manage.&lt;/p&gt;

&lt;p&gt;Common signs include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Components becoming too large and handling too many responsibilities&lt;/li&gt;
&lt;li&gt;Props being passed through multiple layers (prop drilling)&lt;/li&gt;
&lt;li&gt;State logic scattered across the application&lt;/li&gt;
&lt;li&gt;Increasing difficulty in tracking bugs and side effects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, the issue is no longer React itself — it’s &lt;strong&gt;architecture&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 1. Thinking in Components, Not Pages
&lt;/h2&gt;

&lt;p&gt;One of the biggest shifts when building scalable React applications is moving from “page-based thinking” to “component-based systems”.&lt;/p&gt;

&lt;p&gt;A common mistake is building large, multi-responsibility components that handle everything at once.&lt;/p&gt;

&lt;p&gt;Instead, a better approach is separation of concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI components (pure and reusable)&lt;/li&gt;
&lt;li&gt;Feature components (domain-specific logic)&lt;/li&gt;
&lt;li&gt;Layout components (structure only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, instead of one large dashboard component handling everything, breaking it down into:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UserProfile&lt;/li&gt;
&lt;li&gt;UserPosts&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;leads to a system that is significantly easier to maintain and extend.&lt;/p&gt;

&lt;p&gt;The real benefit isn’t just cleaner code — it’s &lt;strong&gt;predictability and scalability over time&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  🎯 Custom Hooks: Extracting Business Logic
&lt;/h3&gt;

&lt;p&gt;As applications grow, duplicating logic across components becomes inevitable if not handled properly.&lt;/p&gt;

&lt;p&gt;Custom hooks solve this by isolating logic such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data fetching&lt;/li&gt;
&lt;li&gt;Loading and error states&lt;/li&gt;
&lt;li&gt;Side effects&lt;/li&gt;
&lt;li&gt;Reusable stateful behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a clear separation between &lt;strong&gt;what the UI looks like&lt;/strong&gt; and &lt;strong&gt;how the data behaves&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The result is simpler components that focus purely on rendering, while logic becomes reusable and testable.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 2. State Management: Choosing the Right Level of Complexity
&lt;/h2&gt;

&lt;p&gt;Not all state belongs in global management, and over-engineering state is one of the most common mistakes in React applications.&lt;/p&gt;

&lt;p&gt;A practical breakdown looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local UI state → &lt;code&gt;useState&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Complex local logic → &lt;code&gt;useReducer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Shared authentication or small global state → Context API&lt;/li&gt;
&lt;li&gt;Server state and caching → TanStack Query&lt;/li&gt;
&lt;li&gt;Complex global state → Zustand or Redux Toolkit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key principle is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use the simplest solution that correctly solves the problem.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Introducing heavy state management too early often increases complexity without real benefit.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚡ 3. Performance: Designing for Scale, Not Micro-Optimizations
&lt;/h2&gt;

&lt;p&gt;Performance in React is less about aggressive optimization and more about avoiding unnecessary work.&lt;/p&gt;

&lt;p&gt;Some effective techniques include:&lt;/p&gt;

&lt;h3&gt;
  
  
  Memoization (when needed, not everywhere)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useMemo&lt;/code&gt; for expensive computations&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useCallback&lt;/code&gt; for stable function references&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;React.memo&lt;/code&gt; for preventing unnecessary re-renders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, overusing these patterns can add unnecessary complexity. They should be applied only when there is a measurable benefit.&lt;/p&gt;




&lt;h3&gt;
  
  
  📦 Code Splitting and Lazy Loading
&lt;/h3&gt;

&lt;p&gt;One of the most effective performance strategies is reducing the initial bundle size.&lt;/p&gt;

&lt;p&gt;By splitting the application into smaller chunks and loading features on demand, you improve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial load time&lt;/li&gt;
&lt;li&gt;Perceived performance&lt;/li&gt;
&lt;li&gt;Resource efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This becomes especially important in large-scale applications with multiple routes and heavy features.&lt;/p&gt;




&lt;h2&gt;
  
  
  🗂️ 4. Structuring React Projects for Growth
&lt;/h2&gt;

&lt;p&gt;Folder structure plays a much bigger role than many developers realize.&lt;/p&gt;

&lt;p&gt;A feature-based architecture tends to scale more effectively than a type-based one.&lt;/p&gt;

&lt;p&gt;A typical structure might look like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;features/ (auth, dashboard, etc.)&lt;/li&gt;
&lt;li&gt;components/ (shared UI elements)&lt;/li&gt;
&lt;li&gt;hooks/&lt;/li&gt;
&lt;li&gt;services/ (API layer)&lt;/li&gt;
&lt;li&gt;utils/&lt;/li&gt;
&lt;li&gt;types/&lt;/li&gt;
&lt;li&gt;app/ (app initialization and routing)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main idea is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep related logic and UI close together.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This reduces cognitive load and makes onboarding new developers easier.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡️ 5. TypeScript as a Scalability Tool
&lt;/h2&gt;

&lt;p&gt;TypeScript is not just about catching errors — it’s about designing predictable systems.&lt;/p&gt;

&lt;p&gt;It enforces structure in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data models&lt;/li&gt;
&lt;li&gt;Component props&lt;/li&gt;
&lt;li&gt;API responses&lt;/li&gt;
&lt;li&gt;Shared logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a project grows, this predictability becomes increasingly valuable, especially in teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 6. Testing: Building Confidence in Change
&lt;/h2&gt;

&lt;p&gt;In scalable applications, change is constant. Without tests, every modification becomes risky.&lt;/p&gt;

&lt;p&gt;A practical testing strategy includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component testing for UI correctness&lt;/li&gt;
&lt;li&gt;Integration testing for feature behavior&lt;/li&gt;
&lt;li&gt;Regression testing for critical flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Testing is not about coverage numbers — it’s about &lt;strong&gt;confidence when shipping changes&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔑 Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Scaling React applications is less about mastering every library and more about making consistent architectural decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer composition over large components&lt;/li&gt;
&lt;li&gt;Extract logic into reusable hooks&lt;/li&gt;
&lt;li&gt;Choose state management based on actual needs&lt;/li&gt;
&lt;li&gt;Optimize performance intentionally, not blindly&lt;/li&gt;
&lt;li&gt;Structure projects around features, not file types&lt;/li&gt;
&lt;li&gt;Use TypeScript to enforce predictable systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Final Thought
&lt;/h2&gt;

&lt;p&gt;React does not enforce architecture — it leaves it to the developer.&lt;/p&gt;

&lt;p&gt;That flexibility is powerful, but also dangerous if not guided by clear principles.&lt;/p&gt;

&lt;p&gt;Scalable applications are not built by writing perfect code from day one, but by continuously making &lt;strong&gt;better structural decisions as the system evolves&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;💬 I’d be interested to hear — what architectural patterns have made the biggest difference in your React projects?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>react</category>
    </item>
    <item>
      <title>The Ultimate Guide To Buying Old Yahoo Accounts In</title>
      <dc:creator>perribeaud </dc:creator>
      <pubDate>Tue, 21 Apr 2026 22:24:11 +0000</pubDate>
      <link>https://dev.to/perribeaudoin70sgm/the-ultimate-guide-to-buying-old-yahoo-accounts-in-3gm8</link>
      <guid>https://dev.to/perribeaudoin70sgm/the-ultimate-guide-to-buying-old-yahoo-accounts-in-3gm8</guid>
      <description>&lt;p&gt;Introduction&lt;br&gt;
⚡ 24/7 Fast Response Support&lt;br&gt;
💬🔷 Telegram: &lt;a class="mentioned-user" href="https://dev.to/getusasmm"&gt;@getusasmm&lt;/a&gt;&lt;br&gt;
📧💠 Email: &lt;a href="mailto:getusasmm@gmail.com"&gt;getusasmm@gmail.com&lt;/a&gt;&lt;br&gt;
🟣💎 Discord: Getusasmm&lt;br&gt;
🌍🔥 Visit Our Platform:&lt;br&gt;
&lt;a href="https://getusasmm.com/product/buy-old-usa-yahoo-accounts/" rel="noopener noreferrer"&gt;https://getusasmm.com/product/buy-old-usa-yahoo-accounts/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In today’s digital economy, email remains one of the most powerful communication tools for businesses, freelancers, and marketers. Whether you are targeting the USA market or building a global audience, having a strong email system is essential for trust, deliverability, and long-term success.&lt;/p&gt;

&lt;p&gt;Many people search for “old USA Yahoo accounts” believing that aged accounts provide better performance or marketing advantages. However, this is a misconception. Email success does not depend on account age or origin—it depends on how the account is created, secured, and used over time.&lt;/p&gt;

&lt;p&gt;This guide explains how to build a strong, secure, and professional email system that performs better than any pre-owned or aged account.&lt;/p&gt;

&lt;p&gt;Why Email Is Important for USA Business Targeting&lt;br&gt;
The USA market is one of the most competitive and high-value digital markets in the world. Email plays a key role in:&lt;/p&gt;

&lt;p&gt;Business communication&lt;br&gt;
Client outreach&lt;br&gt;
Freelance work&lt;br&gt;
Digital marketing campaigns&lt;br&gt;
E-commerce operations&lt;br&gt;
Professionally managed email accounts help build trust with US clients and improve response rates.&lt;/p&gt;

&lt;p&gt;The Myth of “Old USA Yahoo Accounts”&lt;br&gt;
Many online sellers claim that old or USA-based Yahoo accounts are better for marketing or trust. This is not true.&lt;/p&gt;

&lt;p&gt;Common Myths:&lt;br&gt;
⚡ 24/7 Fast Response Support&lt;br&gt;
💬🔷 Telegram: &lt;a class="mentioned-user" href="https://dev.to/getusasmm"&gt;@getusasmm&lt;/a&gt;&lt;br&gt;
📧💠 Email: &lt;a href="mailto:getusasmm@gmail.com"&gt;getusasmm@gmail.com&lt;/a&gt;&lt;br&gt;
🟣💎 Discord: Getusasmm&lt;br&gt;
🌍🔥 Visit Our Platform:&lt;br&gt;
&lt;a href="https://getusasmm.com/product/buy-old-usa-yahoo-accounts/" rel="noopener noreferrer"&gt;https://getusasmm.com/product/buy-old-usa-yahoo-accounts/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Old accounts have higher trust&lt;br&gt;
USA-based accounts improve deliverability&lt;br&gt;
Aged accounts avoid spam filters&lt;br&gt;
Reality:&lt;br&gt;
Email performance depends on:&lt;/p&gt;

&lt;p&gt;Sending behavior&lt;br&gt;
Security settings&lt;br&gt;
Engagement quality&lt;br&gt;
Reputation history&lt;br&gt;
Platform trust signals&lt;br&gt;
Not on account age or geographic origin.&lt;/p&gt;

&lt;p&gt;Risks of Using Pre-Owned Email Accounts&lt;br&gt;
Using accounts from unknown sources creates serious risks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Security Issues&lt;br&gt;
You do not control the original recovery information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Account Recovery Risk&lt;br&gt;
The original owner may reclaim access anytime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Policy Violations&lt;br&gt;
May violate Yahoo and other email providers’ terms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Business Damage&lt;br&gt;
Loss of clients, communication, and platform access.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Safe Alternative: Create Your Own Email Accounts&lt;br&gt;
The best strategy is to create your own accounts and build trust naturally.&lt;/p&gt;

&lt;p&gt;Step-by-Step Setup:&lt;br&gt;
Visit official Yahoo or Gmail signup page&lt;br&gt;
Enter real and consistent identity information&lt;br&gt;
Choose a professional username&lt;br&gt;
Create a strong password&lt;br&gt;
Verify phone number&lt;br&gt;
Add recovery email&lt;br&gt;
Complete setup securely&lt;br&gt;
How to Target USA Market with Email Accounts&lt;br&gt;
⚡ 24/7 Fast Response Support&lt;br&gt;
💬🔷 Telegram: &lt;a class="mentioned-user" href="https://dev.to/getusasmm"&gt;@getusasmm&lt;/a&gt;&lt;br&gt;
📧💠 Email: &lt;a href="mailto:getusasmm@gmail.com"&gt;getusasmm@gmail.com&lt;/a&gt;&lt;br&gt;
🟣💎 Discord: Getusasmm&lt;br&gt;
🌍🔥 Visit Our Platform:&lt;br&gt;
&lt;a href="https://getusasmm.com/product/buy-old-usa-yahoo-accounts/" rel="noopener noreferrer"&gt;https://getusasmm.com/product/buy-old-usa-yahoo-accounts/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of buying accounts, you can optimize your setup for USA targeting.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Language Setup
Use English (US) language settings
Write emails in professional English&lt;/li&gt;
&lt;li&gt;Time Zone Alignment
Set system time to USA timezone if needed&lt;/li&gt;
&lt;li&gt;Professional Identity
Use clean, brand-focused email names
Example:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="mailto:john.business@gmail.com"&gt;john.business@gmail.com&lt;/a&gt;&lt;br&gt;
&lt;a href="mailto:usa.marketing.team@yahoo.com"&gt;usa.marketing.team@yahoo.com&lt;/a&gt;&lt;br&gt;
Building Email Trust (Most Important Factor)&lt;br&gt;
Trust is built through usage, not age.&lt;/p&gt;

&lt;p&gt;Key Trust Signals:&lt;br&gt;
Consistent login behavior&lt;br&gt;
Verified phone/email recovery&lt;br&gt;
Low spam activity&lt;br&gt;
Gradual sending patterns&lt;br&gt;
Engagement with real users&lt;br&gt;
Security Setup for Long-Term Use&lt;br&gt;
To protect your accounts:&lt;/p&gt;

&lt;p&gt;Enable:&lt;br&gt;
Two-factor authentication&lt;br&gt;
Login alerts&lt;br&gt;
Recovery email and phone&lt;br&gt;
Strong password system&lt;br&gt;
Best Practices:&lt;br&gt;
Avoid sharing login details&lt;br&gt;
Monitor account activity regularly&lt;br&gt;
Avoid suspicious third-party tools&lt;br&gt;
Email Warm-Up Strategy&lt;br&gt;
⚡ 24/7 Fast Response Support&lt;br&gt;
💬🔷 Telegram: &lt;a class="mentioned-user" href="https://dev.to/getusasmm"&gt;@getusasmm&lt;/a&gt;&lt;br&gt;
📧💠 Email: &lt;a href="mailto:getusasmm@gmail.com"&gt;getusasmm@gmail.com&lt;/a&gt;&lt;br&gt;
🟣💎 Discord: Getusasmm&lt;br&gt;
🌍🔥 Visit Our Platform:&lt;br&gt;
&lt;a href="https://getusasmm.com/product/buy-old-usa-yahoo-accounts/" rel="noopener noreferrer"&gt;https://getusasmm.com/product/buy-old-usa-yahoo-accounts/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;New accounts should not be used aggressively.&lt;/p&gt;

&lt;p&gt;Proper Warm-Up Steps:&lt;br&gt;
Start with personal emails&lt;br&gt;
Send limited daily messages&lt;br&gt;
Gradually increase activity&lt;br&gt;
Avoid bulk marketing at the beginning&lt;br&gt;
Business Use Cases for Email Accounts&lt;br&gt;
Properly managed accounts can be used for:&lt;/p&gt;

&lt;p&gt;Freelancing&lt;br&gt;
Upwork&lt;br&gt;
Fiverr&lt;br&gt;
Email Marketing&lt;br&gt;
Outreach campaigns&lt;br&gt;
Lead generation&lt;br&gt;
Client follow-ups&lt;br&gt;
Customer Support&lt;br&gt;
Service communication&lt;br&gt;
Order updates&lt;br&gt;
Client assistance&lt;br&gt;
Professional Upgrade Option&lt;br&gt;
For better branding and trust, consider:&lt;/p&gt;

&lt;p&gt;👉 Google Workspace&lt;/p&gt;

&lt;p&gt;Benefits:&lt;br&gt;
Custom domain email addresses&lt;br&gt;
Higher credibility with USA clients&lt;br&gt;
Better deliverability&lt;br&gt;
Team collaboration tools&lt;br&gt;
Common Mistakes to Avoid&lt;br&gt;
Buying or using unknown accounts&lt;br&gt;
Using fake identity details&lt;br&gt;
Sending bulk emails too early&lt;br&gt;
Ignoring security alerts&lt;br&gt;
Logging in from multiple suspicious locations&lt;br&gt;
Long-Term Email Strategy&lt;br&gt;
⚡ 24/7 Fast Response Support&lt;br&gt;
💬🔷 Telegram: &lt;a class="mentioned-user" href="https://dev.to/getusasmm"&gt;@getusasmm&lt;/a&gt;&lt;br&gt;
📧💠 Email: &lt;a href="mailto:getusasmm@gmail.com"&gt;getusasmm@gmail.com&lt;/a&gt;&lt;br&gt;
🟣💎 Discord: Getusasmm&lt;br&gt;
🌍🔥 Visit Our Platform:&lt;br&gt;
&lt;a href="https://getusasmm.com/product/buy-old-usa-yahoo-accounts/" rel="noopener noreferrer"&gt;https://getusasmm.com/product/buy-old-usa-yahoo-accounts/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Success in email marketing comes from consistency:&lt;/p&gt;

&lt;p&gt;Build accounts from scratch&lt;br&gt;
Maintain proper security&lt;br&gt;
Use ethical communication methods&lt;br&gt;
Scale gradually over time&lt;br&gt;
Why Building Your Own Accounts Is Better&lt;br&gt;
Self-created accounts offer:&lt;/p&gt;

&lt;p&gt;Full ownership control&lt;br&gt;
Stable access&lt;br&gt;
Stronger security&lt;br&gt;
Better long-term performance&lt;br&gt;
No risk of recovery loss&lt;br&gt;
Future of Email Communication&lt;br&gt;
Email platforms are evolving with AI and automation:&lt;/p&gt;

&lt;p&gt;Smart spam filtering&lt;br&gt;
AI-generated replies&lt;br&gt;
Behavioral trust scoring&lt;br&gt;
Advanced security monitoring&lt;br&gt;
This means quality usage matters more than account age or origin.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;br&gt;
⚡ 24/7 Fast Response Support&lt;br&gt;
💬🔷 Telegram: &lt;a class="mentioned-user" href="https://dev.to/getusasmm"&gt;@getusasmm&lt;/a&gt;&lt;br&gt;
📧💠 Email: &lt;a href="mailto:getusasmm@gmail.com"&gt;getusasmm@gmail.com&lt;/a&gt;&lt;br&gt;
🟣💎 Discord: Getusasmm&lt;br&gt;
🌍🔥 Visit Our Platform:&lt;br&gt;
&lt;a href="https://getusasmm.com/product/buy-old-usa-yahoo-accounts/" rel="noopener noreferrer"&gt;https://getusasmm.com/product/buy-old-usa-yahoo-accounts/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea that “old USA Yahoo accounts” are better for marketing or business is outdated and risky. Real success comes from building secure, properly managed email accounts and using them consistently.&lt;/p&gt;

&lt;p&gt;By creating your own accounts and maintaining good practices, you can achieve:&lt;/p&gt;

&lt;p&gt;Strong security&lt;br&gt;
Reliable communication&lt;br&gt;
Better business trust&lt;br&gt;
Long-term growth&lt;br&gt;
A properly managed email system is far more powerful than any purchased or aged account.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>machinelearning</category>
      <category>aws</category>
      <category>testing</category>
    </item>
    <item>
      <title>Technical Analysis: JavaScript Weekly Issue 782</title>
      <dc:creator>Norvik Tech</dc:creator>
      <pubDate>Tue, 21 Apr 2026 22:09:42 +0000</pubDate>
      <link>https://dev.to/norviktech/technical-analysis-javascript-weekly-issue-782-g49</link>
      <guid>https://dev.to/norviktech/technical-analysis-javascript-weekly-issue-782-g49</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Originally published at &lt;a href="https://norvik.tech/en/news/analisis-javascript-weekly-782-apr-2026" rel="noopener noreferrer"&gt;norvik.tech&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In-depth analysis of JavaScript Weekly Issue 782, highlighting key updates and their implications for web development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed in JavaScript Weekly Issue 782
&lt;/h2&gt;

&lt;p&gt;The latest issue of JavaScript Weekly sheds light on critical updates relevant for web developers. It highlights advancements in frameworks like React, emphasizing performance enhancements and new features. This issue serves as a reminder of the rapid evolution within the JavaScript ecosystem, making it imperative for teams to stay informed and adapt accordingly. The inclusion of community contributions further enriches the content, providing diverse perspectives on technology adoption.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Focus on performance improvements&lt;/li&gt;
&lt;li&gt;Insights into upcoming features&lt;/li&gt;
&lt;li&gt;Community feedback shaping the landscape&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implications for Web Development Practices
&lt;/h2&gt;

&lt;p&gt;Understanding the implications of these updates is crucial for developers aiming to optimize their applications. The introduction of new features in frameworks can directly impact development speed and application efficiency. Teams should assess how these changes might influence their existing projects and consider integrating new tools that enhance performance. Keeping abreast of these updates ensures that developers can leverage best practices and avoid pitfalls associated with outdated methodologies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Evaluate the relevance of new features&lt;/li&gt;
&lt;li&gt;Assess potential integration challenges&lt;/li&gt;
&lt;li&gt;Stay updated to avoid performance regressions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Actionable Steps for Teams Moving Forward
&lt;/h2&gt;

&lt;p&gt;As teams prepare to integrate these updates, a structured approach is recommended. Start by reviewing current projects to identify areas where new features can be implemented. Schedule training sessions to familiarize team members with upcoming changes in frameworks. Additionally, establish a feedback loop with the community to share insights and experiences regarding these updates. By actively engaging with the latest trends, teams can maintain a competitive edge in the rapidly evolving landscape of web development.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Review projects for potential updates&lt;/li&gt;
&lt;li&gt;Train team members on new features&lt;/li&gt;
&lt;li&gt;Engage with community feedback&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Need Custom Software Solutions?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Norvik Tech&lt;/strong&gt; builds high-impact software for businesses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;development&lt;/li&gt;
&lt;li&gt;consulting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;a href="https://norvik.tech" rel="noopener noreferrer"&gt;Visit norvik.tech&lt;/a&gt; to schedule a free consultation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>60+ Free Website Templates &amp; UI Components You Need in 2026 (Dark Mode, Glassmorphism &amp; More)</title>
      <dc:creator>Imran khan</dc:creator>
      <pubDate>Tue, 21 Apr 2026 22:07:36 +0000</pubDate>
      <link>https://dev.to/proofmatcher/60-free-website-templates-ui-components-you-need-in-2026-dark-mode-glassmorphism-more-1d1j</link>
      <guid>https://dev.to/proofmatcher/60-free-website-templates-ui-components-you-need-in-2026-dark-mode-glassmorphism-more-1d1j</guid>
      <description>&lt;p&gt;If you're looking for &lt;strong&gt;free website templates&lt;/strong&gt;, &lt;strong&gt;CSS UI components&lt;/strong&gt;, or &lt;strong&gt;dark mode designs&lt;/strong&gt; for your next project, this curated list has everything you need for 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Free Website Template Resources
&lt;/h2&gt;

&lt;p&gt;Finding high-quality &lt;strong&gt;free HTML templates&lt;/strong&gt; and &lt;strong&gt;free landing page templates&lt;/strong&gt; has never been easier. Here are the top resources:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. ProofMatcher - Premium &amp;amp; Free Templates Marketplace
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://proofmatcher.com" rel="noopener noreferrer"&gt;ProofMatcher&lt;/a&gt; is one of the best places to find production-ready website templates including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SaaS landing page templates&lt;/strong&gt; with dark mode support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React UI components&lt;/strong&gt; and &lt;strong&gt;CSS UI components&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glassmorphism UI templates&lt;/strong&gt; and &lt;strong&gt;neumorphism UI templates&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard UI templates&lt;/strong&gt; (free and premium)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agency website templates&lt;/strong&gt; and &lt;strong&gt;startup landing page templates&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Their templates include modern design trends like &lt;strong&gt;bento grid CSS&lt;/strong&gt;, &lt;strong&gt;CSS card components&lt;/strong&gt;, &lt;strong&gt;CSS button styles&lt;/strong&gt;, and &lt;strong&gt;CSS navbar templates&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://proofmatcher.com/templates" rel="noopener noreferrer"&gt;Browse Free Templates on ProofMatcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://proofmatcher.com/blogs" rel="noopener noreferrer"&gt;Read their Web Design Blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Web Design Trends 2026
&lt;/h2&gt;

&lt;p&gt;The biggest &lt;strong&gt;web design trends 2026&lt;/strong&gt; include:&lt;/p&gt;

&lt;h3&gt;
  
  
  Glassmorphism CSS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Glassmorphism&lt;/strong&gt; uses frosted-glass effects with &lt;code&gt;backdrop-filter: blur()&lt;/code&gt;. It's dominating SaaS and fintech designs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.glass-card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;backdrop-filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;blur&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&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;
  
  
  Dark Mode Website Templates
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dark mode UI design&lt;/strong&gt; isn't just a trend - it's now an expectation. The best &lt;strong&gt;dark mode template free download&lt;/strong&gt; options use near-black backgrounds (not pure #000000) with subtle borders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--bg-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#050508&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--text-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f4f4f5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.06&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#dc2626&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;
  
  
  Bento Grid CSS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bento grid&lt;/strong&gt; layouts (inspired by Apple's grid-based marketing pages) are huge in 2026:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.bento-grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auto-fit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;minmax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;300px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&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;
  
  
  Best Free Templates by Category
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SaaS Website Templates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SaaS homepage design&lt;/strong&gt;: Clean hero, feature grids, pricing tables&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SaaS pricing page design&lt;/strong&gt;: Toggle annual/monthly, highlighted plan&lt;/li&gt;
&lt;li&gt;Best source: &lt;a href="https://proofmatcher.com/templates" rel="noopener noreferrer"&gt;ProofMatcher SaaS Templates&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Portfolio Website Templates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free portfolio website template&lt;/strong&gt; - one-page, minimal, fast-loading&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer portfolio template free&lt;/strong&gt; - dark mode, code editor aesthetic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free React templates&lt;/strong&gt; for Next.js and Vite portfolios&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Dashboard UI Templates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free admin dashboard template HTML&lt;/strong&gt; with sidebar navigation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dashboard UI template free&lt;/strong&gt; - dark/light mode toggle included&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CSS Components (Free)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CSS toggle switch&lt;/strong&gt; - pure CSS, no JavaScript needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS pricing table&lt;/strong&gt; - with hover effects and gradient accents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS hero section&lt;/strong&gt; - full-viewport with animated background&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS flexbox examples&lt;/strong&gt; - responsive grid layouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS animation examples&lt;/strong&gt; - fade, slide, zoom micro-animations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is glassmorphism?&lt;/strong&gt;&lt;br&gt;
Glassmorphism is a UI design style using frosted glass effects - semi-transparent backgrounds with blur, subtle borders, and layered depth. It's popular for cards, modals, and navbars.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best shadcn UI alternatives?&lt;/strong&gt;&lt;br&gt;
For 2026, top shadcn alternatives include Radix UI, Headless UI, and template libraries like ProofMatcher that offer complete page templates instead of just components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Free Next.js templates?&lt;/strong&gt;&lt;br&gt;
Look for Next.js-compatible templates on GitHub and ProofMatcher. Search for "free Next.js templates" and "Tailwind CSS templates free" for modern options.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Tailwind CSS templates free?&lt;/strong&gt;&lt;br&gt;
Many resources offer free Tailwind starters. ProofMatcher offers both Tailwind and pure CSS options for maximum flexibility.&lt;/p&gt;
&lt;h2&gt;
  
  
  Micro Animation Examples CSS
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Micro animations&lt;/strong&gt; improve UX dramatically. Here are CSS examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* Hover lift effect */&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-4px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;box-shadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt; &lt;span class="m"&gt;40px&lt;/span&gt; &lt;span class="n"&gt;rgba&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="m"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;0.3s&lt;/span&gt; &lt;span class="n"&gt;cubic-bezier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0.4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Fade in on scroll */&lt;/span&gt;
&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;fadeInUp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Whether you need a &lt;strong&gt;free SaaS UI kit&lt;/strong&gt;, &lt;strong&gt;mobile-first website template&lt;/strong&gt;, &lt;strong&gt;parallax website template&lt;/strong&gt;, or &lt;strong&gt;AI website template&lt;/strong&gt; - the resources above have you covered.&lt;/p&gt;

&lt;p&gt;For the most premium options in 2026, &lt;a href="https://proofmatcher.com" rel="noopener noreferrer"&gt;ProofMatcher&lt;/a&gt; offers handcrafted templates for startups, agencies, fintech, healthcare, and SaaS businesses - many with &lt;strong&gt;conversion rate optimization&lt;/strong&gt; principles built in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Related reads:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://proofmatcher.com/blogs" rel="noopener noreferrer"&gt;ProofMatcher Blog - Web Design Guides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Explore templates: &lt;a href="https://proofmatcher.com/templates" rel="noopener noreferrer"&gt;proofmatcher.com/templates&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>javascript</category>
      <category>html</category>
    </item>
  </channel>
</rss>
