skip to content

Runtime Performance

Once the page has loaded, performance becomes a main-thread problem: long tasks, forced layouts, janky animation and leaking memory. This group covers keeping an already-loaded app responsive.

on this pageshow

explore

questions

23

A designer asks for animations that stay smooth on a 60 Hz display and on a 120 Hz laptop. How much wall-clock time does one frame give you at each refresh rate, and why is the budget for your own work smaller than that number?

level: juniorimportance: must knowfreq 58%

answer

  1. one over the refresh rate
  2. the browser takes a cut
  3. not the whole interval is yours
  4. about ten milliseconds at sixty hertz
  5. half again at 120 Hz

basics

~20 s

A frame lasts about 16.7 ms at 60 Hz and about 8.3 ms at 120 Hz. The browser needs part of every frame for its own style, layout, paint and compositing work, so budget only around half of it — roughly 8-10 ms at 60 Hz — for your code.

solid answer

~50 s

The frame interval is just `1000 / refreshRate` milliseconds: ~16.7 ms at 60 Hz, ~11.1 ms at 90 Hz, ~8.3 ms at 120 Hz. That whole interval is not yours, though. Inside it the browser still has to recalculate style, run layout, paint and composite, and it shares the thread with timers, event handlers and garbage collection. The common rule of thumb — it comes from Google's RAIL guidance — is to keep your own per-frame work under about 10 ms at 60 Hz and to treat anything above that as borrowed time. If the frame is not ready when the display refreshes, it is not shown a little late; the previous image simply stays on screen, and the user reads that as a stutter. So the practical target is not "be fast on average" but "never overrun", which is why per-frame budgets are stated as a ceiling and checked on the slowest device you support, not on a developer laptop.

go deeper

for a junior

Be ready to produce the numbers instantly: about 16.7 ms per frame at 60 Hz and about 8.3 ms at 120 Hz, from 1000 divided by the refresh rate. Say plainly that some of that time belongs to the browser.

for a middle

Explain what consumes the rest of the frame — style, layout, paint, compositing, plus timers and garbage collection on the same thread — and why roughly 10 ms at 60 Hz is the usual planning figure for your own work.

for a senior

Show that you check the budget on a slow reference device rather than a laptop, that you judge animations by their worst frames rather than an average, and that you know a high refresh rate often comes with a slower CPU.

for a principal

Own the policy: which device tier the budget is defined against, what number the team is allowed to ship, and how that ceiling is enforced so a motion-heavy feature cannot quietly borrow the whole frame.

