Back

Show HN: Boing

458 points10 hoursboing.greg.technology
junon9 hours ago

Love this. Had to cheat, naturally.

    setInterval(()=>{const canvas=document.getElementById('canvas');const startX=266;const startY=198;const rect=canvas.getBoundingClientRect();const startClientX=rect.left+startX;const startClientY=rect.top+startY;let endClientX,endClientY,distance;do{endClientX=Math.random()*window.innerWidth;endClientY=Math.random()*window.innerHeight;const dx=endClientX-startClientX;const dy=endClientY-startClientY;distance=Math.sqrt(dx*dx+dy*dy)}while(distance<25);const dispatchMouseEvent=(type,target,clientX,clientY)=>{const event=new MouseEvent(type,{view:window,bubbles:true,cancelable:true,clientX:clientX,clientY:clientY,screenX:clientX+window.screenX,screenY:clientY+window.screenY,buttons:type==='mouseup'?0:1,button:0});target.dispatchEvent(event)};dispatchMouseEvent('mousedown',canvas,startClientX,startClientY);setTimeout(()=>{dispatchMouseEvent('mousemove',window,endClientX,endClientY);setTimeout(()=>{dispatchMouseEvent('mouseup',window,endClientX,endClientY)},1);},1);},1);
InsomniacL3 hours ago

Waiting for somebody to write the code to recreate the Star Wars Imperial March: https://www.youtube.com/watch?v=-NDLlWtudpE

blensor3 hours ago

I don't think you need to move the mouse, you just need to click slightly offcenter while still bein inside the ball.

junon1 hour ago

I was looking at the source and it appeared there was special handling on the mousemove event. I also had to introduce timeouts, otherwise it wouldn't work (not entirely sure why). So was being safer since I didn't want this to become a time sink :D

Bewelge3 hours ago

Almost gave up getting this to work...

(function () { function rateToDistance(rate) { const minR = 0.09; const maxR = 4.65; if (rate < minR) rate = minR; if (rate > maxR) rate = maxR; const t = (rate - minR) / (maxR - minR); return 400 * t; } function dispatchMouseEvent(type, target, clientX, clientY) { const event = new MouseEvent(type, { view: window, bubbles: true, cancelable: true, clientX, clientY, screenX: clientX + window.screenX, screenY: clientY + window.screenY, buttons: type === "mouseup" ? 0 : 1, button: 0, }); target.dispatchEvent(event); } const canvas = document.getElementById("canvas"); function triggerPull(distance) { const rect = canvas.getBoundingClientRect(); const startX = 266; const startY = 198; const startClientX = rect.left + startX; const startClientY = rect.top + startY; const endClientX = startClientX + distance; const endClientY = startClientY; return new Promise(resolve => { dispatchMouseEvent("mousedown", canvas, startClientX, startClientY); setTimeout(() => { dispatchMouseEvent("mousemove", window, endClientX, endClientY); setTimeout(() => { dispatchMouseEvent("mouseup", window, endClientX, endClientY); resolve(); }, 50); }, 50); }); } const semitones = 12; const notes = { G: Math.pow(4, -9 / semitones), A: Math.pow(4, -7 / semitones), B: Math.pow(4, -5 / semitones), C2: Math.pow(4, -4 / semitones), D2: Math.pow(4, -2 / semitones), E2: Math.pow(4, -0 / semitones), F2: Math.pow(4, 2 / semitones), G2: Math.pow(4, 4 / semitones), }; async function playWithPitch(rate) { const r = rateToDistance(rate); await triggerPull(r); } async function playScale() { const qrt = 200; const hlf = 400; const fll = 800; const pause = 15; const playNote = async (note, dur) => { await playWithPitch(note); await new Promise(res => setTimeout(res, dur)); }; const loop = async () => { await playNote(notes.C2, fll); await new Promise(res => setTimeout(res, pause)); await playNote(notes.D2, fll); await new Promise(res => setTimeout(res, pause)); await playNote(notes.G, hlf); await new Promise(res => setTimeout(res, pause)); await playNote(notes.D2, fll); await new Promise(res => setTimeout(res, pause)); await playNote(notes.E2, fll); await new Promise(res => setTimeout(res, pause)); await playNote(notes.G2, qrt); await playNote(notes.F2, qrt); await playNote(notes.E2, qrt); await new Promise(res => setTimeout(res, pause)); await playNote(notes.C2, fll); await new Promise(res => setTimeout(res, pause)); await playNote(notes.D2, fll); await new Promise(res => setTimeout(res, pause)); await playNote(notes.G, fll); await new Promise(res => setTimeout(res, pause)); await new Promise(res => setTimeout(res, pause)); await playNote(notes.G, qrt); await playNote(notes.G, qrt); await playNote(notes.A, qrt); await playNote(notes.C2, qrt); await new Promise(res => setTimeout(res, pause)); await playNote(notes.C2, qrt); await new Promise(res => setTimeout(res, pause)); }; await loop(); await loop(); await loop(); } playScale(); })();

jesse__8 hours ago

take my upvote. That's hilarious

meesles7 hours ago

