- Knowledge
- technology
- OOP
- Tips
- Programming
- Tips
- Tutorial
- SEO
- Ranking
- Knowledge
- Special Day
- Seo
- Bug
- Data science
- Seo
- artificial intelligence
- Machine Learning
- Robotics
- happyNewYear2021
- newYearEve
- 2021
- Automation
- Smart Home
- Career
- Best Practices
- Git
- Logging
- Web Fundamentals
- DNS
- HTTPS
- Performance
- AI Tools
- ChatGPT
- Claude
- Gemini
- Laravel
- Eloquent
- MySQL
- HTTPS
- TLS
- Web Security
- Certificates
- Developer Life
- Debugging
- Docker
- DevOps
- Transactions
- Queues
- LLMs
- AI
- AI Coding
- Developer Tools
- React Native
- Expo
- Kate PMS
- Mobile Apps
- Laravel
- Authentication
- Sanctum
- Cookies
- API Design
- Payments
- Idempotency
- DeepSeek
- Open Source AI
- LLMs
- AI News
- Git
- Version Control
- AI Coding
- Prompting
- PHP
- Checklist
- MCP
- AI Agents
- OpenAI
- Architecture
- Microservices
- Modular Monolith
- Estimation
- Developer Life
- Project Planning
- Humour
- OAuth
- OpenID Connect
- Authentication
- Embeddings
- Vector Search
- RAG
- pgvector
- OpenAI
- GPT-4.1
- Codex CLI
- Events
- Testing
- Clean Code
- Maintainability
- Code Review
- Webhooks
- API
- Security
- Claude Code
- Workflow
- AI
- LLM
- Prompt Injection
- Mobile
- React
- Networking
- TCP
- UDP
- HTTP/3
- CLAUDE.md
- AWS
- Cloud Security
- Backups
- PHPUnit
- Software Engineering
- Leadership
- Communication
- RAG
- Embeddings
- AI Engineering
- IT Infrastructure
- Networking
- Access Control
- CI/CD
- GitHub Actions
- Gemini CLI
- Claude Code
- JavaScript
- Async/Await
- Node.js
- Promises
- Security
- Cryptography
- Passwords
- MySQL
- Database
- Vibe Coding
- Software Quality
- DNS
- Code Reading
- Onboarding
- Productivity
- Background Jobs
- Developer Humour
- Estimates
- Dev Life
- JWT
- o3-mini
- DeepSeek R1
- Rate Limiting
- Kate PMS
- E-Signing
- Audit Trail
- REST
- GraphQL
- API Design
- Laravel 12
- Upgrade Guide
- Open Source
- Self-Hosting
- Task Scheduling
- Cron
- Secrets
- CORS
- PHP
- PHP-FPM
- OPcache
- GitHub Copilot
- Software Architecture
- Engineering
- TypeScript
- JavaScript
- Type Safety
- AI Security
- React Native
- Product Design
- AI Agents
- Kiro
- Queues
- Redis
- RabbitMQ
- AWS SQS
- Nginx
- Apache
- GPT-5
- gpt-oss
- Clean Code
- Architecture
- Naming
- Documentation
- Career
- ADR
- Teamwork
- Supply Chain
- Kate HRM
- HR Software
- Permissions
- System Design
- Pagination
- SSH
- Linux
- Big O
- Databases
- Laravel Boost
- MCP
- Developer Skills
- Validation
- Databases
- Indexes
- Code Quality
- Deployment
- Developer Humour
- Feature Flags
- Code Review
- Pull Requests
- Docker
- Cursor
- Authorization
- RBAC
- Gemini
- Long Context
- PHP 8.4
- Caching
- Dependency Injection
- Web Performance
- Browser
- CSS
- Database
- Migrations
- ChatGPT
- AI for Developers
- Monitoring
- On-Call
- REST
- Backend
- SQL
- NoSQL
- Database Design
- Coding Agents
- Claude 4
- API Resources
- REST API
- Load Balancing
- Scaling
- AWS
- AI Tools
- Claude
- Sora 2
- CTE
- 2FA
- TOTP
- Programming Languages
- Prompts
- Developer Workflow
- API Gateway
- APIs
- Passport
- API Auth
- Learning
- Burnout
- Developer Growth
- Web Development
- SEO
- Kate Mall
- ChatGPT Atlas
- Agent Skills
- Middleware
- Laravel 12
- Collections
- Context Window
- Monitoring
- Commit Messages
- Self Review
- Growth
- Regex
- Programming Basics
- Text Processing
- Database Design
- Normalization
- Linux
- Server Security
- Linux Foundation
- Open Standards
- Legacy Code
- Documentation
- AI Workflow
- File Uploads
- Test Data
- Hashing
- Performance
- Caching
- Enums
- Scope Creep
- Estimation
- Codex
- Gemini CLI
- Timezones
- Carbon
- Bugs
- PHP 8.5
- Gemini 3
- GPT-5.1
- Data Integrity
- Event Loop
- Async
- Opus 4.5
- AI Models
- React
- Forms
- Frontend
- Backups
- AI Images
- DALL-E
- Midjourney
- Race Conditions
- Concurrency
- Legacy Code
- Refactoring
- Senior Engineer
- Scope
- LLM
- CDN
- Web
- Sub-Agents
- Soft Deletes
- Audit Log
- Concurrency
- AI Learning
- NestJS
- AI Evals
- Policies
- SPF DKIM DMARC
- Unicode
- UTF-8
- Knowledge Graph
- Value Objects
- Technical Debt
- Feature Flags
- Laravel Pennant
- Deployment
- Copilot
- Composer
- Dependencies
- Artisan
- Automation
- AWS S3
- Object Storage
- Cloud
- Small Language Models
- Ollama
- Production
- Sessions
- HTTP
- Mentoring
- SQL
- Virtual Machines
- Web Development
- HTTP/2
- QUIC
- Web Performance
- AI Integration
- LLM API
- SOLID
- OOP
- Hosting
- Serverless
- Merge Conflicts
- Temperature
- AI Development
- Reverse Proxy
- Nginx
- Infrastructure
- Verification
- Passkeys
- WebAuthn
- Teams
- Communication
- Stakeholders
- Monorepo
- CI/CD
- Versioning
- JSON Schema
- Livewire
- Inertia
- Meetings
- Distributed Systems
- Privacy
- Full-Stack
- T-Shaped Skills
- Money
- Notifications
- Web Security
- HTTP Headers
- CSP
- Function Calling
- Load Testing
- k6
- Data Extraction
- Debugging
- WebSockets
- SSE
- Real-Time
- Laravel Reverb
- Infrastructure as Code
- Terraform
- Side Projects
- Laravel Pint
- OpenAPI
- Swagger
- UX
- Multimodal
- Jest
- Pair Programming
- APIs
- Rate Limiting
- Resilience
- Dev Humour
- Design Tokens
- JWT
- API Keys
- Sessions
- PHPStan
- Rector
- Incidents
- Reporting
- Dashboards
- Zero Trust
- IAM
- Search
- Laravel Scout
- Junior Developers
- Mentoring
- Images
- WebP
- AVIF
- Bug Reports
- Let's Encrypt
- Design Docs
- Software Design
- Observers
- Replication
- Accountability
- Data Structures
- Reliability
- LLM Memory
- Error Handling
- Payments
- Payment Gateway
- Webhooks
- PCI DSS
- Observability
- OpenTelemetry
- Personal Brand
- Writing
- Conventions
- Dates
- Scheduling
- Disaster Recovery
- Compression
- Brotli
- Deadlines
- Developer Habits
- State Machines
- Tech Roles
- UUID
- ULID
- Horizon
- Planning
- Engineering Culture
- Ownership
- Soft Skills
- Socialite
- Cost Control
- Collations
- Unicode
- Octane
- PostgreSQL
How the Browser Turns HTML Into Pixels (and Why Your Page Still Feels Slow)
About Post
Your API answered in 80 milliseconds. The page still feels slow. The scroll stutters, the table jumps when the fonts load, and the button takes a beat to react.
That's because the server sending HTML is only the start. Between "bytes arrived" and "pixels on screen", the browser runs a whole production line, and most front-end performance problems are about making that line do more work than it needs to. Let's walk it, station by station.
The production line at a glance
- Parse HTML into the DOM.
- Parse CSS into the CSSOM.
- Combine them into the render tree.
- Layout: work out the size and position of every box.
- Paint: turn boxes into drawing instructions and pixels.
- Composite: stack the painted layers together, usually on the GPU.
Every frame you see went through some or all of these steps. The trick to a fast page is understanding which changes send the browser back to which step.
1. HTML becomes the DOM
The browser reads HTML as it streams in and builds the DOM, a tree of nodes. It doesn't wait for the whole file, which is why a fast first byte matters.
The thing that stops this streaming is a plain <script src="...">. The parser can't know whether that script will call document.write() or change the DOM, so it stops, downloads the script, runs it, and only then continues. One slow script in the <head> and the whole page waits.
<!-- Blocks parsing until downloaded and executed -->
<script src="/js/app.js"></script>
<!-- Downloads in parallel, runs after parsing, in order -->
<script src="/js/app.js" defer></script>
<!-- Downloads in parallel, runs as soon as it's ready, any order -->
<script src="/js/analytics.js" async></script>
Use defer for your app code and async for independent things like analytics. Module scripts (type="module") are deferred by default.
2. CSS becomes the CSSOM
Stylesheets are parsed into the CSSOM: every rule, resolved into the final computed style for each element. CSS is render-blocking. The browser won't paint anything until it has the CSS, because painting unstyled content and then restyling it would look broken.
That's a sensible default, and it's why a huge stylesheet from a slow CDN delays the very first paint, even when the HTML arrived instantly.
3. The render tree
The browser walks the DOM, applies the CSSOM and keeps only what will actually be drawn. An element with display: none isn't in the render tree at all. An element with visibility: hidden is: it's invisible, but it still takes up space. That small difference matters later, because only the render tree goes through layout.
4. Layout: the expensive maths
Layout (Firefox calls it reflow) calculates geometry: how wide is this box, where does this line wrap, how tall is this table row. It's expensive because geometry is connected. Make one element taller and everything below it moves. Change the width of a container and every child may need recalculating.
Things that trigger layout: changing width, height, padding, margin, top/left, font size, adding or removing elements, and even reading geometry at the wrong moment. More on that in a second.
5. Paint and 6. composite
Paint fills in the pixels: text, colours, borders, shadows, images. The browser may split the page into layers, paint them separately, and then compositing stacks them together like sheets of glass.
Compositing is the cheap step, often handled by the GPU. That's the key to smooth animation: if you only change things the compositor can handle on its own, the browser skips layout and paint completely.
| You change... | Browser redoes | Cost |
|---|---|---|
width, height, top, left, font-size | Layout, paint, composite | High |
color, background-color, box-shadow | Paint, composite | Medium |
transform, opacity | Composite only (usually) | Low |
So animate a sliding panel with transform: translateX(), not by changing left. Same visual result, very different amount of work. At 60 frames per second, the browser has about 16 milliseconds per frame for everything, your JavaScript included.
The classic trap: layout thrashing
Browsers are lazy in a good way. When you change a style, they don't recalculate layout immediately; they batch the work until the next frame. But if you read a geometry value like offsetHeight right after a write, the browser has to calculate layout right now to give you a correct answer.
Do that in a loop and you force a full layout on every iteration:
// Bad: read, write, read, write... a forced layout every time
rows.forEach((row) => {
const height = row.offsetHeight; // read (forces layout)
row.style.height = `${height + 10}px`; // write (invalidates layout)
});
// Better: all reads first, then all writes
const heights = rows.map((row) => row.offsetHeight);
rows.forEach((row, i) => {
row.style.height = `${heights[i] + 10}px`;
});
On a staff dashboard with a long table of contracts or invoices, the first version is the difference between instant and visibly sluggish. The fix isn't clever. It's just batching.
Practical tips that come straight from the pipeline
- Don't block the parser:
deferyour scripts, keep the<head>lean. - Get CSS in fast: keep the critical CSS small; load the rest without blocking if it's not needed for the first screen.
- Reserve space for images: set
widthandheightattributes (oraspect-ratio) so the layout doesn't jump when they load. - Animate
transformandopacity, not position and size. - Batch DOM reads and writes, and do visual updates inside
requestAnimationFrame. - Use
will-changesparingly. It promotes an element to its own layer, which costs memory. Use it on the thing you're about to animate, not on everything. - Render less. A table with thousands of DOM rows will always be slow to lay out. Paginate or virtualise the list.
The rule to remember: layout is expensive, paint is medium, composite is cheap. Every performance trick on this list is a way of staying as far down that line as possible.
See it for yourself
Open Chrome DevTools, go to the Performance panel, record yourself scrolling a heavy page, and look for long purple blocks (layout and style) and green ones (paint). Firefox has the same idea in its profiler. Once you've seen a forced layout warning in your own code, you never unsee it.
Which one of these has bitten you: a render-blocking script, a jumping layout, or a scroll that stutters for no obvious reason?

Be first to comment it...