<?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: Tan Phan</title>
    <description>The latest articles on DEV Community by Tan Phan (@tan_phan_eb99b9731a38b2c3).</description>
    <link>https://dev.to/tan_phan_eb99b9731a38b2c3</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3898137%2Ff97f4d81-b6d7-4461-8061-bd2a2d86c10a.jpg</url>
      <title>DEV Community: Tan Phan</title>
      <link>https://dev.to/tan_phan_eb99b9731a38b2c3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tan_phan_eb99b9731a38b2c3"/>
    <language>en</language>
    <item>
      <title>How I Fixed a 42/100 Lighthouse Score on a B2B 3D WebGL Landing Page (and Open-Sourced the Fix)</title>
      <dc:creator>Tan Phan</dc:creator>
      <pubDate>Sun, 26 Apr 2026 00:40:25 +0000</pubDate>
      <link>https://dev.to/tan_phan_eb99b9731a38b2c3/how-i-fixed-a-42100-lighthouse-score-on-a-b2b-3d-webgl-landing-page-and-open-sourced-the-fix-e0f</link>
      <guid>https://dev.to/tan_phan_eb99b9731a38b2c3/how-i-fixed-a-42100-lighthouse-score-on-a-b2b-3d-webgl-landing-page-and-open-sourced-the-fix-e0f</guid>
      <description>&lt;p&gt;A few weeks ago, I was building a B2B landing page that embedded an industrial 3D model using &lt;code&gt;&amp;lt;model-viewer&amp;gt;&lt;/code&gt;. The page looked fantastic. Then I ran Lighthouse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 42/100.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are the silent killers I found - and what made them non-obvious:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Font Weight Trap (8 requests instead of 3)
&lt;/h2&gt;

&lt;p&gt;Google Fonts was loading &lt;code&gt;wght@300;400;500;600;700;800&lt;/code&gt;. That's 6 weights for Inter plus 2 for JetBrains Mono = &lt;strong&gt;8 separate HTTP requests just for fonts&lt;/strong&gt;.&lt;br&gt;
Each one is render-blocking. &lt;br&gt;
&lt;strong&gt;The fix:&lt;/strong&gt; Reduce to exactly the weights used (&lt;code&gt;400;600;700&lt;/code&gt;). Saved ~400ms.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. &lt;code&gt;&amp;lt;model-viewer&amp;gt;&lt;/code&gt; Blocking the Main Thread
&lt;/h2&gt;

&lt;p&gt;The WebGL script was 300KB. Sitting right in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;strong&gt;The fix:&lt;/strong&gt; Move it to the very end of the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;. Use &lt;code&gt;type="module"&lt;/code&gt; so the browser defers it automatically.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. The Silent SEO Killer: JavaScript in JSON-LD
&lt;/h2&gt;

&lt;p&gt;This was the nastiest bug.&lt;br&gt;
A background animation (Particle.js) was running on the page. Due to a weird timing issue, it injected callback code directly into my &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; block. &lt;br&gt;
The page looked perfect visually. No console errors. But the JSON-LD was now invalid. Google Search Console quietly dropped my rich snippets for 3 months before I noticed.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Open-Source Fix: &lt;code&gt;web-performance-surgeon&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I got tired of hunting these issues manually, so I built a Python CLI that scans your HTML and auto-fixes all of them in one go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Audit only - shows all issues&lt;/span&gt;
python web_performance_surgeon.py index.html

&lt;span class="c"&gt;# Audit + auto-fix everything&lt;/span&gt;
python web_performance_surgeon.py index.html &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final score after fixes: &lt;strong&gt;100/100 Lighthouse&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I've open-sourced this tool along with my entire B2B web toolchain (including a JSON-LD guardian that specifically catches the injection bug):&lt;br&gt;
&lt;a href="https://github.com/tanphan1105/web-performance-surgeon" rel="noopener noreferrer"&gt;GitHub: tanphan1105/web-performance-surgeon&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Has anyone else run into that JSON-LD corruption bug? It's terrifying how silently it fails.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>performance</category>
      <category>python</category>
      <category>seo</category>
    </item>
    <item>
      <title>How I Found and Fixed 5 Critical Lighthouse Issues in a WebGL 3D Landing Page (With a Python CLI)</title>
      <dc:creator>Tan Phan</dc:creator>
      <pubDate>Sun, 26 Apr 2026 00:07:34 +0000</pubDate>
      <link>https://dev.to/tan_phan_eb99b9731a38b2c3/how-i-found-and-fixed-5-critical-lighthouse-issues-in-a-webgl-3d-landing-page-with-a-python-cli-282o</link>
      <guid>https://dev.to/tan_phan_eb99b9731a38b2c3/how-i-found-and-fixed-5-critical-lighthouse-issues-in-a-webgl-3d-landing-page-with-a-python-cli-282o</guid>
      <description>&lt;p&gt;A few months ago, I was building a B2B landing page that embedded a 3D industrial model using . The page looked great visually. Then I ran Lighthouse.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Score: 42/100.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are the 5 issues I found -- and what made each one non-obvious:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Too Many Font Weights (8 requests instead of 3)