## What a frame actually is A display refreshes at a fixed rate — 60 times a second on most desktop monitors, 90 or 120 on many phones and newer laptops. Each refresh shows whatever image the graphics system has ready at that moment. The browser's job during an animation is to have a new image ready before every refresh. "60 fps" is therefore not a browser setting or a quality slider; it is a deadline imposed by the hardware. ## The arithmetic The interval between refreshes is `1000 / refreshRate` milliseconds: ``` 60 Hz -> 1000 / 60 = 16.67 ms 90 Hz -> 1000 / 90 = 11.11 ms 120 Hz -> 1000 / 120 = 8.33 ms 144 Hz -> 1000 / 144 = 6.94 ms ``` Those are the only numbers worth memorising, and 16.7 / 8.3 are the two you will be asked for. ## Why the whole interval is not yours Inside that window the browser has to do everything, not just run your callback. It recalculates styles for elements whose properties changed, runs layout if anything affected geometry, paints the changed areas, and hands the result to compositing. Those steps take real milliseconds, and they scale with how many elements you touched and how large an area changed. On top of that, the same thread is running your event handlers, timers, promise reactions and any garbage collection the engine decides to do. The widely quoted planning figure — from Google's RAIL performance model — is to leave the browser roughly 6 ms of every 16.7 ms frame and keep your own work under about 10 ms. Treat that as a budget, not a law: the honest version is "your work plus the browser's work must fit, and you only control one half of the sum". ## Overrunning is not a small penalty A frame you fail to deliver is not shown slightly late. The previous image stays on screen for another refresh interval, so the motion visibly hitches. This is why a mean frame time of 12 ms tells you almost nothing: an animation that is comfortably inside budget for 200 frames and blows through it for three still looks broken, and the average hides exactly the frames the user noticed. Budgets for animation are stated as ceilings and checked against the worst frames, not the mean. ## Higher refresh rates make the budget harder, not easier It is tempting to assume a 120 Hz device is a fast device. Often it is not: high-refresh panels are common on mid-range phones whose CPUs are several times slower than a developer laptop. That combination is the worst case — half the time per frame, on slower hardware. Some browsers and devices respond by animating at a lower rate, or by throttling on battery saver, so you cannot assume you will be given the top rate either. The practical consequence is that you should never write an animation that assumes a fixed frame interval, and you should pick a *reference device* — a specific mid-tier phone, or a CPU-throttled desktop profile — as the machine the budget must hold on. ## Spending the budget well Once you accept that you own roughly 8-10 ms at 60 Hz and about half that at 120 Hz, the design decisions follow: - **Animate few things, and small areas.** Per-frame cost scales with the number of animating elements and the pixel area the browser must redraw. Ten animating cards cost roughly ten times one. - **Prefer properties the browser can update without redoing layout for the whole page** — `transform` and `opacity` are the two you can nearly always afford, which is why motion systems are usually built out of them. - **Hoist everything constant out of the per-frame path.** Values you can compute once — element sizes, easing tables, DOM references, colour strings — should not be recomputed 60 times a second. - **Avoid per-frame allocation.** Creating objects, arrays or strings each frame gives the garbage collector reasons to run inside your budget. - **Prefer declarative animation where you can**, so there is no per-frame JavaScript in the budget at all. ## How the number shows up in an interview A junior is expected to produce 16.7 ms and, ideally, 8.3 ms. A stronger answer adds the second half: that the browser takes a share, that the effective budget is roughly half the interval, that missing the deadline drops a whole frame rather than delaying it slightly, and that the budget must be verified on a slow reference device rather than the machine you develop on.

  • Does a 120 Hz display mean the animation needs to do twice as much work per second?
    Yes — twice as many frames, each with half the time. And the device is usually not twice as fast; high-refresh panels are common on mid-range phones. Browsers may also drop to a lower animation rate under battery saver or heavy load, so you should target smoothness at the rate you actually get rather than assume the panel's maximum.
  • If your per-frame work measures 12 ms on a developer laptop, is that inside budget?
    On paper it fits a 16.7 ms frame, but it is not safe. The browser still needs its share, and a mid-tier phone can be four to ten times slower on the same code, which puts you far past the deadline. Re-measure with CPU throttling or on a real reference device before calling 12 ms acceptable.
  • Your animation must fit a tighter budget. What do you cut first?
    Cut per-frame work before cutting the animation. Hoist constants and DOM lookups out of the loop, stop allocating objects each frame, reduce how many elements animate and how much screen area changes, and move to properties that do not force the browser to redo layout. Shortening the duration hides the problem rather than fixing it.

saying these in an interview costs you the question

  • Says a frame is 16 ms and all of it is yours to spend
  • Treats 60 fps as a browser setting rather than the display's refresh rate
  • Assumes a 120 Hz device has proportionally faster CPU
  • Thinks a missed deadline just shows the frame slightly late
  • Reports average fps and calls a stuttering animation smooth

context

open as a page

A single-page app swaps views in and out without ever reloading the document. When a view is torn down, what must its setup code release so the view's memory can be reclaimed, and why does skipping that matter more here than on a traditional multi-page site?

level: juniorimportance: must knowfreq 62%

basics

~20 s

Release everything setup registered on something longer-lived than the view: listeners on window or document, timers, observers, store and socket subscriptions, in-flight requests, and entries pushed into module-level collections. A multi-page site gets a clean heap on every navigation; an SPA never does.

open as a page

In a Chrome DevTools performance recording, one JavaScript call in the flame chart sits above dozens of alternating short "Recalculate Style" and "Layout" entries, instead of a single layout at the end of the frame. What does that pattern tell you, and how do you get from it to the line of code responsible?

level: middleimportance: must knowfreq 55%

basics

~20 s

That sawtooth is layout thrashing: the code measures geometry and mutates the DOM in the same loop, so each iteration forces the browser to redo layout. To locate it, open the detail pane on one of those layout entries — DevTools flags a forced layout and links to the JavaScript that triggered it.

open as a page

In web performance, what counts as a "long task" on the browser's main thread, why is the threshold 50 ms rather than a frame's 16 ms, and what does a single long task cost the user?

level: middleimportance: must knowfreq 70%

basics

~20 s

A long task is one uninterrupted unit of main-thread work lasting over 50 ms. The 50 ms comes from the roughly 100 ms budget for responding to input: it leaves half that budget free. While a long task runs, input, timers and frames all wait.

open as a page

