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.
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.
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.
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.
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:
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. |
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.
I checked the network tab, and everything seemed fine. The response time was less than 300ms, which is considered instantaneous.
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.
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.
The spinner is only shown when the response time is more than 300ms.
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.