Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: Looptap – A minimal game to waste your time (vasanthv.com)
1241 points by vasanthv on Jan 13, 2022 | hide | past | favorite | 170 comments

One of my favorite things to do with these neat little games is to build something that plays them for me by just pasting stuff into the browser JS console.


At a score of 1,619 the ball is moving so fast it no longer works! I switched to a new tab (hoping that the event loop would be sent to the background/context switch/whatever) and ta-da - it misclicked. High score of 1,637.

You beat me to it, haha, This is what I just did:

function intersectRect(rectA, rectB) { return !( rectB.left >= rectA.right || rectB.right <= rectA.left || rectB.top >= rectA.bottom || rectB.bottom <= rectA.top ); }

const arc = document.getElementById("arc"); const ball = document.getElementById("ball");

setInterval(() => { if (intersectRect(arc.getBoundingClientRect(), ball.getBoundingClientRect())) { window.dispatchEvent(new KeyboardEvent('keypress', { keyCode: 32, })); } }, 10);

My code seems to go on forever, closed the tab after it reached 60K

Oh neat! You did the Harder version and didn't use the game's own code to check it. I find that with JS games it's better to imitate the game's exact logic and call it's functions to make sure the state is working/updates correctly.

Of course, for how delightfully simple this one is, your approach is definitely faster/better!

Definitely seems like alfon's solution is faster/better, as it's more accurate when the game speeds up. However, it's also more resource intensive! getBoundingClientRect (used to at least, long time I go I did browser performance stuff) forces a new layout calculation each time it's called, which is one of the most expensive things you can do in the DOM, and can lead to jank in the website. Some more information here: https://developers.google.com/web/fundamentals/performance/r...

Just adding this information for the ones who don't know and like to know more about browser performance :)

It is a bit of a slippery slope, because at some point, you could just do "GameApp.AddScore(10000)" which is clearly not playing the game. The line of what's "fair" and what's not isn't too clear. I do think staying out of the game code is actually a fairly good line though.

EDIT: Looked down thread and basically saw a bunch of examples of just that!

I believe that getBoundingClientRect does not force a layout calculation by itself, it only forces queued recalculations to happen synchronously.

So calling it 100 times in a loop should be fast, but calling it 100 times in a loot that also updates the DOM should be cripplingly slow.

Thanks for the note!

What the hell is happening past the point of 2k where the ball remains in the centre? Is it all happening too fast for my tiny brain to comprehend?

This overwrites the games internal `arc` variable so the arc no longer updates, hence the forever-ness.

Here's my solution:

    loopTapApp.score = Number.MAX_SAFE_INTEGER;

high score!

Not anymore

loopTapApp.score = Infinity

loopTapApp.score = Infinity^Infinity

You want loopTapApp.score = Infinity*Infinity, yours is 0 because ^ is XOR na anything XORed with itself is zero

This effectively turns this game into a spinning animation that appears to get increasingly frustrated that whatever is being loaded hasn't completed yet

Correct.. what it is loading, is effectively your score though. Or rather, measuring it, which takes some time? No idea, this is nonsense:)

Thank you!

I didn't peek and ended up with the same solution as you, so for an extra bit of fun I tried to golf it to be as short as possible. There's probably room for much improvement: (94 chars)

setInterval("l=loopTapApp;b=l.getBallAngle();a=l.arc;b+6>a[0]&b-6<a[1]&&l.tap(new Event(1))")

Nice! Managed to shave off a couple of bytes:

  setInterval("l=loopTapApp;b=l.getBallAngle();[a,c]=l.arc;b+6>a&b-6<c&&l.tap(new Event(1))")

This entire comment thread is hilarious

Strangely enough this shorter version reads and clicks better into my tiny brain

This is neat.

Also, in retrospect, I probably should have read the code before pasting it into my console.

Code run in the console has the same restrictions as code on the page it is for so it's not really any riskier than clicking the post link in this case. For sites you have (or will have) a presence on (bank, HN, email, etc) it's a very risky idea though.