On a web page, a button's click handler marks the row as selected in the DOM and then spends about 300 ms recalculating a summary, all in one synchronous block, and Interaction to Next Paint (INP) for that button is poor. Where in the handler do you introduce a yield back to the browser, and why does the placement matter more than how much work the handler does?

level: middleimportance: must knowfreq 50%

basics

~20 s

Yield right after the DOM update that shows feedback and before the expensive recalculation. The interaction is timed until the next paint, so painting the feedback first closes the measured window while the heavy work runs after it.

open as a page

A scroll listener on a long page calls getBoundingClientRect() on about forty elements on every scroll event, to decide which ones are on screen and to reposition a sidebar. Scrolling stutters, and batching the reads and writes only helps a little. What would you replace the measurement with, and why is that cheaper?

level: seniorimportance: must knowfreq 62%

basics

~20 s

Stop measuring on scroll. Use IntersectionObserver for on-screen decisions and ResizeObserver for size-dependent ones: the browser reports the geometry it already computed and calls you only when something crosses a threshold, so scroll frames where nothing changed cost nothing. A sticky sidebar usually needs no JavaScript at all.

open as a page

While scrolling a page, Chrome's console prints `[Violation] Forced reflow while executing JavaScript took 47ms`. What is that warning telling you about your code, and what would you do with it?

level: juniorimportance: should knowfreq 38%

basics

~20 s

Chrome prints that when your JavaScript asked the browser for a geometry value and forced it to compute layout on the spot, costing 47ms inside that script. It is a symptom, not a location: record a performance trace of the same interaction to find the call site.

open as a page

A Save button's click handler on a web page first serializes the whole draft into localStorage, then builds an analytics payload, and only after that updates the DOM to show the "Saved" state. Users say the button feels laggy. What would you change, and why does the order matter?

level: juniorimportance: should knowfreq 40%

basics

~20 s

Show the "Saved" state first, then defer the localStorage write and the analytics payload until after the browser has painted. Only the feedback has to be in that frame; the bookkeeping is what the user is currently waiting behind.

open as a page

A UI animation can be driven three ways: a CSS transition or keyframe animation, the Web Animations API through Element.animate(), or a requestAnimationFrame loop that writes styles every frame. How do you choose between them?

level: middleimportance: should knowfreq 46%

basics

~20 s

Default to declarative CSS for state-driven motion with known start and end values. Reach for Element.animate() when values are computed at runtime or you need playback control such as pause, reverse or a finished promise. Use a per-frame JavaScript loop only for motion that cannot be expressed as interpolation between keyframes.

open as a page

Operating systems expose a "reduce motion" accessibility setting, which the web surfaces as the CSS media feature prefers-reduced-motion. What does a page owe a user who has enabled it, and how do you honour it for both CSS animation and JavaScript-driven motion?

level: middleimportance: should knowfreq 48%

basics

~20 s

Reduce large or unexpected movement — parallax, zooms, sliding transitions, autoplaying loops — while keeping the feedback that tells users what changed, usually as an opacity crossfade. Honour it in CSS with a prefers-reduced-motion media query and in JavaScript by checking the same query through matchMedia.

open as a page

Total Blocking Time (TBT) is reported for a page load. How is it computed from the page's long tasks, and why can a page with one 900 ms task score far worse than a page with six 120 ms tasks?

level: middleimportance: should knowfreq 52%

basics

~20 s

Total Blocking Time sums, over every long task in the measured load window, the amount by which that task exceeds 50 ms. One 900 ms task contributes 850 ms; six 120 ms tasks contribute 70 ms each, or 420 ms. Anything at or under 50 ms contributes nothing.

open as a page

In a browser application, what is a "detached DOM tree", and how can one variable still pointing at a single removed node keep megabytes of nodes alive?

level: middleimportance: should knowfreq 48%

basics

~20 s

A detached DOM tree is a group of nodes removed from the document but still referenced from JavaScript, so the browser cannot free them. Because every node references its parent and every parent its children, holding one node keeps its entire former tree in memory.

open as a page

A browser SPA keeps `const rowMeta = new Map()` at module scope, keyed by the `<tr>` elements it renders, and adds an entry every time a table is drawn. Why does this grow without bound as the user navigates, and does switching it to a `WeakMap` fix it?

level: middleimportance: should knowfreq 40%

basics

~20 s

A Map holds its keys strongly, so every row element ever rendered stays reachable through the module-level Map and its whole table with it. A WeakMap holds keys weakly and does fix this case, but only for object keys and only if nothing else still references the element.

open as a page

