Interactive loading animation: a Rubik's cube that solves as the page loads
Endi Saliaj 10 min read
Nobody likes a loading state. How I designed an interactive loading animation: a Rubik's cube that solves itself as the page loads. Live demo inside.
Loaders are a pain. Nobody opens an app to look at a spinner. Every second on a loading screen is a second the person did not ask for.
As designers, we usually can’t make the server faster. That is work for the developers. But we decide what people see while they wait, and what they see changes how long the wait feels. So a loading state is not “put a spinner there and move on”. It is part of the design, and making it better is part of the designer’s job.
This note is about one experiment: a loader with a Rubik’s cube. The cube solves itself while the page loads. When the cube is almost solved, the loading is almost done.
Why waiting feels so long
In 1985 David Maister wrote a short paper, The Psychology of Waiting Lines. It is about people in lines at banks and airports, but it fits screens just as well. Three of his ideas matter here:
- Empty time feels longer than busy time. When there is nothing to look at, every second is long.
- A wait with no end in sight feels longer than one you can measure. “Almost done” is easier than “who knows”.
- A wait with no reason feels longer than one with a reason. People relax when they know what is happening.
A spinner fails all three. It gives you nothing to do, no end and no reason. It only says “wait”.
Research on screens says the same. Nielsen Norman Group reports a study where people who saw a moving progress bar were happier, and were ready to wait about three times longer, than people who saw nothing.
Spinner, skeleton or progress bar: which one when
Before you design a playful loader, pick the right kind of loader. The length of the wait decides. These numbers come from Jakob Nielsen’s response time limits and Nielsen Norman Group’s advice on skeleton screens.
| How long it takes | What to show |
|---|---|
| Under 1 second | Nothing. A loader that flashes for a moment looks like a glitch. |
| About 2 to 10 seconds | A spinner, or a skeleton screen: grey shapes in the place of the content that is coming. |
| More than 10 seconds | A progress bar that shows how much is done, and a line that says what is happening now. |
Spinner or skeleton? A skeleton shows the shape of the page before it arrives, so nothing jumps when the content lands. Use it for a whole page or a list. A spinner is better for a small part of the screen, like a button.
An interactive loader belongs in the last row. It is for long waits that don’t happen often: the first time an app opens your workspace, a big export, or an AI that builds a whole page. These waits are long enough to get bored, and rare enough that a little play doesn’t get old.
The idea: the cube is solved when the loading is done
A progress bar measures progress with a number. A Rubik’s cube also shows progress, but you see it with your eyes. Almost everyone knows what a solved cube looks like: six sides, one color each. A mixed cube looks like chaos. A cube with only a few wrong pieces looks “almost there”.
So I joined the two. The cube starts mixed up and needs exactly 20 quarter turns to be solved. A quarter turn is one side of the cube turning 90 degrees. Each turn moves the bar by 5%. After 20 turns the cube is solved, the bar is full and the workspace is ready.
The loader shows the progress twice: as a number for people who read, and as a picture for people who only glance. You don’t need to read “65%” to know you are close. You see that most sides are nearly one color.
The short line under the bar says what is happening: “fetching projects…”, “aligning layers…”, “matching colors…”, “turning the last corner…”. Each line is about the app and about the cube at the same time. On the right is the current turn in cube notation, the way cube players write moves (R is the right side, U′ is the top side turned the other way), and a counter like 7/20.
How the cube knows how many turns are left
Solving a real Rubik’s cube takes a clever program. I didn’t want one. I used a simple trick instead:
- Start with a solved cube.
- Mix it with 20 random turns. Two turns in a row never use the same side or the side opposite it, so no turn cancels the one before.
- Keep the list of turns.
- To solve the cube, play the list backwards and turn each side the other way.
After exactly 20 turns, the cube is solved again. There is no solver and no guessing. It also keeps the cube honest: turn 10 of 20 is always 50%.
Real loading is not smooth
A fake loader moves at one speed. Real loading does not. Data arrives in bursts. Sometimes 15% comes at once, and sometimes nothing comes for two seconds. The cube follows the real progress with two rules:
- It never runs ahead. A turn starts only when the loading has really earned it. The bar never shows more than what is done.
- It catches up. When the progress jumps ahead, the turns get faster: from half a second per turn down to about a quarter of a second. So the cube is never far behind.
Each turn goes a little past 90 degrees and then settles back, like a real cube that clicks into place. It is a tiny detail, but it makes the turns feel like a real object in a hand.
When the loading gets stuck
This is the moment people hate most. The bar stops, and they ask: “Is it broken?”
A spinner that keeps spinning while nothing happens is a small lie. A bar that freezes looks like a crash. I wanted a third way.
When no progress comes for about half a second, the cube starts to think. It turns over slowly in the air and tips forward and back, like someone who holds a cube and looks for the next move. When the progress comes back, it stops in its normal position and makes the next turn.
So a stall looks like thinking, not like a crash. People still see that the loading has paused, because the bar doesn’t move. But the screen is alive, so they don’t worry.
A small reward at the end
When the last turn lands:
- the bar turns green and a check mark pops in,
- the title changes from “Loading workspace” to “Workspace ready”,
- the cube jumps and spins once,
- the line under the bar says how long it took, like “solved in 20 moves · 6.4s”.
The whole celebration takes about one second. It has to be short. People came for the workspace, not for the cube.
Give the hands something to do
An interactive loader gives something to the hands, not only to the eyes.
- Click the cube and it mixes itself with a new scramble, then solves again.
- Move the mouse and the cube tilts a little toward it, as if it looks at you.
- On the full page there is a slider to drag the cube through all 20 turns by hand. Open it full screen.
None of this is needed. People who ignore it lose nothing. People who play with it get a few seconds that feel shorter.
How it is built
- HTML and CSS 3D, no WebGL. The cube is 26 small cubes (cube players call them cubies). Each one is a div with six faces, and only the outer faces get a sticker. The page, with all its HTML, CSS and JavaScript, is about 28 KB.
- Light without a 3D engine. On every frame, each face checks which way it points. Faces turned away from the light get a little darker. This is what makes the cube look solid and not flat.
- One number drives everything. The progress is counted in turns, from 0 to 20. The bar, the percent, the turn name and the cube all read this one number, so they can never disagree.
- A pile of work behind the card. Two faint cards sit behind the status card, like tasks that wait their turn.
The demo makes up its own progress. In a real app, you give it the true number, from 0 to 1:
// Each time a file arrives or a step on the server is done:
rubikLoader.setProgress(loaded / total);
The cube does the rest. It turns when a turn is earned, thinks when nothing comes, and celebrates at 1.
Accessibility and reduced motion
A playful loader still has to work for everyone.
- Screen readers hear a real progress bar. The card has
role="progressbar", and its value updates with every percent. When the loading is done, a polite message says “Workspace ready” once. - Keyboard. The cube is a button. Tab to it and press Enter or Space to play again.
- Reduced motion. If the system is set to reduce motion, the cube doesn’t float, sway, think or spin. It only makes its 20 turns, because those turns are the progress.
- The bar alone is enough. Even if you never look at the cube, the bar and the percent tell the whole story.
When not to use an interactive loader
A loader like this is not for every wait.
- Not for short waits. In under two seconds the cube would only start to turn. Show nothing, or a small spinner.
- Not for waits that come all the time. The first time it is fun. The fiftieth time it is in the way. Use it for a first load, an export or a big AI job, not for every click.
- Not for serious moments. A payment, a medical result or an error is not the place for a toy.
- Never slower than the real work. If the data is ready, show it. Don’t make people watch the end of an animation.
- It can’t fix a slow product. First make the wait shorter. Then make what is left feel better.
Loading state UX in short
- Under 1 second, show nothing.
- For 2 to 10 seconds, show a spinner or a skeleton screen.
- For longer waits, show real progress and say what is happening.
- Give the eyes something to watch and the hands something to touch.
- When the loading gets stuck, show that the app is still alive. Don’t freeze, and don’t fake it.
- End with a short, clear “done”.
Loaders will always be a pain, and people will never love them. But a good loading state turns dead time into a small moment, and that is part of the job. If you like small interactions like this one, the note on slider microinteractions is built the same way. And if your product has a long wait that needs to feel shorter, this is the kind of work I do as a UX/UI designer.
The piece
More notes