← Back to the game

About Juggle Perfect

Why I built it

I love juggling, and I love getting better at things. I know I'll never be perfect, but I want to keep moving in that direction. Perhaps Juggle Better would have been a perfect better name, but it doesn't sound as good, does it?

The original name for this game was actually Juggle Hero, inspired by Guitar Hero. I know Guitar Hero didn't really teach you to play guitar, but I love the idea of a game where the controller is something from real life rather than a keyboard: something that gets you in the zone and makes you feel like you're getting better.

I loved Guitar Hero when I was a kid. Hopefully Juggle Perfect is almost as fun, but actually does teach you to get better at juggling. Anyway, here's how to play…

How to play

1. Get ready

2. Teach the game your ball (Colour)

Press Start. Hold your ball up and tap it in the camera picture. If your balls have panels in two colours, tap each colour, up to two.

3. Check it can see the ball (Tracking)

Throw a ball a few times. A trail should follow it. If it doesn't, go back and tap the ball again, or find better light. Press Looks good.

4. Place the rings (Confirm)

Drag the two rings to where your throws reach their highest point. In a cascade, your right hand throws to the left ring (L), and your left hand throws to the right ring (R). Set your speed in BPM (beats per minute): 160 is a throw about every third of a second. The game remembers all of this for next time.

LRyour left handyour right hand
A cascade: each hand throws across, so right-hand throws peak in the left zone and left-hand throws peak in the right.

5. Play

Pick how long to play (30, 60 or 120 seconds) and press Start. Juggle a three-ball cascade. A metronome ticks to help your rhythm, but you don't have to throw exactly on the tick: every good throw counts.

● Perfect: gold ring100 points● Good: blue ring50 points● Miss: outside bothbreaks your streak
Where the top of the throw lands decides the score.

Score throws in a row to build a streak. The longer your streak, the more each throw is worth: ×2 from 5 in a row, ×3 from 10 and ×4 from 15. If no ball reaches a ring for a couple of beats, the streak ends.

6. See how you did

At the end you get your Perfects, Goods and best streak, plus a snapshot of every scored throw so you can see what happened.

How does it work?

The game has no sensors in the balls. It only has your camera, so it works everything out by looking, a bit like you watching a friend juggle. Every picture goes through the same steps:

1. Cameraa picture2. Shrink320 × 2403. Find ballscolour + motion4. Name tagswhich ball is which5. Guess the toprainbow curve6. Score!Perfect / Good
What happens about 60 times every second.

Step 1: Shrink the picture

Each camera picture is squashed down to 320 × 240 dots. That's small, but a computer can check every single dot 60 times a second without getting out of breath.

Step 2: Find the balls

The game asks two questions about every dot:

  1. Is it ball-coloured?
  2. Did it change since the last picture? If so, something moved there.

Why ask about movement? Your orange T-shirt or an orange cushion could look ball-coloured, but they sit still. Flying balls don't. So something only counts as a ball if it's the right colour and moving.

🪄 Trick: colour by hue, not red-green-blue

Screens mix colours from red, green and blue light, but a ball in shadow and the same ball in sunlight give very different red-green-blue numbers. So the game turns every colour into hue (which colour it is), saturation (how strong) and value (how bright). Hue barely changes when the light does, so it's the best clue.
hueYour orange ballAny colour inside the whiteslice counts: 20° either side.Sunlight or shadow?Only the brightness changes.The hue, the ball's place onthe wheel, stays put.
Hue is where a colour sits on the colour wheel. Shade and sunlight barely move it.

Touching ball-coloured dots are gathered into one blob, like the paint-bucket tool in a drawing app. The middle of the blob is where the ball is.

🪄 Trick: the crescent moon

When a ball moves, only its front edge has changed since the last picture. The back half was orange a moment ago too. So “orange and moving” finds only a crescent moon, and its middle is in the wrong place. The fix: movement proves a ball is there, then the game looks around that spot for all the orange, to measure the whole ball.
Orange AND movingonly the front edge changed,so its middle is too far forward→Then: all orange nearbythe whole ball, middlein the right place
Movement proves a ball is there. Colour then shows where all of it is.

🪄 Trick: gluing blobs back together

A shiny highlight can split one ball into two blobs. Blobs whose middles are very close get glued back into one.

Step 3: Give each ball a name tag

The game needs to know which ball is which from picture to picture. In each new picture, every blob gets the name of the nearest ball from last time. A blob with nobody close enough gets a brand-new name.

picture 1ABpicture 2ABpicture 3AB
Each new picture, every blob gets the name of the nearest ball from the picture before.

🪄 Trick: being patient

Sometimes a ball disappears for a picture or two, hidden behind a hand, or blurry at the top of its throw. The game keeps its name tag for a few pictures before giving up.

Step 4: Guess the top of the throw (the physics bit!)

