Perceived Performance

The only kind that really matters.

Hi, I'm Eli.

I like to make things on the internet

Today I'll be howling about

  • Why focusing on perceived performance is smart
  • How humans perceive time
  • A box of tricks to manage perceived loading time
  • Predictive preloading

Why Perceived
Performance Matters

Web Performance
is a matter of time.

Time is a funny thing.

Objective time
Subjective time

We've gotten INSANELY good at objective performance.


Service workers

Code minification

Image optimization

Critical Rendering Path

Tree shaking

Code splitting


Edge node caching

SVG spritesheets

We optimize for objective time.

What if users don't notice?

Just Noticeable Difference.

Objective Time differences of 20% or less are imperceptible.

Steve Seow, Microsoft

Shoot for 30% speedup.

We don't live inside of a web performance blog post.

Don't chase diminishing returns.

Focus on the subjective.
Better 💥 for 🤑.

How Humans
Sense Time

Active phase
Passive phase

Humans tend to overestimate passive time by 36%.

Richard Larson, MIT

Takes about ~1 second to transition to a passive state

Nielsen Norman Group

How do we use this

  • Keep users in an active state, keep them from entering a passive state.
  • Make passive phases feel faster than they actually are.

Keep users active

Key for short durations.

What we can do

  • Don't tell users they're waiting.
  • Respond to users immediately, even if nothing is happening.
  • Keep users in flow with more responsive event listeners.

Resist loading

Woah. Woah. Woah.

Slow down here.

Don't use loaders?

But perceived perf?

Loaders tell users
that they have to wait.

Adding a loader prematurely
puts them in a passive state.

As waits approach ~1 second,
start to use loaders.


Immediacy keeps users from entering a passive state.

Stay :active, kids!


Hyperbole and a half

Better Event

Takes ~1 second to
enter a passive state.

React as soon as
the user signals intent.

Mousedown gives you a
100-150ms head start.

:active animations
buy you more time

~200ms duration gives you a +50ms bonus

Works on touch devices too!

Begin on touchstart.

Cancel on touchmove.

Un-suck passive waits

You start to lose people
at ~1-4s of waiting.

Critical to get this right.

What we can do

  • The right loading animation can make time pass more quickly.
  • Adapt loading scheme to each user.
  • Distract with shiny objects.

The Perfect
Progress Bar

Uncertain waits feel longer.

David Maister

Progress bars give
users certainty.

Animation makes a difference.

Bars with accelerating
bands feel 12% faster.

But what about spinners?


Spinners aren't versatile

  • < ~1s? Don 't indicate loading.
  • > ~2s? Really need a progress bar.
  • Only useful between 1 and 2 seconds.

Real progress bars
can be overkill.

Most progress bars are fake.

Many are pretty 💩y.

Adaptive loading

Keep it real…ish.

Measure requests.

const API_EXPECTED_TIME = 1000;

function getImage() {
	let t0 =;
	fetch(`${GET_IMAGE_API_URL}`).then(response => {
		const apiRoundtrip = - t0;
		setPerformanceScalar(apiRoundtrip, API_EXPECTED_TIME);
		// Do stuff

function setPerformanceScalar(observed, expected) {
	this.performanceScalar = observed / expected;
	// ...
	// tuck this away in app state, local storage, a global variable, etc


Some tips

  • Set baseline times with your test runner.
  • Normalize for resource or payload.
  • Adapt your whole loading scheme.

Occupy Users'

Reticulating splines.

We can learn from gamedevs.

If this feels like cheating,
that's because it is.

You dirty cheaters, you.


React as soon as
users signal intent.

What if we could
predict their intent?

Gamedevs do this all the time.

You may have done it
without realizing.

Login form

~4+ second head start!

CTA Button

Exploiting behavioral quirks

  • People tend to watch hover & active animations.
  • People necessarily slow their cursor as it approaches a target.

People watch hovers.

Fancy hovers buy you
up to 600ms extra.

Cursors slow down.

Detect decelleration
with Futurelink!

Elaborate Hover + Futurelink
At least ~1.5 second head start!

Predictive preloading is a very powerful tool.

Use it wisely.

Mitigate risk with data.

Dry run first.


Perceived perf is
low hanging fruit.

Provide users immediate, accurate, live feedback.

Tailor your solution
to individual users.

React as soon as users signal intent; extrapolate where possible.

Introduce predictive preloading
bit by bit.

At the end of the day, what matters is how it feels.

Focus on the user.

Thanks folks!