What is 'jank', and how do you reason about a 16ms (or 8ms) frame budget?
A dropped frame isn't a crash and won't show up in a stack trace — it's a budget you either meet or visibly miss, sixty (or ninety, or a hundred twenty) times a second.
LEARN BY DOINGPut your intuition to work3 steps
What is 'jank', and how do you reason about a 16ms (or 8ms) frame budget?
Use the lesson’s examples and explanation to check your idea.
Personal reflection. Nothing is sent to an AI service.#What "jank" means precisely
Jank is the user-visible stutter that happens when the system fails to produce a new frame in time for the display's next refresh, so the display shows the previous frame again (or an incomplete one) instead of the intended next one. It isn't a single bug with a single cause — it's the symptom of any work exceeding the frame's time budget, and that budget is set by the display's refresh rate: a 60Hz display refreshes every ~16.7ms, a 90Hz display every ~11.1ms, and a 120Hz display — now common on flagship and upper-mid-range Android phones, and on iPhone Pro/iPad Pro's ProMotion — every ~8.3ms. The number people quote as "the frame budget" isn't a fixed constant; it's whatever the target display's refresh interval is, and the same app logic that comfortably fits inside 16ms can visibly jank on a 120Hz screen where its budget just shrank by half.
Crucially, the whole budget isn't yours. The 16ms (or 8ms) has to cover input handling, your app's layout/measure and draw work, the OS compositor doing its own work, and handoff to the GPU — your app's actual code has to fit in whatever's left after the platform's own overhead, which is one reason a screen that "looks simple" can still jank: the fixed overhead is a larger fraction of a smaller budget at higher refresh rates.
#What's inside one frame's work
That's an illustrative split, not a fixed spec — the actual proportions shift with what the frame is doing (a scroll leans on layout/draw, an animation leans on app logic driving property changes, a list with images leans on decode/draw). The point of drawing it as a timeline is the takeaway itself: every one of those stages competes for the same 16ms, and a regression anywhere in the chain — a synchronous main-thread call, a layout pass that's deeper than it needs to be, an unnecessarily expensive onDraw/custom paint — pushes the total past the deadline and the whole frame is late, not just the stage that regressed.
#Why "just optimize everything a bit" is the wrong mental model
The frame budget reframes performance work from "make the code fast" to "make this stage finish before the deadline, using whatever budget is actually left after the other stages." That has real implications:
- A 2ms regression is invisible in isolation and fatal in aggregate. If four different screens each independently add "just 2ms" of layout complexity over a few sprints, none of them individually crosses the threshold in review, but the accumulated total on the busiest screen eventually does — which is why frame-time regression tracking over time matters more than a one-off measurement.
- The bottleneck moves. Fixing your slowest stage doesn't guarantee a faster frame if a different stage was already close to its share of the budget — it just means the next slowest stage becomes the new ceiling. This is standard bottleneck reasoning, but it's easy to forget mid-optimization and declare victory after fixing the first thing you found.
- Consistency beats raw speed. A frame that reliably takes 15ms every time reads as smooth; a sequence that alternates between 10ms and 20ms — same average — reads as visibly janky, because human perception is sensitive to variance in motion, not just to the mean. This is why tools report percentiles (p50/p90/p99 frame time, or "janky frame" counts) rather than an average frame time.
#How you actually measure and reason about it in practice
Both platforms expose this directly rather than making you infer it from FPS averages, which hide exactly the variance that matters. Android's Perfetto/GPU Rendering Profile ("Profile GPU Rendering" bars in Developer Options, or the more detailed Perfetto trace) breaks a single frame down by stage — input handling, animation, measure/layout, draw, sync & upload, command issue — so you can see which stage of the pipeline in the diagram above is actually the one blowing the budget on a given screen, rather than guessing. iOS's Instruments Core Animation / Time Profiler similarly reports per-frame timing and highlights hitches directly against the display's actual refresh interval, correctly adjusting the "budget line" for a ProMotion 120Hz display versus a 60Hz one.
The senior-level framing to bring to an interview: jank is a scheduling problem against a hard, display-defined deadline, not a vague "the app is slow" problem, and every optimization technique you already know — avoiding main-thread I/O, reducing overdraw, flattening view hierarchies, memoizing expensive recomposition — is really just a way of shrinking one stage of this same recurring, per-frame race against the clock.