Performance - balancing atmosphere with speed

Performance

Performance is not a technical afterthought. It is the medium. Every decision on a browser-native site - every image, every animation, every font choice, every script inclusion - either contributes to or detracts from the experience of using the page. A site that loads in one second and responds instantly to interaction communicates competence. A site that takes four seconds and stutters during scroll communicates carelessness, regardless of how beautiful the design is.

This page covers the practical discipline of keeping immersive web pages fast. Weight budgets, render budgets, image handling, motion restraint, script management, and the overarching principle that atmosphere and speed are not in tension - they are the same goal expressed through different constraints.

Weight budgets

A weight budget is a hard limit on how many bytes a page can transfer before it exceeds the acceptable loading time for its audience. The budget is not arbitrary. It is derived from the connection speed of the target audience, the acceptable time to interactive, and the overhead of the protocol and server response.

For a site targeting a broad audience on mobile connections, a practical weight budget for a complete page load is 400 to 600 kilobytes transferred. That includes HTML, CSS, JavaScript, fonts, and images. On a 3G connection at roughly 1.5 megabits per second, 500 kilobytes takes about 2.7 seconds to transfer. Add server response time and parsing, and the page is interactive in roughly 3 to 4 seconds. That is the edge of acceptable.

The budget forces prioritisation. If the page has 500 kilobytes to spend, a 300-kilobyte hero image leaves only 200 kilobytes for everything else. That is tight. A 150-kilobyte hero image leaves 350 kilobytes, which is comfortable. The image quality difference between 300 and 150 kilobytes at 1600 pixels wide is rarely visible to the reader, but the loading time difference is always felt.

Fonts consume more of the budget than most designers expect. A single variable font file can be 80 to 120 kilobytes. Two weights of a standard font are 40 to 60 kilobytes. The temptation to add a third font family for decorative purposes should be resisted unless the budget has clear room.

JavaScript is the most expensive byte-for-byte content type because it must be downloaded, parsed, compiled, and executed. A 100-kilobyte JavaScript bundle costs more in time-to-interactive than a 100-kilobyte image, even though the transfer time is identical. For immersive pages that prioritise speed, the JavaScript budget should be as small as possible - ideally under 50 kilobytes total.

Render budgets

Transfer size is only half the performance equation. The other half is render cost - how much work the browser has to do to paint and composite each frame.

A render budget targets 16.67 milliseconds per frame, which is the maximum time available to maintain 60 frames per second. In practice, the browser needs some of that time for its own housekeeping, so the effective budget for page-triggered work is closer to 10 milliseconds per frame.

Layout recalculation is the most common render budget violation. When an element’s size or position changes, the browser must recalculate the layout of every affected element. On a complex page, a single layout change can trigger hundreds of recalculations, each of which costs time. If these happen during an animation, the frame drops below 60fps and the motion stutters.

The solution is to avoid triggering layout during animation. Transform and opacity changes are handled by the compositor thread and do not trigger layout. Width, height, margin, padding, top, left, and font-size changes trigger layout and should never be animated on performance-sensitive pages.

Paint cost is the next concern. The browser paints pixels to the screen in layers. If an element changes in a way that requires repainting its layer, the cost depends on the size of the layer. Full-page background changes are expensive. Small element changes are cheap. Promoting frequently animated elements to their own compositor layer through will-change or transform: translateZ(0) isolates their paint cost from the rest of the page.

Image discipline

Images are the largest single contributor to page weight on most sites, and the most common source of performance problems. The discipline around images is straightforward but requires consistency.

Every image must declare its width and height in HTML. This allows the browser to reserve space before the image loads, preventing layout shifts that disrupt reading flow and cost Cumulative Layout Shift points.

Every image should be sized to its display context. An image displayed at 800 pixels wide should not be served at 1600 pixels wide unless the page serves a high-density display variant specifically for screens that benefit from it. Serving oversized images wastes bytes and decode time.

