n-minute read: calculating an average reading speed

I’m interested in measuring what effect, if any, displaying an approximate reading-time for articles will have on readers of my site1. Will people be put off by knowing in advance that an article will take a long time to read? Will they gravitate to shorter pieces, or avoid them because they’re looking for more in-depth content? Will it have no effect at all?

My hypothesis is this: adding a read-time to post summaries will increase click-throughs from the archive page to full-post views. It reduces the “mystery meat” problem by providing users with objective data about their options, meaning they are better prepared to make a decision about which post to read.

Because this is an increasingly common idea2, there are a great many examples of reading-time-calculation functions out there. I’ve borrowed and adapted concepts from all over the place, but diverged from most I saw in two main ways.

  • Firstly, where others have focused on concision, I have opted for a more verbose style. One of my goals in writing pieces like this is to fully understand the code I’m working with. Breaking down each discrete operation into its own line (and compulsively commenting everything) ensures I know what every part of the code is doing (and hopefully makes it easier for readers to understand, too).
  • Secondly, I’ve chosen to separate the calculation-logic from display-logic. The working-out of the length of time the content will take to read just needs to output a number. Turning that number into human-readable text is another process, and thus another function in my solution.

The “Separation of Concerns” is a fundamental programming concept, and ties in nicely with the WordPress rubric: content-manipulation is for plugins, display-stuff is for themes. If I were planning to release this code, I could make a plugin to handle the calculation and let theme creators outputting the result any which way they choose.

Calculating the time

Here’s my final function for calculating the read-time in seconds. This example is written in PHP, but only because I’ll be using it in WordPress. The general gist (count the words, divide by the reading-speed) can be applied to any language.3

function reading_time( $content ) {

    // Predefined words-per-minute rate.
    $words_per_minute = 225;
    $words_per_second = $words_per_minute / 60;

    // Count the words in the content.
    $word_count = str_word_count( strip_tags( $content ) );

    // How many seconds (total)?
    $seconds = floor( $word_count / $words_per_second );

    return $seconds;

Displaying the time

When it comes to outputting this calculation to the screen, there are a few steps we need to take. Firstly, my guess is that very few people have an innate understanding of time when it’s expressed as seconds. Thirty-seconds is a chunk of time I can comprehend, but when we go much above sixty, my instant-comprehension drops dramatically. Without thinking, do you know how long 438 seconds is? I certainly don’t.4

The simplest option would be to convert our time-in-seconds into mm:ss format. And “Minutes and seconds” is a pretty standard way to show short periods of time, so it would solve the intuitive-comprehension problem. In essence, this boils down to converting a decimal number (in base ten) to a sexagesimal number (base sixty).

Getting the number of minutes is easy: we just divide the number of seconds by 60 (the number of seconds in a minute), and round down to the nearest whole number:

$minute_count = floor( $seconds / 60 );

Getting the number of seconds is a little tricker, but here modular arithmetic is our friend. What we want is the remainder (a.k.a. the modulus) from the minute-calculation, which we get by using the modulo operator:

$minute_remainder = $seconds % 60;

Getting fancy with our output

Simply showing users the minutes-and-seconds that our calculation spits out doesn’t feel right to me. This is, after all, a very rough approximation of reading-time, as no two people read at exactly the same speed. I’d far rather show a more human result.

The simplest (but most verbose) way to get the effect I was after was to simply set a custom message if the number of seconds fell within a certain range:

if ( $seconds < 30 ) {

    $message = 'hardly any time at all.';

} elseif  ( $seconds < 50 ) {

    $message = 'less than a minute.';

} elseif  ( $seconds < 55 ) {

    $message = 'nearly a minute.';

// ...

But this could get tiresome pretty quickly, and I much prefer a more dynamic approach. After a bit of tinkering I finally settled on a system that created a message based around how close the total-length was to a round minute. If the result is within two seconds of a minute (e.g. 122 seconds, or 239 seconds), then the result reads “$minute_count minutes, on the nose”, and so on and so forth.

// ...
} elseif ( $minute_remainder < 2 || $minute_remainder > 58 ) {

    // If we're within +/- 2 seconds of a minute:
    $message = $minute_count . ' minutes, on the nose.';

} elseif ( $minute_remainder > 50 ) {

    // If we're within less than 10 seconds short of any minute:
    $message = 'just shy of ' . $minute_count . ' minutes.';

// ...

The last challenge was to convert the raw integers (1, 2, 3…) into strings ("one", "two", "three"…). To do this I’ve simplified Karl Rixon’s much more comprehensive solution. His function handles virtually every number conceivable, with support for positives, negatives, decimal-points – the works! All we need to do for our current goal is convert the basic integers into strings, and we don’t even need to go up very far up the number-line, either. It’s unlikely I’ll be writing anything that takes more than ten or fifteen minutes to read.

$dictionary  = array(
    0   => 'zero',
    1   => 'one',
    2   => 'two',
    3   => 'three',
    4   => 'four',
    // ... etc.

$string = $dictionary[$number];

View the full convert_number_to_words() function I used in this Gist.

Putting it all together

With all our functions created, all that’s left is to put it all together and hook the whole thing up to the content we want to measure (in this instance the WordPress post content):

// Get reading time.
$reading_time = reading_time( get_the_content() );
$reading_time_string = parse_read_time( $reading_time );

Whether or not this will actually achieve anything remains to be seen. Will people click-through more when they know how much time they’ll need to commit up-front? Maybe, maybe not. Either way, I’ve enjoyed diving into the problem (probably too deep).

  1. Medium is the obvious example, but I’ve seen it used effectively on other sites, too. The most recent example I saw was on the Boagworld site. 
  2. This is, of course, on a very trivial scale. But the idea that displaying more information (provided it’s easily digestible) leads to more decisive action by users has wider implications. Most of my day-to-day work is focused around “moving the needle” on conversion rates. A percentage-point increase in click-throughs to a checkout represents a Job Well Done. 
  3. Here we’re using the PHP’s native str_word_count to count the number of words in a string. If you’re using JS, you can get the same effect by getting the .length of a .split(" "). A quick Google will probably reveal similar functions for whichever lang you prefer. 
  4. A big assumption in this project is that there is a uniform reading rate, which there really isn’t. Most code examples I looked at stuck to a standard words-per-minute rate of 200, but I (very unscientifically) tested my own reading-speed and found it to be closer to 300wpm. The real task at hand here is to give people a rough-estimate, so erring on the side of caution (going at the pace of the slowest) is probably the best option. I eventually settled on 225wpm as a usable middle-ground.