LogoTRUONG PHAM
Home
Projects
Blogs
YouTube
Contact

Newsletter

Stay updated with technical artifacts and engineering insights.

LogoTRUONG PHAM

Building scalable software and sharing insights on technology & life.

Sitemap

  • Home
  • Projects
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back
Blog #17: Images vs JS – The battle for priority in the Critical Path
50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

Blog #17: Images vs JS – The battle for priority in the Critical Path

Why optimize JS down to a few dozen KB and the web still loads slowly? A lesson in viewing system resources holistically.

TP
Truong PhamSoftware Engineer
PublishedApril 25, 2024

"Why do I have to wait 5 seconds to see the Hero image, when the buttons appeared long ago but clicking them does nothing?"

This is a typical user complaint when accessing a "half-optimized" website. We are often too obsessed with reducing JavaScript (JS) file size, pushing from 500KB down to 100KB, but forget that the browser system is an extremely complex resource-regulating machine. The problem doesn't lie in the file size of a single component, but in Resource Contention across the entire system.

1. Overall Architecture: The Enemy of Visualization

For a web page to appear, the browser must go through the Critical Rendering Path. In this flow, JS and Images play two completely different roles:

  • JS: Occupies the CPU for parsing and execution. It is a "blocking intruder."
  • Images: Occupies Bandwidth and GPU for download and decoding.

If you send too many heavy JS files from the start, the CPU will be overloaded, making the browser have no "mind" left to decode images. Conversely, if you have Hero images that are too large without priority attributes, they will compete for bandwidth with important JS files needed to make the page interactive.

2. Analysis of Data Flow & Resource Priority

Look at how the system coordinates:

  • Data Flow: Server → Browser Buffer → Network Stack → Main Thread (JS) / GPU (Images).
  • Contention: If the Network Stack is choked by an unnecessary 2MB image, an important main.js file might be delayed.
<!-- Error: Not guiding the system -->
<img src="hero.jpg" /> <!-- Browser guesses priority -->
<script src="app.js"></script> <!-- Blocks render if no async/defer -->

3. Quick Fix vs. Architectural Refactoring

Quick Fix: Compress images heavily and use loading="lazy". This is a common method, but it only solves the symptom. It does nothing for images that are "above the fold."

Architectural Refactoring (Architectural Fix): Use Resource Hinting and Priority Hints. Instead of letting the browser guess, we proactively coordinate the system's data flow:

  • Use fetchpriority="high" for Hero images.
  • Use rel="preload" for critical fonts.
  • Fragment JS (Code splitting) so the CPU only executes what's truly needed for the first display.

4. Technical Debt and Self-Reflection

Technical debt here is our "drifting" of control over the browser. We believe the browser is smart enough to handle everything. But in reality, every project has a different display structure. Lack of a Resource Loading Policy will make the project increasingly chaotic as it grows.

I wonder: "Am I over-engineering by trying to optimize every bit of JS when just changing the image format to WebP could save 1 second of load time?". Sometimes, we choose the hard way (optimizing code) because we are programmers, while the most effective solution lies at the infrastructure layer (Assets/CDN).

Lesson Learned

A Frontend system is a fragile balance between CPU, Bandwidth, and Memory. Optimizing one component without looking at the whole often leads to the opposite result. Don't just be a JS programmer; be a "Browser Engineer"—someone who understands how the gears of resources fit together.


Notes on the trade-off between bytecode and pixels.

Series • Part 17 of 50

50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

NextBlog #18: Laggy Animations – When the touch is no longer smooth
Blog #16: Misplaced Debounce – When a localized solution becomes a system burden
12Blog #12: Don't bring the whole supermarket home just to buy a loaf of bread13Blog #13: When optimization becomes a burden – Don't 'memo' everything you see14Blog #14: The Domino Effect – When one small change crashes the whole Render system15Blog #15: The 10,000-Row Lesson – When Table Virtualization saved my career16Blog #16: Misplaced Debounce – When a localized solution becomes a system burden17Blog #17: Images vs JS – The battle for priority in the Critical PathReading18Blog #18: Laggy Animations – When the touch is no longer smooth19Blog #19: Chrome Performance Tab – Looking through a microscope at the system's pulse20Blog #20: The Mystery of LCP – Why do 'fast' websites still get rated low?21Blog #21: The Global State Nightmare – The decision to 'topple' the Redux monument22Blog #22: Click Race Condition – When User's click speed beats your App
TP

Written by Truong Pham

Software Engineer passionate about building high-performance systems and meaningful experiences.

Read more articles