Lazy loading should be applied to every image below the fold. The loading="lazy" attribute is sufficient for most cases. The first image in the viewport - typically the hero image - should not be lazy loaded, because the browser needs to fetch it immediately to avoid a visible placeholder.

Format selection matters. JPG at quality 75 to 80 produces acceptable results for photographic content at roughly half the file size of quality 95. The visual difference is negligible at web display sizes. For this site, JPG is the standard format. No PNG hero images. No WebP-only pipelines that complicate the build.

Frame budget basics covers the relationship between image decode time and frame budgets in more detail.

Motion restraint

Every animation costs rendering time. A CSS transition on opacity costs almost nothing. A CSS transition on box-shadow costs significantly more. A JavaScript animation that moves twenty elements simultaneously while calculating scroll position costs substantially more again.

Motion restraint means choosing the cheapest possible implementation for each visual effect. If the effect can be achieved with a CSS transition on transform, use that. If it requires JavaScript, measure the frame rate on a target device and remove it if it drops below 50fps.

Restraint also means limiting the number of simultaneously animated elements. The browser’s compositor can handle multiple transform animations efficiently, but each animation still costs some scheduling overhead. On constrained devices, even compositor-only animations can stutter if there are too many of them competing for GPU time.

The practical maximum is roughly 5 to 8 simultaneously animated elements on a mid-range phone. Beyond that, the rendering pipeline starts to saturate and frame drops become visible. This number varies by device, but designing for 5 concurrent animations is a safe baseline.

For web.dev performance audits, motion-heavy pages often fail on Total Blocking Time and Cumulative Layout Shift metrics. The solution is not to optimise the animations - it is to have fewer of them and to ensure that the ones that remain do not trigger layout.

Avoiding heavy page scripts

The strongest performance decision most pages can make is to not include a JavaScript framework. React, Vue, Angular, and similar frameworks add 30 to 150 kilobytes of JavaScript before any application code is written. On a page that is primarily content - text, images, and light interaction - this overhead buys nothing that HTML and CSS cannot provide.

This site uses no client-side framework. The JavaScript that ships is limited to three concerns: search initialisation through Pagefind, navigation toggle for mobile, and search overlay management. Together, these scripts are under 5 kilobytes of authored code. The Pagefind library adds weight at runtime when search is activated, but it loads on demand rather than blocking the initial render.

Third-party scripts are excluded entirely. No analytics libraries. No social widgets. No chat plugins. No consent management platforms. Each of these adds weight, adds execution time, adds network requests, and adds privacy concerns. Their absence is a performance feature.

Balancing atmosphere with speed

The question is not atmosphere versus speed. The question is how to create atmosphere efficiently. A page can feel immersive and atmospheric while loading in under two seconds and maintaining smooth scroll on a three-year-old phone. The two goals are not in conflict - they are both expressions of craft.

Atmosphere comes from coherence: colour, spacing, typography, and timing working together. None of these are inherently expensive. A carefully chosen colour palette costs zero bytes. Generous whitespace costs zero bytes. Good typography costs the weight of the font files and nothing more. Timing control through CSS transitions costs almost nothing at runtime.

What is expensive is spectacle: heavy images, complex animations, particle systems, video backgrounds, 3D effects, and runtime visual processing. Spectacle can create atmosphere, but it does so at a high cost that most pages cannot afford. The discipline is to achieve atmosphere through composition rather than computation.

This means choosing colours carefully rather than applying gradients and filters at runtime. Setting type at considered sizes rather than scaling it dynamically. Using whitespace structurally rather than filling it with animated decoration. Making every pixel deliberate at design time so the browser has less work to do at runtime.

The result is a site that feels fast because it is fast, and feels atmospheric because every visual decision was made with intention rather than thrown at the GPU. Performance and atmosphere are not competing priorities. They are the same priority, expressed through different aspects of the same craft.