DEV Community

Rajnish Raj
Rajnish Raj

Posted on

How Casinos Control Randomness: Building Plinko with React and HTML Canvas

Introduction

During an internship in an igaming service-based company, I developed the frontend for a lot of crash games and Arcade Casino games. One of the first games I built was Plinko.
I learnt a lot of things during the development period of that game, which I am going to share with you in this post.

So, Let's begin.

Our goal

Our goal is to build Plinko, an Arcade Casino Game using HTML Canvas and React.

What is Plinko?
Plinko is a game of chance in which a ball or puck is dropped from the top of a vertical board filled with pegs. As the ball falls, it randomly bounces left or right at each peg until it lands in one of several slots at the bottom, each associated with a different prize or payout.

Plinko

Plinko working mechanism

The player drops a ball from the top, and it collides with obstacles positioned as a Pascal triangle; then it reaches the bottom level, where it collides with one of the multiplier blocks with multiplier X.

The player won betAmount * X money.

We cannot randomly drop a ball from any X position from top because casinos need a fixed RTP (Return to Player). To achieve that fixed RTP, we need to control the ball so it collides with a predetermined multiplier block.

How to control the ball?

The answer is simple. We map the initial x position, or you can say the dropping x position of the ball, to the multiplier blocks by running a lot of simulation.
In the simulation, we drop a ball from random x position from the top, track it to see which multiplier block it is going to collide with, and we repeat it for 5-10 thousand times until we map every multiplier block.

export const map = [
  {
    canvasWidth: 800,
    canvasHeight: 600,
    rows: 16,
    records: {
      0: [
        { initialX: 362.0786996500253, initialY: 30, multiplier: 1000},],
      1: [
        { initialX: 357.44116222657084, initialY: 30, multiplier: 130},],
      2: [
        { initialX: 351.58243647873775, initialY: 30, multiplier: 26},],


      }
   }
]
Enter fullscreen mode Exit fullscreen mode

Plinko Physic

We are going to use the following physics concept :

  1. Collision
  2. Gravity
  3. Friction

Collision

If the ball collides with an obstacle, it will bounce off. There are two steps in this collision detection and bounce-back physics.

Collision between two balls

Collision detection physic

   const dx = ball.x - obstacle.x;
   const dy = ball.y - obstacle.y;
   const distance = Math.hypot(dx, dy);

    if (distance < ball.radius + obstacle.radius) {
        // Collision
    }
Enter fullscreen mode Exit fullscreen mode

Bounce back physic

   const angle = Math.atan2(dy, dx);
   const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
   ball.vx = Math.cos(angle) * speed * horizontalFriction;
   ball.vy = Math.sin(angle) * speed * verticalFriction;
Enter fullscreen mode Exit fullscreen mode

Gravity

We add gravity only for the ball not for obstacles, obstacles should be fixed.

    const gravity = 0.3;
    ball.vy = ball.vy + gravity;
Enter fullscreen mode Exit fullscreen mode

Friction

As you already noticed, we used horizontal and vertical friction in bounce-back physics. Friction is crucial physics for this game because if we do not add friction, the ball will never loses it energy and it will eventually bounce off the canvas.

    const horizontalFriction = 0.4;
    const verticalFriction = 0.8;
Enter fullscreen mode Exit fullscreen mode

Development

I divided the whole game into two parts: Game Panel and Betting Panel
Game Panel has the HTML Canvas element, and the Betting Panel controls the bet.

    <gameContext.Provider value={{ ...values }}>
       <GamePanel />
       <BettingPanel />
    </gameContext.Provider>
Enter fullscreen mode Exit fullscreen mode

Context API is used to share state between components, and a custom-made EventEmitter is used to trigger events and share data.

   eventEmitter.emit("ballDropEnd", {});
   eventEmitter.emit("ballDropStart", {});
   eventEmitter.emit("ballObstacleCollision", {
            x: obstacle.x,
            y: obstacle.y,
    });

   and more events...
Enter fullscreen mode Exit fullscreen mode

Conclusion

Building Plinko taught me that arcade casino games are far more engineered than they appear. What looks like a simple ball-dropping game involves a careful balance of physics simulation, RTP control, and canvas rendering working together seamlessly.

live link for demo

Top comments (0)