The Code of No-Code

Feb 06 2014

Sometimes you just need a progress bar. While working on a HTML5 game last year we had the issue where we were making an ajax request for two very large files and then processing them both. Roughly the times were a minute for each file on a slow connection and then another 30 seconds to process each file. The first problem was we needed a progress bar quickly. Our second problem was each jump, assuming I wrote a proper progress bar, would be 25% and the time between might not solve our last problem. The user needed something to indicate progress was being made. So I suggested we fake the progress bar. The funniest thing was after I implemented what I'm about to describe, both my manager and the client commented that "it seems to load faster now". Like I said, sometime you just need a progress bar.

How do you fake a progress bar?

What you need is a function that never reaches 1.0 and a good guess as to what a "long" time is for your process. In our case I used a variation of 1-(1/t) and adjusted so that it reached 80% after 60 seconds.

start = Date.now()
el = document.getElementById('progress-bar')
rate = 4 / 60000
done = 0
interval = setInterval(->
  elapsed = Date.now() - start
  progress = 1 - (1 / (elapsed * rate + 1))
  if (done > 0)
    if (done == 4)
      progress = 1.0
      clearInterval(interval)
    else
      progress += (1 - progress) * done / 4
    done++
  width = Math.floor(progress * 400)
  el.style.width = width + 'px'
, 250)

# Here we fake the process finishing early in 45 seconds
setTimeout(->
  done = 1
, 45000)

Here's a working copy where I've adjusted the times to 80% in 30 seconds and the process finishing early in 20 seconds:

What exactly is going on?

The first thing we need is to adjust our equation. The thing to note is that t in 1-(1/t) must be greater than 1, so first we adjust our equation

1 - 1 / (rate * time + 1) == 0.8

and solve for rate as a function of time

rate = 4 / time

You'll notice in the above code the fake finish in 45 seconds. This starts a counter done that will take the next 1 second (4 * 250ms) to finish up our progress bar and then clear the interval. This is accomplished by taking the remaining progress 1 - progress times the number of 4ths done / 4 and adding this to progress:

progress += (1 - progress) * done / 4;

Further thoughts

Because we were short on time I choose a simple equation (the first one that occured to me) and the simplist implementation. It's possible this could be cleaned up even further.

For one, since we knew 4 distinct things that needed to complete I could have implemented the fake progress as a sub-progress for each of the main "things" that completed, moving the bar 25% for each one that finished, but using a 1-(1/t) type equation to keep the bar moving during the load and processing stages.

It's probably just the fact that I had to watch it so many times during development and test, but after a while it starts to look fake. I could, also, have added a bit of "noise" to the progress bar movement to make it look a little more random.