BoingBoingBoingBoingBoing

ngkw7 hours ago

I love this.

coffeecoders7 hours ago

Funny thing: this feels "realistic" because it’s not perfect physics. A perfectly simulated Hooke's law spring actually looks fake and too stiff. But if you let the animation wobble a bit more and slow down the damping, our brain reads it as weight and squishiness.

It’s basically controlled sloppiness.

dataflow4 hours ago

> this feels "realistic" because it’s not perfect physics. A perfectly simulated Hooke's law spring

Confused. Perfect physics means perfectly simulating reality, not perfectly simulating an unreal idealized formula. Are you saying Hooke's law doesn't feel realistic or are you saying a simulator for a realistic spring doesn't feel realistic?

skrebbel4 hours ago

Elasto Mania is a great game from decades ago (but still for sale!) that exploits this fact to a hilarious extent. You control a motor bike with excessively wobbly physics making all kinds of stunts possible (and necessary, to complete the levels) that are spectacular and surprising.

https://elastomania.com/

dom962 hours ago

I remember playing this game when I was like 12 years old, good times

ngcazz4 hours ago

Also worth checking out, the FOSS clone X-Moto

finger4 hours ago

I spent thousands of hours on that game.. just too good :)

iamflimflam16 hours ago

Same is true in a lot of old platformer games. Real physics feels horrible.

faeyanpiraat2 hours ago

Yeah, I really like the low gravity during my dreams

e1gen-v1 hour ago

If you manage to push it all the way down directly in the middle it boings forever

mythz4 hours ago

This takes me back a few years when the first of my Uni friends had a baby, they spoiled him with so many toys that their lounge room was like an obstacle field where you had to be careful where to step, but despite all his toys the baby spent all his time while I was there playing with the door spring.

There's something therapeutic about door springs, that you just have to stop and play with it.

dfex8 hours ago

Finally!

Time to recreate the classic: https://www.youtube.com/shorts/pTgJaJYHIAs

mavamaarten5 hours ago

Heh. I expected it to be this one https://youtube.com/shorts/ocvBI_vtJwA

tdeck1 hour ago

I'm slightly ashamed of how many times I boinged this. Great work!

TheAceOfHearts8 hours ago

Any consideration on sharing the unminified code? I was a bit curious to read through the code and it seems like such a shame to keep it obfuscated. From a quick perusal, it seems like the bulk of the code comes from howler.js (a sound library), and the core functionality is conveniently implemented below the mobile template.

gregsadetsky5 hours ago
mmabbq5 hours ago

Turns out I was curious too, so I tried to de-minify it myself. Now I get to see how close I was.

https://pastebin.com/FKyz20LG

sixtyj7 hours ago

Online unminifier doesn’t work? E.g. https://www.unminify2.com/

coffeecoders7 hours ago

Looks like a simple exponential drag/sprint-return simulation using requestAnimationFrame.

Here is AI's implementation. https://jsfiddle.net/z0or7d2y/1/

lionkor6 hours ago

If they wanted an AI answer, surely they would have asked AI

reactordev4 hours ago

That’s got to be one of the most satisfying things ever. The real device was a darling invention and this is a faithful recreation of the experience of being in time out in the 80s.

jonplackett5 hours ago

Oh it needs a total boings by everyone counter!

gregsadetsky4 hours ago

alright, I implemented a world boing counter :-) thanks for the great idea

kentiko3 hours ago

Nice, could you share how you implemented it?

doubleorseven4 hours ago

maybe also de-boing the boining now that it's calling the server. i think the js script pasted here will show you it's needed

gregsadetsky4 hours ago

there's rate limiting so the script posted in this thread actually mostly hits 429s :-) but yeah, great pointer

dmje5 hours ago

Total boing heatmap!

gregsadetsky4 hours ago

I considered it for a minute, but then I remembered https://xkcd.com/1138/ .. ha. but let me know if you have other thoughts about this

dmje1 hour ago

ha :-)

codeulike4 hours ago

It doesnt boing rotationally, only in a straight line. Like the spring isn't really there.

If I bend it right round to one side so the spring is curved I expect it to bounce round to the other side.

gregsadetsky4 hours ago

you are right - just improved this and I think it looks a lot better (deploying now)

thanks!

xpe1 hour ago

Anyone want to commission an AI to make a sequel called Boing or Krill where you have to choose between boinging the spring or playing a game of snake (drawn as a line of krill)?

modeless9 hours ago

I would love to see an accurately simulated version of this, à la https://www.engine-sim.parts/

texuf6 hours ago

TIL I don't know how to “unmute” my device anymore. My new-ish iPhone doesn't have a physical switch on the side and I can’t find it in the settings in the pulldown menu.

gregsadetsky5 hours ago

Oh good point sorry. If you open the Control Center (drag down from the top left, typically), there should be a bell icon..?

That, or Settings -> Sounds & Haptics -> Silent Mode ?

gmac4 hours ago

I just got an iPhone 17, and presumably because it inherited its Control Centre configuration from the 13 it replaces (which had a physical switch) the silent mode toggle was not present. Tap and hold in an empty space to edit the controls and add it in.

