Files
simvx/examples/ports/balatro_feel

Balatro Feel (SimVX port)

SimVX port of mixandjam/balatro-feel, André Cardoso's Unity study of what makes Balatro's card hand feel so good. Seven procedurally drawn cards fan across the table; each lifts and tilts toward the cursor on hover, punches when selected, and swaps slots with its neighbours while dragged. Playing a hand pulses the selected cards left to right before clearing them.

There is no scoring and no deck: the whole point is the feel.

Licensing: clean re-implementation under MIT, with every card face, suit pip, and drop shadow generated at runtime as a NumPy array. No upstream art, audio, or code is redistributed. See ATTRIBUTION.md and LICENSE.

Run

# from the repo root
uv run python examples/ports/balatro_feel/main.py            # interactive
uv run python examples/ports/balatro_feel/main.py --test     # headless capture (3 frames)
uv run python examples/ports/balatro_feel/harness.py         # scripted-input capture (7 stages)
uv run simvx export web examples/ports/balatro_feel/main.py \
    -o /tmp/balatro_feel.html

Controls

Every action has an on-screen button, so mouse or touch alone is enough; the keys are shortcuts for the same verbs.

Input Action
Hover Lift, scale-up, parallax tilt toward the cursor
Click Toggle selection (selected cards rest higher)
Drag Card follows the cursor; crossing a neighbour swaps their slots
Play hand / Space Pulse the selected cards left to right, then deselect
Discard / Delete Remove the hovered card, or every selected card
Clear / right-click Deselect all
New hand Re-deal the seven cards
Menu / Esc Back to the menu (Esc quits from the menu)

What it demonstrates

  • Signals as the seam between logic and juice. Card owns the interaction state and emits a signal per change; CardVisual connects to those signals in its constructor and answers each one with a spring, a punch, or a scale. Neither node calls the other.
  • Coroutines for animation. Each punch is a damped-sine coroutine started on the event and finishing on its own, with no tween graph or update-flag bookkeeping.
  • InputMap actions polled from on_update, so one code path serves the window, a touch screen, and the scripted harness.
  • Procedural textures. nodes/card_textures.py rasterises each face into an RGBA uint8 array (rounded corners, freetype rank glyphs, vector suit pips, soft drop shadow) and hands it straight to Sprite2D. No asset files ship with the port.
  • z_index draw order, so a hovered, selected, or dragged card lifts above its neighbours while resting cards keep slot order.
  • Resize-aware layout. The hand centre and every text box are recomputed from the tree's screen_resized signal; the controls strip is anchored, so the port follows a desktop window resize and the responsive web canvas alike.

Layout

balatro_feel/
├── main.py                # entry, InputMap actions, menu + table screens, controls strip
├── harness.py             # scripted input via InputSimulator + staged screenshots
├── nodes/
│   ├── card.py            # Card (state + signals) and CardVisual (spring-follow juice)
│   ├── card_textures.py   # procedural card faces, suit pips, drop shadow
│   └── hand_holder.py     # fan layout, slot swapping, input dispatch, hand verbs
├── screenshots/           # idle + harness captures
├── ATTRIBUTION.md
└── LICENSE

License

MIT (matches upstream).