Anything thrown through the air follows the same curve: a parabola, the shape of a rainbow. Gravity pulls on it the same way every time. So while the ball is still going up, the game finds the rainbow that best fits the dots it has seen so far (mathematicians call this a least-squares fit). Then it works out where the top of that rainbow will be.

hand lifts the ball← release: moving up fastestpredicted topdots seen so farthe rest of the curve,worked out with maths
From a few dots on the way up, the game draws the whole rainbow and knows where the top will be.

The game scores your throw just after the ball passes the top, using the rainbow that fits its whole way up. It never needs a sharp picture of the top itself, so blurry pictures at the slow, fuzzy top of a throw don't spoil your score.

🪄 Trick: start the curve when the ball leaves your hand

Your hand lifts the ball slowly, then flings it. Once the ball is free, gravity can only slow it down. So the moment it's going up fastest is exactly when you let go, and the curve starts there. Each throw also starts fresh from the lowest point of the ball's path, so an old throw never gets mixed into a new one.

🪄 Trick: no impossible answers

If the maths says the top happened before the game even saw the ball, that's nonsense, so the guess is thrown out.

Step 5: Score it

Readyswitched on→Scored!switches off→Empty a momentswitches back on
One throw, one score, even if the game thinks it sees the ball twice.

🪄 Trick: not fooled by a lost ball

Balls often vanish for a moment at the top of a throw, where they're slowest and blurriest. That's fine: the game already has the rainbow from the way up, so it scores the throw from that. If it then finds the ball again, it might give it a new name tag and think it's a new ball. But a ball first spotted near the top hasn't risen far enough to count, and the ring has switched off anyway.

Step 6: Keep the beat

Juggling has a rhythm, like music. The beat drives the metronome and the pulsing rings, and it decides when your streak has gone quiet for too long. It never stops a good throw from scoring.

🪄 Trick: one clock, not two

Computers have more than one clock! The game once mixed up two of them, like timing one race with your watch and another with the kitchen clock, and quietly ignored most throws. Now the code won't even build if the clocks are mixed.

Tricks for phones

How was it made?

A web page

The whole game runs in your browser, so there is nothing to install. It is built with Next.js and React: kits for making pages out of reusable pieces, like Lego.

TypeScript

JavaScript with a spell-checker that spots mistakes before the game runs.

Canvas

A drawing board on the page. Each camera picture is copied onto it, then the rings, trails and “PERFECT!” are drawn on top.

The game loop

The browser taps the game on the shoulder about 60 times a second. Each tap it looks at the newest picture, finds the balls and checks the scores: a flip-book that thinks on every page.

Notebooks (Zustand)

Little stores that remember the score, the streak and your settings.

A browser pocket (localStorage)

Your settings are saved inside your own browser. No server, no account: nothing leaves your computer.

Robot testers

Vitest checks the small pieces, like the maths. Playwright opens a real browser and plays the whole game to check the right numbers come out.

Why build it this way?

There were lots of ready-made ways to build a game that watches you through a camera. Here are the big ones, and why we didn't pick them:

🏭 A game engine (like Unity or Godot)

Great for: A giant toy factory for making big 3D games, with physics, lighting and sound built in.

Why not here: Huge to download and slow to start in a web browser. We only need to draw some rings and flashes, which a web page already does.

🤖 A trained AI (like MediaPipe or TensorFlow)

Great for: A robot brain that has studied millions of photos, so it can spot hands, faces and bodies.

Why not here: Nobody has taught it about juggling balls, so we would have to train it ourselves. It is also heavier to run, which can make an older laptop slow and jumpy.

🕶️ An augmented reality kit (like ARKit, ARCore or WebXR)

Great for: Great at sticking pretend things onto the real world, like a dinosaur standing on your carpet.

Why not here: It is made for placing things in 3D, not for chasing small, fast balls, and mostly needs a phone app.

🧰 A giant vision toolbox (like OpenCV)

Great for: Hundreds of ready-made tools for working with pictures.

Why not here: We only need a handful of them, and carrying the whole toolbox makes the game slower to load.

Instead, we built our own, from the simple ideas on this page: look for the ball's colour, check it's moving, give it a name tag and draw a rainbow through its path. Why?

🪄 Trick: it did work, with some help

The simple way needed lots of little tricks to become reliable: the crescent moon, being patient with lost balls, starting the curve at release, and switching rings off. Most of those were found with the robot clown below. And because the tracking is kept separate from the rest of the game, a smarter ball-finder could be swapped in one day without rebuilding everything else.

The robot clown

Waving balls in front of a webcam every time the code changes gets tiring. So the game can pretend to be a camera: a cartoon clown in a top hat juggles for it. The game can't tell he isn't real, so every part gets tested. Because we know exactly where his balls go, we can check the game gets the right answer. That's how most of its bugs were found.

Try him yourself: open the game with the clown.

Let's juggle