egeres4 hours ago

Amazing to see software like this without sign-in requirements or paid subscriptions!

cr125rider9 hours ago

Oh random Flash apps, how I miss you

victorbuilds7 hours ago
alex4404402 hours ago

If you were Elon you would claim it's an early alpha of a world simulator that in a year will be able to perfectly predict weather and stock market.

ethmarks10 hours ago

I noticed that the boing sound gets deeper and lower with smaller-magnitude boings. Is the boing audio generated procedurally/realistically in response to the physics of the boing, or is just playing a premade boing sound effect that's dynamically pitch shifted?

junon9 hours ago

The original is pretty low, it appears to be sped up. Check the network panel.

naich4 hours ago

I had to stop at 100 or I would have been there all day.

prodigycorp10 hours ago

i love this. it reminds me of simpler times when we’d have iphone apps/games that would explore a single mechanic and implement it really well.

aetherspawn7 hours ago

The sound is not physics based, the boing sound keeps going if you grab the head, likewise sometimes the sound ends before the vibration finishes.

gregsadetsky5 hours ago

Well spotted! I'd love a synthesized version - if anyone has pointers.

gnarlouse7 hours ago

Is this physics based audio?

zer0tonin2 hours ago

I don't think so

cons0le7 hours ago

Finally something I actually want to pay for!! Give us a premium tier with exclusive boingers plz

satvikpendem10 hours ago

Very fun and nostalgic. The head of the boinger doesn't seem to exactly follow the cursor/finger however, at least on mobile, it always arcs.

OuterVale7 hours ago

This is the natural thing to make with this tool: https://youtube.com/watch?v=5VGLPP70Xtw

____tom____9 hours ago

There seems to be a minor bug. When I switch tabs and come back, sometimes the spring is moving. Some times a small amount, and other times it appears to be streched to the max, and extending off the top and bottom of the screen, until it calms down.

Safari, Mac.

mg9 hours ago

I just wanted to write about a similar observation when using it in FireFox on Linux:

When wiggle the spring, keep the mouse inside the white area until it is at rest, press CTRL+u to see the source code, move the mouse to close the source code tab and close it - for some magical reason the spring is moving again for a little bit.

gregsadetsky5 hours ago

Yes, good sleuthing, that was one of the last remaining things I wanted to fix before launching.

Just fixed, should be live soon.

qwertytyyuu9 hours ago

As a phone user, I hate you, I hate how good this is. That counter is just mocking me.

sam-cop-vimes8 hours ago

I wasn't hearing the sound initially so I thought it wasn't working in Firefox. Put the sound all the way up and boinged again. Made me jump out of my seat. Hilarious :-)

HelloUsername6 hours ago

I have no sound on ff ios, volume 100%

gregsadetsky5 hours ago

Make sure to unmute your device - either using the physical mute rocker on older iPhones, or by disabling Silent Mode (tap the bell in the Control Center)

Quizzical42308 hours ago

Love this! It's highly addictive. (No guilt)

abhinavsns8 hours ago

There seems to be a bug. If I catch it mid boing, the sound doesn't stop.

gregsadetsky5 hours ago

very good observation! just fixed and pushed

nopurpose6 hours ago

How many before you stopped? I am at 37.

ku1ik5 hours ago

103. Was curious if there’s any prize for hitting 100 :)

ProllyInfamous3 hours ago

>prize for hitting 100 :)

https://www.decisionproblem.com/paperclips/

You can hate me and/or close the window at any point, friend...

arbol5 hours ago

Same, I thought it might be like cookie clicker

arbol6 hours ago

230

ramnik107 hours ago

I liked it, would love to code it

rezmason8 hours ago

There goes my evening.

bitcrshr9 hours ago

I needed this. Thank you.

brcmthrowaway5 hours ago

AI?

gregsadetsky5 hours ago

Yes! I've been toying with this project idea for a few ~months, trying out most of the models out there. The physics and the look of the spring would come out quite crazy looking, so I put it on the back burner.

This is not an ad, there's no affiliate link... but the physics & drawing code were one shot by the recently released Gemini 3 Pro. It was pretty incredible to see. Additional tweaks & boing counter server by Claude Code.

jesse__8 hours ago

boingboingboing

29athrowaway8 hours ago

I noticed that the sound changes depending on how you interact with it. Neat

johnwheeler8 hours ago

Most excellent.

akho6 hours ago

when haptics

gregsadetsky5 hours ago

I would love to, but iOS support doesn't seem possible - there's a trick [0] to make a hacky haptic vibration in javascript, but it doesn't work with the kinds of events here of drag&release. And (lame excuse, but) I don't have an Android phone to test the haptics to make sure they're semi realistic.

This might have to wait for the native app versions ha.

[0] https://progressier.com/pwa-capabilities/vibration-api

thenthenthen8 hours ago

Can we add accelerometer support? :D

karanveer9 hours ago

so satisfying.

catapart9 hours ago

fantasitic

doppelgunner6 hours ago

[dead]

supareya9 hours ago

[dead]