&lt;/h2&gt;

&lt;p&gt;Google Fonts was loading wght@300;400;500;600;700;800 -- 6 weights for Inter plus 2 for JetBrains Mono = &lt;strong&gt;8 separate HTTP requests just for fonts&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each one is render-blocking. The fix: reduce to wght@400;600;700. Saved ~400ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  2.  Script in  (Render-Blocking)
&lt;/h2&gt;

&lt;p&gt;The WebGL script was 300KB. Sitting in &lt;/p&gt;. Blocking everything.

&lt;p&gt;Fix: Move it to the end of &lt;/p&gt;. Use type="module" so it defers automatically.&lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Before (in &amp;lt;head&amp;gt;) --&amp;gt;&lt;/span&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;"model-viewer.min.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- After (end of &amp;lt;body&amp;gt;) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"model-viewer.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;h2&gt;
  
  
  3. Missing preconnect and dns-prefetch
&lt;/h2&gt;

&lt;p&gt;The page used Google Fonts, a QR code API, and the model-viewer CDN -- but had zero preconnect tags. Each domain needed a full DNS lookup + TLS handshake before any resource could load.&lt;/p&gt;

&lt;p&gt;Fix: Add preconnect for all external domains in &lt;/p&gt;.
&lt;h2&gt;
  
  
  4. Hero Image Set to loading="lazy"
&lt;/h2&gt;

&lt;p&gt;Someone (me) had applied loading="lazy" to ALL images including the hero image at the top of the page. The browser would defer loading the most important image -- the one that determines LCP.&lt;/p&gt;

&lt;p&gt;Fix: Hero image gets loading="eager" and fetchpriority="high". Everything below the fold stays lazy.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. The Silent SEO Killer: JavaScript Injected Into JSON-LD
&lt;/h2&gt;

&lt;p&gt;This one is the nastiest bug.&lt;/p&gt;

&lt;p&gt;A Particle.js animation was running on the page. Due to a timing issue, it appended animation callback code into a  block. The page looked perfect in the browser. No console errors.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;But the JSON-LD was now invalid. Google Search Console quietly stopped indexing the Product rich snippet. I only found out 3 months later when I ran a structured data audit.&amp;lt;/p&amp;gt;

&amp;lt;hr&amp;gt;
&amp;lt;h2&amp;gt;
  &amp;lt;a name="the-fix-webperformancesurgeon" href="#the-fix-webperformancesurgeon" class="anchor"&amp;gt;
  &amp;lt;/a&amp;gt;
  The Fix: web-performance-surgeon
&amp;lt;/h2&amp;gt;

&amp;lt;p&amp;gt;I got tired of hunting these issues manually, so I built a Python CLI that scans and auto-fixes all of them:&amp;lt;br&amp;gt;
&amp;lt;/p&amp;gt;
&amp;lt;div class="highlight"&amp;gt;&amp;lt;pre class="highlight shell"&amp;gt;&amp;lt;code&amp;gt;&amp;lt;span class="c"&amp;gt;# Audit only -- shows all issues&amp;lt;/span&amp;gt;
python web_performance_surgeon.py index.html

&amp;lt;span class="c"&amp;gt;# Audit + auto-fix everything&amp;lt;/span&amp;gt;
python web_performance_surgeon.py index.html &amp;lt;span class="nt"&amp;gt;--fix&amp;lt;/span&amp;gt;
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;Final score after fixes: &amp;lt;strong&amp;gt;98/100 Lighthouse&amp;lt;/strong&amp;gt;.&amp;lt;/p&amp;gt;

&amp;lt;p&amp;gt;I also built companion tools for the other issues:&amp;lt;/p&amp;gt;

&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;json-ld-schema-guardian&amp;lt;/strong&amp;gt; -- specifically scans for JS injection into JSON-LD blocks&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;static-dom-surgeon&amp;lt;/strong&amp;gt; -- surgically patches HTML elements at scale (used BeautifulSoup to patch 21 product cards without touching surrounding code)&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;strong&amp;gt;b2b-landing-page-checklist&amp;lt;/strong&amp;gt; -- 50-point pre-launch checklist with all the lessons from this project&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;

&amp;lt;p&amp;gt;All free and open source: &amp;lt;a href="https://github.com/tanphan1105"&amp;gt;github.com/tanphan1105&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;

&amp;lt;hr&amp;gt;

&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;Have you run into the JSON-LD injection bug before? Curious if this is more common than I think.&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;
&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>python</category>
      <category>seo</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