Most definitely :D

Pasting here in case the pastebin disappears:

  function autoPlay() {
    const fakeClick = {
        preventDefault: function() {}, 
        stopPropagation: function() {}
    const ballAngle = loopTapApp.getBallAngle();
    if (ballAngle + 6 > loopTapApp.arc[0] && ballAngle - 6 < loopTapApp.arc[1]) {
  const autoPlayer = setInterval(autoPlay, 10)

It's not exactly playing the game, but this was my approach:

    setInterval(() => {
     loopTapApp.arc = [ -Infinity, Infinity ]
     loopTapApp.tap({ preventDefault: () => {}, stopPropagation: () => {} })

This is just "console.log("You Won!");" with extra steps... /s

Isn’t everything though? “Finally, the box of blinking lights is showing the pattern of lights I want it to!”

1,665 and the dot is just sitting in the center where the play button goes.

Safari Version 15.2 (16612., 16612) macOS Big Sur 11.6.2 (20G314) iMac Pro 2017

Brilliant, thanks.

Are you usually able to do this when e.g. it's more of a SPA and you have to drill down more to get to the state of some deeply nested component?

I haven't really tried. Most of the time I do this and use the actual game functions for autoclicker games like e.g. Cookie Clicker. It makes progression a lot simpler and I get to see the "fun" aspects of the game (like the super weird late game stuff) without actually breaking the fundamental game code. I like operating within the game's own code boundaries.

Mine was this

    setInterval(() => {
      if(loopTapApp.getBallAngle() > loopTapApp.arc[0] && loopTapApp.getBallAngle() < loopTapApp.arc[1]) {
        loopTapApp.tap({preventDefault: ()=>{}, stopPropagation: ()=>{}});
    }, 50);

I got 1,683 in Firefox on a new M1 Pro MacBook Pro with your code… I'm gonna try the next guy's in Safari now. How optimal can I get haha!

Late to the party, but I prefer the more manual version:


that works. If there was a leaderboard, this thing will win for sure :)

this is the reason that most FOSS games (especially browser games in JS) don't really do well with a leaderboard. Still a neat trick to mess around with!

No requestAnimationFrame?

Basically the same in this case, as the setInterval is set with 10ms between firings, and requestAnimationFrame would (most likely, depending on computer/display) fire every ~16ms. It would be more efficient though, that's for sure, but most likely basically the same :)

sad. i got stuck in a loop at 1477. it just kept going round and round.

Mine gave up at 1683

Great stuff, got 315 on third try. I made a game in the same vein sometime ago: https://vladimirslepnev.itch.io/zigzag

One thing to note about this kind of endless runner games is the scoring system. Everyone uses the system "get as much points as possible in one run, each run starts from zero". But that forces people to slog through the early parts each time, and also makes them more likely to stop playing after an especially good run. I thought about this for a while, and came up with the idea that a better system would dock points for mistakes. The run would continue, a misclick would just give you a short breather and set you back fifty points or so.

I made some prototypes like that and the effect was striking: you stick with one run for a long time, falling into a kind of meditative state, never wanting to stop. Eventually you find an equilibrium where wins and losses balance out, and keep playing there, at the best of your ability. If I ever release an endless runner again, it will use that system.

It took me a while to understand what was going on with your game. Once I got it it was fun, though!

For anyone else confused: the goal is to keep the light colored "snake" moving through the dark colored "tunnel". When the snake stops moving, it's because it's at a junction in the tunnel. You're goal is to tell the snake which direction to go (either left or right) _from its perspective_.

Wow, that's a great test of spatial reasoning (or something akin to spatial reasoning). You kind of have to rotate the shape in your brain to figure out which direction to turn from the line's perspective. I've driven with people who can't follow GPS directions unless it's in "first person" mode, e.g. if the line is moving "down" but then heads to the "right", you actually need to turn left, but they think you need to turn right or are just confused in general because the orientation is different - and I wonder if they could train their brain to work that way by playing something like this.

Thanks, that helped a lot. I couldn't figure out how this worked without this tip.

so nauseating though. got a headache within seconds of playing from the constant and completely random camera movement

Hey, that's the Barkley: Shut Up and Jam: Gaiden title music! https://www.youtube.com/watch?v=lWKQiZVBtu4

I really like your idea for solving the "endless runner problem." I guess the reason we haven't seen mobile games adopt this already is because each restart is an opportunity to show you an ad. :/

Not that I endorse it, but you could do some (or all) of the following to preserve the suggested system while still having everyone's favorite size of transactions:

- Ads after X failures

- Spend Fun Points to avoid ads and prevent misclicks from subtracting your score

- Fun points can be bought

- or obtained by watching an ad

- or obtained very rarely through regular play (e.g. by spending many Fun Coins)

- Spend Fun Coins to customize your ball and background

- Fun coins can be bought

- or obtained by watching an ad

- or obtained infrequently through regular play

- Daily bonuses which scale with number of consecutive days logged in

- Fun Points and/or Coins obtained by referring a friend

et cetera

Aw nah, I hate it when people bootleg really good MODs and ruin th--


Oh ok. This is whatever the opposite of a bootleg is. It's something entirely new.

So I think this might be my new favorite thing, if for no reason than that I honestly cannot think of anything as straightforwardly and equivalently awesome. Perhaps the Bad Apple stop-motion PV (https://youtu.be/240Vq6tIxio) is vaguely in the ballpark.


Now to close it so I don't wear it out. :D

Cool game! I can definitely see the Super Hexagon inspiration :) Took me a few tries to get the hang of it (you need to reason from the perspective of the line, not the screen).

I thought the same while playing this game. When I reached 200 points I wondered how the play experience would change if by losing I just lost 100 points or so.

Well this is infuriatingly awesome :D

Really sad the Android build appears to have fallen off the Play store :/

I wanted to keep going just to hear more of the song!

that's a great little game!

This is a great little time waster!

Couple of recommendations, in no particular order:

1. Keep the ball moving even after you die. Instead of pressing start, the user will just click when the ball is back inside the colored area. 2. You can click anywhere to restart, but you have to click the play button to restart. You should allow the user to just click anywhere to restart. 3. When you fail, show where the ball was. 4. As for collision detection, I think you should err on being lenient and count success if the bounds of the circle overlap, rather than the center or entirely. I think this is also due to the rounded linecaps for the arc potentially not counting as part of the valid area. 5. Show where the next arc will be. This is more of a game design thing. If you do this, quick fade out of the old and fade in on the new would look nice.

I understand that this was probably a small little one off project, and that there's always more you can iterate on, so I just want to say nice job in putting out a fun little time waster!

6. Delay showing the share button. I kept clicking it accidentally after failing

Yes, but this almost seems intentionally genius at the same time.

Dark pattern though

Thanks, you've just wasted valuable engineering time of multiple people. The actual cost of this game is much more than what we realize.

Or not! They will completed the tasks in the last hour anyway. What's the cost of HN?

But can they capture that lost value? I suspect not.

It'd be more fun if you somehow got "bonus points" for reducing the time between clicks. I kept playing it safe trying to time the ball but it kind of felt like cheating!

That is how it works, I thought that was the whole point. It's a game about greed. You can either play it safe and go up 1 by 1, or go fast. The max you can get is 5 if you press immediately, seems to drop down to 1 after half a turn.

EDIT: Found the code

    (tapInterval < 500 ? 5 : tapInterval < 1000 ? 2 : 1)
So 5 points for under 0.5s, 2 points for under 1s, 1 point otherwise.

It does give you more points if you click fast.

It took me way too many loops to figure this out. I was thinking the same as GP, someone next to me already proposed a penalty for letting it slip a loop and wanted to fork it, but this whole time the game has been ahead of us!

A half point lost per revolution would spice it up.

This is in the event handler for the tap:

    this.score = this.score + (tapInterval < 500 ? 5 : tapInterval < 1000 ? 2 : 1);

So funny I missed this behavior. Maybe the feedback then is there could be more gradations/tweaking for us slowpokes out there (especially since it appears other folks missed it as well)

Also I should have lead my original comment with kudos to the maker, didn’t mean to only leave (apparently inaccurate) critical feedback!

I think it would be neat if you could get bonus points for multiple clicks within the same arc. So like if you click right when the ball enters the arc, and there's sufficient distance left before the end of the arc, you could click again (multiple times, quickly) and get bonus points until the ball reaches the end of the arc, at which point a new arc would be generated as usual. I just think it would be fun to line up a long arc and mash the space bar for a bit.

Feature request: show where the ball was on game over screen, because now it quickly resets to start position. Maybe show its "ghost" in different color or just outline.

Cool game! I know you're trying to keep it simple but a couple suggestions that might help and won't increase game complexity too much:

  1. Add a share button to share your score
  2. Add a timer component (can be hidden).  It will add urgency.
  3. Speed up the ball as time goes on (maybe i haven't gotten far enough for this to happen...?)
  4. If you're adding more points for "same rotation hit", make it clear that it's doing that and don't just add more points to the score (shake the number, throw some fireworks, or something).  Your focus is on the ball not the score so you won't even notice the number went up a little bit more than usual until you lose.

You use share buttons in games / on websites? I've never even clicked one, what even should that do, give a copyable text or a URL to a picture to share or so?

I personally don't but one look at wordle and you'll see that there are a lot of people that actually do :D

Wordle has the most fantastic share button I've seen. It copies a spoiler-free version of your game board into your clipboard so you can share as you wish, no links or growth spam. Normally I avoid the buttons because they just try to paste a link to my twitter profile.

Just tried it out!:

Wordle 209 6/6

⬛⬛⬛⬛ ⬛⬛⬛⬛ ⬛⬛⬛ ⬛⬛⬛ ⬛⬛

EDIT: the formatting doesn't seem to work here, green and yellow squares disappear after posting the comment.

On point 3, there are speed ups around 40, ~80, and on. The colors change at the same points.

Because it had to be done:

    let robot = function () {
        const ballAngle = loopTapApp.getBallAngle();
        const arc = loopTapApp.arc;
        if (ballAngle + 6 > arc[0] && ballAngle - 6 < arc[1]) {
            window.dispatchEvent(new Event('mousedown'));

    var timer = setInterval(robot, 10);

at ~1600 the ball started to spin too quickly for an intersection with that 10ms timer and there were 6 balls visible at the same time with my 144hz display. fun stuff.

3 visible at ~1600 here. I then set the timer to 1ms and it got a few more points and then put the ball in the very center of the circle. When I clicked, the game ended.

Why does this autoplay cause the ball to seemingly "jump/skip"?

It seems possible to click when the circle intersects the shape, but still lose. I guess it's based on the centre of the circle, but feels a little frustrating to me.

I’m of the opposite opinion. The entire circle should be in for it to count. Basing it off the center feels generous.

Great little fun game though!


I feel really superior when I succeed in clicking immediately after the previous click. I clicked on the target arc and was relieved. But I immediately realize the next arc is right there, or I'm even on the arc now! Then adrenaline kicks in, and I click again immediately! Don't think, feel! And it continues because I haven't missed it.

Like I was a top F1 driver...

Feature request:

Start the game by pressing "space" as well.

Added this :)

> Added this :)

Doesn't work for the first game yet.

Thank you

Nice! My grandma enjoys mobile games but is, uh, let's say she's not a pro just yet. This is something she can do! Let's see if she'll enjoy it :)

Without a "share" button that creates an emoji summary of your results, this will never get as viral as Wordle

Wow, I never noticed that

https://i.imgur.com/liFqyj3.png felt like i might have clicked too early, but it's showing in on game over


Feature Request: I'd love to see the ratio of successful points to number of times the ball traverses the circle. I think others have already said things about scoring mechanisms for faster taps, I wonder if there could be one around a sweet spot on the line (earlier is better? Later?!)

Well done, you've made me late for a meeting!

This is well made! Easy to understand, challenging, and a hint of variable reward.

Simple addictive games like this always remind me of that ball-in-a-cup game that everyone got addicted to in that "The Game" episode of "Star Trek: The Next Generation".

Every time I rewatch that episode it reminds me about when smart phones and social media first swept through, turning people into zombies staring at their phone screens and barely interacting with people physically around them.

Good game though there seems to be a bug where if you tap twice quickly, not even a doubt tap but just relatively quickly, it doesn’t register the second tap.

Safari, iOS (whatever the latest update is)

For autoplay, paste this into the console and click the play button.

  setInterval(function() {loopTapApp.arc=[0,359];loopTapApp.tap(document.createEvent('Event'))},5);

There's a pastebin from another user that actually "autoplays" the game.

This version is more of a hack/cheat as it doesn't obey the rules of the game and just fires the tap event all the time.

Cool. I haven’t read the comments but I did find a possible bug. If I successfully click in the painted area the generation of the next painted area may land on top of my current “cursor”. I wasn’t quick enough to click it and the game didn’t fail me ( it let the cursor go around the perimeter again at which time I clicked when the cursor was over the painted area and was awarded a point ). Might not necessarily be a bug but it adds unnecessary ambiguity to the game.

I feel like the variation in placement and length helps. Or, it pushes towards the "randomness" that makes some skinner boxes more sticky than others.

Missed opportunity to name it "ball tap".

"tappy ball"

A play on "flappy bird".

Nice! Reminds me of the Cyclone / "stop the light" arcade games. If anyone wants to play a similar game on iOS, I made one about 7 years ago called hyper•loop https://apps.apple.com/us/app/hyper-loop/id944029806

Played twice. Immediately knew that I should stop.

Great game!

This reminds me of this cat toy - the circle with a ball in it (or at least it made me feel like a cat for a minute)


I think the score should go down when the ball makes a full revolution. Encourage more same revolution taping

``` loopTapApp.startPlay(); setInterval(() => { if(loopTapApp.getBallAngle() > loopTapApp.arc[0] && loopTapApp.getBallAngle() < loopTapApp.arc[1]) { loopTapApp.tap({preventDefault: ()=>{}, stopPropagation: ()=>{}}); } }, 50); ```

This is brilliant!!

I made a game quite similar that took me years to complete. And I feel this is much more immediately engaging, could just be me being hyper critical of my own work...but its always amazing to see how other designers approach similar ideas.

Either way I love it!!!!

There might be a bug. I “died” but the ball appears in the live zone https://i.imgur.com/XJdKgDL.png. Happened to my partner too.

The ball resets to that 3 o'clock position when you lose, so this doesn't show where it was when you clicked.

A cool variation would be to show the bar that's up next in a faint outline.

Great lil game! The color is just gray after 550 taps, maybe rotate the colors?

Cool game, works nicely on my android phone.

One thing that you could improve is add user-select:none to the page. Quickly tapping the document did select the counter text and opened a google mini popup, distracting from the game.

Well done!

The ryhtm should accelerate each N clicks so that the game eventually ends. Otherwise no matter how many points you get by clicking fast it's possible to accumulate the same amount by playing it safe.

It does. If you use the "autoplay" code elsewhere, it definitely starts speeding up.

Fun! 171 on my second round.

So well done.

I love these styles of development where it can be thrown together quickly but complete the full spectrum of ux, creativity, and hitting that sweet spot that felt satisfying to play.

Seriously well done!

Sometimes it increases the hit counter by one, others by two - why?

Pretty sure you get two points if you hit it quicker. So if you let the dot rotate around to get the segment the next time you miss out.

You can get more than 2 if you hit two in quick succession. I think the max is 5 (tested with blind double-clicks until I hit two in a row).

You get 5 points if you tap twice within 0.5 seconds, and 2 if the interval is within 1 second. The source code is very readable.

    this.score = this.score + (tapInterval < 500 ? 5 : tapInterval < 1000 ? 2 : 1);

Seems to be correlated with how much time passed between two hits.

Love the mechanic where the more quickly you click the next one the more points you get.

There should be a timed mode to encourage rapid clicking (i.e., score the most points within 30s or 1min).

Not a bad time waster. Simple. Could be used for office fun as well.

"Martha. If you beat my score on 1st attempt, I'll do those TPS reports." lol.

Good game, clever concept, appreciate the simplicity.

This was really stimulating for my ADHD. Hyperfocusing my high score was 1300.

Make this an iOS game!

Does anyone have any interesting iOS recommendations for ADHD stmming?

Here's another one: https://netspeed.ichi.city/

How do you come up with this stuff? I need answers.

There should be a score multiplier that builds up by getting points in quick succession. That would add a risk/reward element to it.

Request: add info about how many loops game had. less loops should add bonus points or more loops should have penalty points.

Otherwise fun game :)

Cool game, but the collision detection needs to work better. Currently can lose if partially touching, does not seem intuitive.

Disagree, should be within the bounds. Git gud.

Should get marginally faster every 10 clicks...

Some sort of measurement or score for more hits per unit time would be cool. Overall, fun little game, nice job.

You already get more points of you click correctly before a full rotation.


Keep this one advancing. Great job with maintaining simplicity. Lots of forks will come your way

just repaired http://lalo.li/ddd/ as prevent default changed behaviour on event listeners some time ago (from default active to passive, needed to set them to passive)

Cool! Reminds me of defusing mines in a game I used to play. Can't remember the name.

Couldn't add my score because twitter doesn't like what I have to say.

Reminds me a hamster game from back in the gay - the hamster would go in a loop.

Ouch, eyestrain. Not really a complaint, but a limitation of my own physiology.

it remind me of Super Hexagon :D

You could change the dot rotation direction on each success ...

Great game! Suggestion, penalize each rotation that does not have a click.

This is very similar to the popular game "Pop the lock"

When you come to the white beans it becomes sooo difficult.

This was delightful, and now I will get no work done today.

Oh hey, it’s one of Fable’s mini games! Very cool to see

This is great! You've got something here

Right click resets it, otherwise good game!

I love this. Added to my home screen.

Great, free dopamine for my ADHD!

I love it, accurate description

This game is hooking!

Simple yet addictive


Nice minimal game!


Best: 1


Nice one

Nice game!

maybe rotate the colors?

so good

you can also try the minimal game to waste your time https://www.snapfeel.com/share/post/?id=116

it's basically a clone of what is already playable in the link they provide

literally the same

what is the point other than pure 100% identical copy

no shame?


Well, I don’t need to download an iOS app to play OP’s game.

you don't need to download anything, the game is playable on that page

click and test by yourself: https://coogyloop.com/

I see, it only has that demo on desktop.

On mobile, it just presents a link to the app store.

Awesome work! I strongly recommend monetizing this. If this was a native iOS/Android app, you could show an interstitial ad after each Game Over and offer a small in-app purchase to remove all ads. It worked for 2048, and I believe it would work here as well!

Addictive drop-dead simplicity is repeatable recipe for success in games. I'm working towards this same strategy in my own projects, but I think you've really nailed it here!

EDIT: Whoops, I just noticed that this was based off such an app. Still, great work on the implementation!

You have to get a lucky/viral head start though, because the moment you monetize it and it proves successful you'll immediately have a bunch of clones competing from Zynga, Ketchapp, etc. That's what happened with 2048 as well as Flappy Bird.

looking forward to the trashy ripoff ads on youtube

Nothing gets a one-star/delete from me faster than grubby monetization efforts like interstitial ads. Banners below the game, removable via a one-time IAP, are one thing, but an ad that takes over the entire screen for half a minute just makes the whole game seem shabbier. Especially considering that the ads displayed are invariably horrible.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact