How We Build Fast Websites: A Core Web Vitals Field Guide
Core Web Vitals are not a scoreboard, they are a promise to your users. Here is exactly how we build fast websites at Innovation T, from image strategy to gating WebGL on capable devices.
By Innovation T Team
A slow website loses people before it ever gets to make its case. Someone taps your link on a mid range Android phone over a patchy connection, waits, sees the layout jump around, taps a button that does nothing for half a second, and leaves. No amount of clever copy survives that experience. At Innovation T we treat performance as a product feature, not a cleanup task, and Core Web Vitals are the language Google gives us to measure it.
This is the field guide we actually use when we build. It explains what the metrics mean in plain terms, then gives you the concrete tactics we reach for on real projects.
What the metrics actually measure
Core Web Vitals are three user centered metrics, plus one lab metric that helps you find problems before your users do. Forget the acronyms for a second and think about what the user feels.
LCP (Largest Contentful Paint) answers a simple question: how long until the main thing on this page shows up? That "main thing" is usually a hero image, a heading, or a big block of text. If your LCP is 4 seconds, your visitor stares at a mostly blank screen for 4 seconds. Google considers 2.5 seconds or less to be good.
INP (Interaction to Next Paint) measures responsiveness. When someone taps, clicks, or types, how long before the page visibly reacts? INP replaced the older First Input Delay metric because it looks at all interactions across the visit, not just the first one. A laggy button, a menu that opens a beat late, a form field that stutters: that is bad INP. Good is 200 milliseconds or less.
CLS (Cumulative Layout Shift) measures visual stability. It is the frustration of reading a paragraph and having it jump because an image or ad loaded above it, or reaching for a button that suddenly slides out from under your finger. Good CLS is 0.1 or less.
TBT (Total Blocking Time) is the lab counterpart to INP. It measures how long the main thread was blocked and unable to respond during page load. You will see TBT in Lighthouse and other lab tools. High TBT almost always predicts poor INP in the field, so we use it as an early warning while developing.
The pattern here matters. LCP is about loading, INP and TBT are about interactivity, and CLS is about stability. Most performance work comes down to shipping less to the browser and doing less work on the main thread. Everything below is a version of those two ideas.
Image strategy: usually the biggest win
Images are the heaviest thing on most pages, and the hero image is frequently the LCP element, so this is where we start.
- Serve modern formats. AVIF and WebP are dramatically smaller than JPEG or PNG at the same quality. We serve AVIF with a WebP fallback and only reach for older formats when we truly must.
- Size images to their display size and ship responsive variants. Use
srcsetandsizesso a phone downloads a phone sized image, not a 2000 pixel desktop asset scaled down in the browser. - Always set explicit
widthandheight(or a CSSaspect-ratio). This reserves the space before the image loads and is the single most effective fix for CLS. - Prioritize the LCP image and lazy load the rest. Add
fetchpriority="high"to the hero andloading="lazy"to everything below the fold so offscreen images do not compete for bandwidth during the critical first paint. - Consider a preload hint for the hero image so the browser starts fetching it before it finishes parsing the CSS.
Getting the hero image right often pulls LCP under the 2.5 second mark on its own. The same discipline applies to video: use a poster image, and never autoplay a heavy background video on mobile.
Font loading: stop the invisible text
Web fonts are a quiet LCP and CLS killer. A page waits for a custom font, shows nothing (or shows fallback text that then reflows), and the user pays for it.
- Add
font-display: swapso text renders immediately in a fallback font and swaps to the web font when it arrives. Users can read while the font loads. - Self host your fonts instead of pulling them from a third party. It removes an extra connection and gives you cache control.
- Preload the one or two critical font files so they start downloading early.
- Subset fonts to the characters and weights you actually use. Shipping every weight of a family when you use two is pure waste.
- Pick a fallback font with similar metrics, or tune it with
size-adjust, so the swap does not cause a visible reflow. That protects CLS.
Reducing main-thread JavaScript
JavaScript is where good INP and TBT go to die. Every script the browser has to parse, compile, and execute ties up the main thread, and while that thread is busy it cannot respond to taps. This is the hardest and most valuable area to get right.
- Ship less. Audit your bundle and remove dependencies you do not need. A date library, a giant UI kit used for one component, three overlapping utility libraries: these add up fast.
- Code split and lazy load. Load the JavaScript for a route or a component only when it is needed, not all upfront. A modal that opens on click does not need to be in the initial bundle.
- Defer non critical scripts. Analytics, chat widgets, and marketing tags should load after the page is interactive, not compete with it. Use
deferor load them on idle. - Break up long tasks. Any task over 50 milliseconds blocks interaction. Chunk heavy work and yield to the main thread so the browser can respond to input between chunks.
- Prefer the platform. A lot of what people install libraries for (form validation, simple animation, date formatting) the browser now does natively. We build high converting pages with less code, which is a theme we cover in our guide to the anatomy of a high converting landing page.
Gating heavy WebGL and animation libraries
This is where practitioner experience separates good sites from janky ones. Rich visuals, WebGL scenes, and physics based animation look stunning on a high end laptop and turn a budget phone into a slideshow. The answer is not to remove them, it is to serve them conditionally.
- Only run heavy WebGL when a real GPU is present. Before mounting an expensive 3D scene, we detect the rendering context and check the reported renderer. On a software renderer or a device that fails a quick capability probe, we fall back to a static image or a lightweight CSS version instead of a full WebGL loop.
- Respect the user. Honor the
prefers-reduced-motionmedia query and skip non essential animation for people who ask for it. Also skip heavy effects when the device reports few CPU cores or the Save Data hint is on. - Move animation to CSS wherever you can. CSS transforms and opacity animations run on the compositor thread and are cheap. A hover effect, a fade in, a subtle slide: these belong in CSS, not in a JavaScript animation library that runs on the main thread.
- Throttle your render loop. If you must run an animation frame loop, cap the frame rate, and pause it entirely when the element scrolls out of view or the tab is hidden. A loop repainting at full speed for an offscreen canvas is pure waste that shows up as bad INP.
- Load the heavy library only when the effect is actually visible. Use an intersection observer so the WebGL or animation bundle downloads when the user scrolls near it, not on initial load.
Lazy loading, caching and the CDN
The last layer is about not doing work twice and not serving from far away.
- Lazy load below the fold content: images, iframes, embeds, and heavy components. The browser has native
loading="lazy"for images and iframes, and an intersection observer covers everything else. - Cache aggressively. Static assets with content hashed filenames can be cached for a year, because a change to the file changes the filename. Set long
Cache-Controlheaders for those and shorter ones for HTML. - Use a CDN. Serving assets from an edge location near your user cuts latency directly, which helps LCP. A CDN also absorbs traffic spikes and takes load off your origin.
- Compress everything. Brotli for text assets beats gzip, and it should be on by default at the server or CDN level.
- Reduce server response time. A slow first byte poisons every metric downstream. Cache rendered pages where you can, and keep database work off the critical path. When we design the APIs behind these pages we optimize for exactly this, a topic we get into in designing APIs developers love.
The performance checklist
Here is the numbered list we run through before shipping.
- Confirm the LCP element (usually the hero image or heading) and give it priority loading.
- Serve AVIF or WebP, sized responsively with
srcsetandsizes. - Set explicit dimensions or
aspect-ratioon every image and media embed to kill layout shift. - Lazy load every image, iframe, and heavy component below the fold.
- Self host fonts, subset them, preload the critical files, and use
font-display: swap. - Audit the JavaScript bundle and remove or replace heavy dependencies.
- Code split by route and lazy load components that are not needed at first paint.
- Defer analytics, chat, and marketing scripts until after interactivity.
- Break up any main thread task longer than 50 milliseconds and yield between chunks.
- Gate WebGL on a real GPU check, with a static fallback for weak devices.
- Move animations to CSS transforms and opacity, and honor
prefers-reduced-motion. - Throttle and pause render loops when offscreen or when the tab is hidden.
- Set long cache headers on hashed static assets and shorter ones on HTML.
- Serve through a CDN with Brotli compression enabled.
- Measure with lab tools (Lighthouse, TBT) during development, then validate with field data (real LCP, INP, CLS) after release.
That last point is the one people skip. Lab scores tell you where the problems are, but only field data tells you what your real users experience on their real devices and networks. We build to the lab and verify in the field.
Fast is a design decision you make on purpose, over and over, at every layer. Do it well and the metrics take care of themselves, because they are just measurements of a site that respects the person using it.
If your site feels slow and you are not sure where the time is going, that is exactly the kind of problem we like. Take a look at our services or get in touch and we will help you build something fast.
Ready to build with Innovation T?
Whether it is security, growth or engineering, our team can help you ship it well.