A product manager reports that a list animation "feels janky" on real phones, though it looks fine on your laptop. What number would you use to describe animation smoothness, and how would you measure it?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Report the percentage of frames dropped during the animation, plus the duration of the worst frame — not average frames per second. Measure it by recording a performance trace on a slow reference device or with CPU throttling while performing the interaction, and read the frames the browser could not present on time.

open as a page

A card must animate from its slot in a grid to a full-width detail view, and animating its width, height, top and left is visibly janky. What is the FLIP technique, and how does it animate that layout change smoothly?

level: seniorimportance: should knowfreq 38%

basics

~20 s

FLIP stands for First, Last, Invert, Play: measure the element's starting box, apply the final layout and measure again, apply a transform that visually puts it back at the start, then animate that transform away. The layout change happens once, up front, and only a transform is interpolated during the animation.

open as a page

A server-rendered page shows its content in about a second, but clicks and taps do nothing for another two seconds. The trace shows one roughly 1.8 s main-thread task starting right after the JavaScript bundle finishes downloading. What is usually happening, and what are your options?

level: seniorimportance: should knowfreq 46%

basics

~20 s

That gap between visible and usable is the classic hydration cost: the server HTML paints, then one long task compiles and evaluates the bundle and attaches behaviour to the existing markup, blocking input until it finishes. The fixes shrink or split that work; making the download faster does not help.

open as a page

Users report that a browser tab running your SPA becomes sluggish after half an hour of moving between views, and the tab's memory never comes back down. How would you establish that this is a genuine leak rather than normal caching, and identify what is retaining the memory?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Reduce it to a repeatable round trip, then take heap snapshots after equal numbers of cycles and compare them. A leak grows by a similar amount every cycle and never plateaus; a cache rises and levels off. The retainers path of a growing object names the reference to cut.

open as a page

A team splits a heavy click handler so it paints feedback and then yields before the expensive work, and local traces show much shorter tasks — yet real-user Interaction to Next Paint for that control has not improved, and a few interactions look worse than before. What would you investigate, and in what order?

level: seniorimportance: should knowfreq 30%

basics

~20 s

Usually the deferred work did not disappear — it now runs while the user's next input arrives and shows up as input delay on the following interaction. Check the interaction's phase breakdown before concluding the split failed.

open as a page

A team proposes moving the app's data layer — parsing and transforming large API responses — into a Web Worker to eliminate long tasks on the main thread. How would you evaluate that proposal?

level: principalimportance: should knowfreq 34%

basics

~20 s

Offloading pays off only when the work is pure computation and the data is cheap to move. Copying large objects across the boundary is itself main-thread work at both ends and can erase the win. Measure the real tasks, prototype the hot path, and price the complexity before committing.

open as a page

What does a DOM batching library such as FastDOM (`fastdom.measure()` / `fastdom.mutate()`) actually do to your DOM code, and why can adding it to one component still leave the page janky?

level: seniorimportance: nice to knowfreq 28%

basics

~20 s

FastDOM keeps two queues — reads and writes — and flushes them in a single animation frame with every read before every write, so a frame contains one layout instead of many. It only works if all DOM access in that frame goes through it; one unbatched reader elsewhere re-splits the frame.

open as a page

A page registers a PerformanceObserver for entryType 'longtask' and receives a 320 ms entry. How much does that entry actually tell you about which code was responsible, and what does the Long Animation Frames API ('long-animation-frame') add?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

A longtask entry gives duration, start time and a frame-level attribution — never the script or function responsible. The Long Animation Frames API reports whole slow frames instead of single tasks, with a per-script breakdown including source URL and function name, which is what makes field attribution usable.

open as a page

In Chromium, `navigator.scheduling.isInputPending()` reports whether input events are waiting to be processed. A long batch job on the main thread calls it after each item and keeps going while it returns false. What does that buy compared with yielding on a fixed time budget, and what are the risks of relying on it?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

It turns blind time-slicing into an interrupt check: the job keeps the thread while nobody is waiting and releases it the instant input queues. Risks are that it is Chromium-only, blind to rendering and timers, and excludes continuous events by default.

open as a page

A dashboard your team ships runs unattended on wall displays for weeks, and the current mitigation for its steadily growing memory is a nightly location.reload(). As the lead, how do you decide whether that is an acceptable answer or whether the underlying leak has to be fixed?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Decide on evidence: measure the growth rate per hour against the device's headroom, and check who else runs this code. A scheduled reload is a legitimate control for a kiosk with slow, bounded growth; it is a cover-up when ordinary user sessions hit the same ceiling.

open as a page