Back to DevLog

Polishing the Casino UX: Auto-Refresh Balances & Plinko Physics

3 min read

Had a solid UX polish session today working on Cardizzle, my casino platform. Nothing groundbreaking, but the kind of details that make or break user experience.

The Small Wins That Matter

First up was something that's been bugging me for weeks - players had to logout and login again to see their updated chip balance after games. Super annoying. I added auto-refresh across all the game pages (lobby, roulette, blackjack, plinko) so balances update seamlessly. One of those "why didn't I do this sooner" moments.

Also fixed a visual bug in poker where the result modal cards looked squished. Turned out I had some overly aggressive CSS overrides (!w-10/!h-14) that were fighting with the component's natural sizing. Stripped those out, used proper size="sm" proportions, and widened the modal. Much cleaner now.

Plinko Gets a Redesign

The bigger chunk of work was redesigning the Plinko game flow. The old version felt janky - balls would drop individually and results would pop up before the physics even finished. Not great for that satisfying "watch all the balls fall" experience.

So I rebuilt it with a proper 3-phase state machine:

  • Betting phase: Players queue up their bets, balls wait patiently
  • Dropping phase: All balls drop simultaneously when the timer ends
  • Closed phase: Results show only after the last ball lands

The tricky part was bridging the physics animation loop (requestAnimationFrame) with React state without running into stale closure hell. Ended up using a ref-based callback pattern (onAllBallsLandedRef) that lets the physics loop communicate back to React when everything's settled.

Server-side, I'm now queuing PlinkoDropResult objects during betting and emitting them all at once when the round closes. The client holds onto results in pendingResultRef until the physics simulation confirms all balls have landed.

The Details

Animation timing is estimated as max(5000, 3500 + maxBalls*250) ms - gives enough buffer for even crowded games to finish their physics.

I also dropped the client-side dropping state entirely since bets are now optimistic. When you click drop, hasBetThisRound gets set immediately so the UI responds right away.

Oh, and I snuck in some fixes for pre-existing roulette issues - ball orbit math was wonky and the table action panel layout needed tweaking.

What's Next

I've got the Plinko redesign implemented but haven't committed it yet - want to do one more review of the diff first. Then it's testing time with multiple players to make sure the synchronized dropping actually feels as satisfying as I think it will.

Might add a visual indicator showing queued balls during the betting phase too. Nothing fancy, just a count so players know their bets registered.

These polish sessions aren't the most exciting to write about, but they're where products actually get good. One auto-refresh and one physics timing fix at a time.

Share this post