# Balatro

> The swirling Balatro paint shader, with crisp pixels, a cursor that stirs the paint and click whirlpools.

Two paints swirl around a center like the background of the card game Balatro, drawn with raw WebGL2 at a crisp pixel size. Move the cursor to stir the paint and steer the swirl, click to spin up a whirlpool that slowly unwinds, and turn up `crt` for scanlines. The swirl spins into place when it mounts. No extra dependencies.

<demo-balatro>



</demo-balatro>

## Installation

```bash
npx shadcn-vue@latest add "https://nxui.geoql.in/r/balatro.json"
```

## Usage

```vue
<script setup lang="ts">
  import Balatro from '~/components/ui/balatro/Balatro.vue';
</script>

<template>
  <div class="relative h-125 w-full overflow-hidden">
    <Balatro
      color1="#DE443B"
      color2="#006BB4"
      color3="#162325"
      :contrast="3.5"
      :swirl="0.25"
      :pixel-size="1.5"
      mouse-interaction
      click-swirl
    />
  </div>
</template>
```

Set `:pixel-size="0"` for a smooth render, or `:pixel-size="4"` with `:crt="1"` for an arcade screen. On light pages pass `light-mode`: the gaps between the paints turn pale.

## Props

<table>
<thead>
  <tr>
    <th>
      Prop
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Default
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        color1
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#DE443B'
      </code>
    </td>
    
    <td>
      Main paint color.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color2
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#006BB4'
      </code>
    </td>
    
    <td>
      Second paint color, swirled into the first.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        color3
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        '#162325'
      </code>
    </td>
    
    <td>
      Color of the gaps between the two paints.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        contrast
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        3.5
      </code>
    </td>
    
    <td>
      How sharply the paints separate. Lower values blend them into soft washes.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        lighting
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.4
      </code>
    </td>
    
    <td>
      Strength of the glossy highlights on the paint.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        swirl
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0.25
      </code>
    </td>
    
    <td>
      How tightly the paint spirals around the center, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How fast the paint flows. <code>
        0
      </code>
      
       freezes it.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rotation-speed
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Turns the whole swirl continuously. Negative values turn it the other way.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scale
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Size of the paint shapes. Higher values zoom in.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        detail
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        5
      </code>
    </td>
    
    <td>
      How many times the paint folds over itself, from 1 to 8.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        offset
      </code>
    </td>
    
    <td>
      <code>
        [number, number]
      </code>
    </td>
    
    <td>
      <code>
        [0, 0]
      </code>
    </td>
    
    <td>
      Moves the center of the swirl, as a fraction of the container diagonal.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        pixel-size
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1.5
      </code>
    </td>
    
    <td>
      Size of the pixels in CSS pixels. <code>
        0
      </code>
      
       renders it smooth.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        crt
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        0
      </code>
    </td>
    
    <td>
      Adds the scanlines and soft edge falloff of an old CRT screen, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-interaction
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      The cursor stirs the paint and steers the swirl.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        mouse-strength
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      How strongly the cursor drags the paint along.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        click-swirl
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      Clicking spins the paint into a whirlpool that slowly unwinds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        intro
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        true
      </code>
    </td>
    
    <td>
      The swirl spins into place on mount.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        light-mode
      </code>
    </td>
    
    <td>
      <code>
        boolean
      </code>
    </td>
    
    <td>
      <code>
        false
      </code>
    </td>
    
    <td>
      Lightens the gaps between the paints so it sits on light pages.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        opacity
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        1
      </code>
    </td>
    
    <td>
      Overall opacity, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        max-device-pixel-ratio
      </code>
    </td>
    
    <td>
      <code>
        number
      </code>
    </td>
    
    <td>
      <code>
        2
      </code>
    </td>
    
    <td>
      Highest pixel ratio the canvas renders at.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        class
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <code>
        ''
      </code>
    </td>
    
    <td>
      Extra classes for the container.
    </td>
  </tr>
</tbody>
</table>

## Accessibility

- The canvas is decorative and has `aria-hidden="true"`. Put real content on top of it.
- With `prefers-reduced-motion: reduce` the paint flows at a fifth of the speed, cursor stirring is softened, and the intro and click whirlpools are skipped.
- Rendering stops while the component is off screen, the tab is hidden, or nothing is moving.
- Ported from [React Bits](https://reactbits.dev/backgrounds/balatro) by David Haz.
