Wanna see something cool? Check out Angular Spotify 🎧

Perceived Performance: The psychology of waiting

I recently took the course Web Performance Fundamentals, which covered an intriguing topic about perceived performance. This concept revolves around the psychology of waiting and how we can make our websites feel faster to users.

Perceived Performance

Over the years, I’ve been deeply interested in web performance and how to make websites faster. I’ve used tools like Lighthouse, WebPageTest, and Chrome DevTools to measure and improve my site’s performance. However, I never really considered the perceived performance of my website. I always focused on actual performance metrics like First Contentful Paint (FCP), Largest Contentful Paint (LCP), and Cumulative Layout Shift (CLS). But perceived performance is just as crucial as actual performance. In this article, I’ll share insights from Todd Gardner’s course about the Psychology of Waiting and how we can apply these principles to make our websites feel faster to users.

Update 2024

I recently presented a 30-minute talk titled “Creating Fast-Feeling Web Apps” at JSConfJP in Tokyo, one of the largest web developer events in Japan. The feedback from attendees was overwhelmingly positive. You can view the slides and the recorded talk here: Creating Fast-Feeling Web Apps at JSConfJP.

JSConfJP 2024

The Psychology of Waiting

Todd Gardner referenced The Psychology of Waiting Lines by David Maister, which outlines eight key points. Here are six of them:

1. Wait Time is Subjective

Imagine going to an empty bar. You expect to be served quickly, but if the bartender is on the phone for a minute, it feels like a long wait because you have nothing to do. Conversely, in a busy bar with a bustling scene, the wait feels shorter because you’re engaged.

2. Bored Waits Feel Slower

Think about road trips before the era of smartphones and in-car entertainment. Staring out the window made the trip feel endless, even if it was just two hours.

3. Anxious Waits Feel Slower

Waiting for high-stress results, like visa approvals or loan decisions, feels much longer than waiting for something trivial. The uncertainty and anxiety make the wait unbearable.

4. Uncertain Waits Feel Slower

Not knowing how long a wait will be can make it feel interminable. Is it going to be a minute, five minutes, or an hour?

5. Unexplained Waits Feel Slower

If you don’t understand why you’re waiting, the wait feels longer. For example, if a bartender takes a long time without explanation, it feels frustrating.

6. People Will Wait for Value

People are willing to wait longer for something valuable. For instance, they’ll wait for a high-quality video to buffer but won’t wait long for a trivial news article.

Applying the Psychology of Waiting to Web Performance

Understanding these psychological principles can help us design web experiences that feel faster to users, even if the actual load times remain unchanged. However, before we dive into the solutions, let’s see how long is too long for users to wait.

Many cite Jakob Nielsen, who specifies three limits:

  • 0.1 second is about the limit for having the user feel that the system is reacting instantaneously, meaning that no special feedback is necessary except to display the result.
  • 1.0 second is about the limit for the user’s flow of thought to stay uninterrupted, even though the user will notice the delay. Normally, no special feedback is necessary during delays of more than 0.1 but less than 1.0 second, but the user does lose the feeling of operating directly on the data.
  • 10 seconds is about the limit for keeping the user’s attention focused on the dialogue. For longer delays, users will want to perform other tasks while waiting for the computer to finish, so they should be given feedback indicating when the computer expects to be done. Feedback during the delay is especially important if the response time is likely to be highly variable, since users will then not know what to expect.

Bucketing timeframes by attention span (based on Steve Souw’s definitions and research by Rina A. Doherty and Paul Sorenson) proves more useful when assessing performance depending on length of the operation and perceived complexity:

Attention Category Response time Description
Attentive Instantaneous below 300ms Perceived as closed-loop system, where people are in direct control.
Immediate 300ms–1 sec Perceived as easy to perform.
Transient 1–5 sec Perceived as requiring some simple processing, but people still feel like they are making continuous progress. People are unlikely to disengage from task flow.
Attention span 5–10 sec Perceived as requiring more wait time. People need informative feedback to stay engaged.
Non-attentive Non-attentive 10 sec–5 min Perceived as requiring more complex processing. People are likely to disengage and multi-task.
Walk-away above 5 min Perceived as requiring intensive processing. People won’t stay engaged with the task.

My Story with Perceived Performance

When I started my career as a frontend developer about 7-8 years ago, I thought I was doing everything right. Every time I fetched data, I showed a loading spinner. However, my boss kept complaining about how slow everything felt.

Perceived Performance

I checked the network tab, and everything seemed fine. The response time was less than 300ms, which is considered instantaneous.

Perceived Performance

The code itself was fairly simple, just a few states and a data fetch. What could be wrong?

export const useFetchVehicle = (vehicleId: string | undefined) => {
  const [vehicle, setVehicle] = useState<Vehicle | null>(null)
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  useEffect(() => {
    const init = async () => {
      try {
        const data = await fetchVehicle(vehicleId)
        setVehicle(data)
      } catch (err) {
        setError((err as Error).message)
      } finally {
        setLoading(false)
      }
    }

    init()
  }, [vehicleId])

  return { vehicle, loading, error }
}

I was confused. I thought I was doing everything right. However, my boss asked, “Why is the spinner on forever?”

After much discussion, I realized that the spinner was the problem. For a resource as simple as the vehicle list, the spinner should not be there. Showing the spinner is necessary if the user’s network is slow or our API server is slow. But most of our users have high-speed internet, and our API server is fast. So the spinner was not needed. We decided to only show the spinner if the response time was more than 300ms.

Perceived Performance

To implement this, I added a delay of 300ms before showing the spinner. If the response time was less than 300ms, the spinner would not be shown.

export const useFetchVehicle = (vehicleId: string | undefined) => {
  useEffect(() => {
    let timer: ReturnType<typeof setTimeout>;
    const init = async () => {
      try {
+        timer = setTimeout(() => {
+          setLoading(true);
+        }, 300);
        const data = await fetchVehicle(vehicleId);
        setVehicle(data);
      } catch (err) {
        setError((err as Error).message);
      } finally {
+        if (timer) clearTimeout(timer);
        setLoading(false);
      }
    };

    init();
  }, [vehicleId]);

  return { vehicle, loading, error };
};

The result was very satisfying.

The spinner doesn’t show up anymore when the response time is less than 300ms. Users feel like the website is faster even though the actual response time is the same.

Perceived Performance

The spinner is only shown when the response time is more than 300ms.

Perceived Performance

Conclusion

Perceived performance is just as important as actual performance. By understanding the psychology of waiting, we can design web experiences that feel faster to users.

Reference

Published 13 Jan 2023

Read more

 — zsh history not working after VSCode upgrade
 — The different between :focus and :focus-visible
 — Angular 13 upgrade - Error: Unknown keyword formatMinimum
 — Why Web Performance Matters: An Introduction
 — ngIf - Store the conditional result in a variable